+ // !!! maybe this should be done before clearing the background !!!
+ if (game_status == GAME_MODE_PLAYING)
+ {
+ ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
+ SetDrawtoField(DRAW_TO_FIELDBUFFER);
+ }
+ else
+ {
+ SetDrawtoField(DRAW_TO_BACKBUFFER);
+ }
+}
+
+void MarkTileDirty(int x, int y)
+{
+ redraw_mask |= REDRAW_FIELD;
+}
+
+void SetBorderElement(void)
+{
+ int x, y;
+
+ BorderElement = EL_EMPTY;
+
+ // only the R'n'D game engine may use an additional steelwall border
+ if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
+ return;
+
+ for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
+ {
+ for (x = 0; x < lev_fieldx; x++)
+ {
+ if (!IS_INDESTRUCTIBLE(Feld[x][y]))
+ BorderElement = EL_STEELWALL;
+
+ if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
+ x = lev_fieldx - 2;
+ }
+ }
+}
+
+void FloodFillLevelExt(int from_x, int from_y, int fill_element,
+ int max_array_fieldx, int max_array_fieldy,
+ short field[max_array_fieldx][max_array_fieldy],
+ int max_fieldx, int max_fieldy)
+{
+ int i,x,y;
+ int old_element;
+ static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
+ static int safety = 0;
+
+ // check if starting field still has the desired content
+ if (field[from_x][from_y] == fill_element)
+ return;
+
+ safety++;
+
+ if (safety > max_fieldx * max_fieldy)
+ Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
+
+ old_element = field[from_x][from_y];
+ field[from_x][from_y] = fill_element;
+
+ for (i = 0; i < 4; i++)
+ {
+ x = from_x + check[i][0];
+ y = from_y + check[i][1];
+
+ if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
+ FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
+ field, max_fieldx, max_fieldy);
+ }
+
+ safety--;
+}
+
+void FloodFillLevel(int from_x, int from_y, int fill_element,
+ short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
+ int max_fieldx, int max_fieldy)
+{
+ FloodFillLevelExt(from_x, from_y, fill_element,
+ MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
+ max_fieldx, max_fieldy);
+}
+
+void SetRandomAnimationValue(int x, int y)
+{
+ gfx.anim_random_frame = GfxRandom[x][y];
+}
+
+int getGraphicAnimationFrame(int graphic, int sync_frame)
+{
+ // animation synchronized with global frame counter, not move position
+ if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
+ sync_frame = FrameCounter;
+
+ return getAnimationFrame(graphic_info[graphic].anim_frames,
+ graphic_info[graphic].anim_delay,
+ graphic_info[graphic].anim_mode,
+ graphic_info[graphic].anim_start_frame,
+ sync_frame);
+}
+
+void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
+{
+ struct GraphicInfo *g = &graphic_info[graphic];
+ int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
+
+ if (tilesize == gfx.standard_tile_size)
+ *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
+ else if (tilesize == game.tile_size)
+ *bitmap = g->bitmaps[IMG_BITMAP_GAME];
+ else
+ *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
+}
+
+void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
+ boolean get_backside)
+{
+ struct GraphicInfo *g = &graphic_info[graphic];
+ int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
+ int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
+
+ if (g->offset_y == 0) // frames are ordered horizontally
+ {
+ int max_width = g->anim_frames_per_line * g->width;
+ int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
+
+ *x = pos % max_width;
+ *y = src_y % g->height + pos / max_width * g->height;
+ }
+ else if (g->offset_x == 0) // frames are ordered vertically
+ {
+ int max_height = g->anim_frames_per_line * g->height;
+ int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
+
+ *x = src_x % g->width + pos / max_height * g->width;
+ *y = pos % max_height;
+ }
+ else // frames are ordered diagonally
+ {
+ *x = src_x + frame * g->offset_x;
+ *y = src_y + frame * g->offset_y;
+ }
+}
+
+void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
+ Bitmap **bitmap, int *x, int *y,
+ boolean get_backside)
+{
+ struct GraphicInfo *g = &graphic_info[graphic];
+
+ // if no graphics defined at all, use fallback graphics
+ if (g->bitmaps == NULL)
+ *g = graphic_info[IMG_CHAR_EXCLAM];
+
+ // if no in-game graphics defined, always use standard graphic size
+ if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
+ tilesize = TILESIZE;
+
+ getGraphicSourceBitmap(graphic, tilesize, bitmap);
+ getGraphicSourceXY(graphic, frame, x, y, get_backside);
+
+ *x = *x * tilesize / g->tile_size;
+ *y = *y * tilesize / g->tile_size;
+}
+
+void getSizedGraphicSource(int graphic, int frame, int tilesize,
+ Bitmap **bitmap, int *x, int *y)
+{
+ getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
+}
+
+void getFixedGraphicSource(int graphic, int frame,
+ Bitmap **bitmap, int *x, int *y)
+{
+ getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
+}
+
+void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
+{
+ getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
+}
+
+static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
+ int *x, int *y, boolean get_backside)
+{
+ getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
+ get_backside);
+}
+
+void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
+{
+ getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
+}
+
+void DrawGraphic(int x, int y, int graphic, int frame)
+{
+#if DEBUG
+ if (!IN_SCR_FIELD(x, y))
+ {
+ printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
+ printf("DrawGraphic(): This should never happen!\n");
+ return;
+ }
+#endif
+
+ DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
+ frame);
+
+ MarkTileDirty(x, y);
+}
+
+void DrawFixedGraphic(int x, int y, int graphic, int frame)
+{
+#if DEBUG
+ if (!IN_SCR_FIELD(x, y))
+ {
+ printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
+ printf("DrawGraphic(): This should never happen!\n");
+ return;
+ }
+#endif
+
+ DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
+ frame);
+ MarkTileDirty(x, y);
+}
+
+void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
+ int frame)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+
+ BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
+}
+
+void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
+ int frame)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+ BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
+}
+
+void DrawGraphicThruMask(int x, int y, int graphic, int frame)
+{
+#if DEBUG
+ if (!IN_SCR_FIELD(x, y))
+ {
+ printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
+ printf("DrawGraphicThruMask(): This should never happen!\n");
+ return;
+ }
+#endif
+
+ DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
+ graphic, frame);
+
+ MarkTileDirty(x, y);
+}
+
+void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
+{
+#if DEBUG
+ if (!IN_SCR_FIELD(x, y))
+ {
+ printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
+ printf("DrawGraphicThruMask(): This should never happen!\n");
+ return;
+ }
+#endif
+
+ DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
+ graphic, frame);
+ MarkTileDirty(x, y);
+}
+
+void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
+ int frame)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+
+ BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
+ dst_x, dst_y);
+}
+
+void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
+ int graphic, int frame)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+
+ BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
+ dst_x, dst_y);
+}
+
+void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
+{
+ DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
+ frame, tilesize);
+ MarkTileDirty(x / tilesize, y / tilesize);
+}
+
+void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
+ int tilesize)
+{
+ DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
+ graphic, frame, tilesize);
+ MarkTileDirty(x / tilesize, y / tilesize);
+}
+
+void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
+ int tilesize)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+ BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
+}
+
+void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
+ int frame, int tilesize)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+ BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
+}
+
+void DrawMiniGraphic(int x, int y, int graphic)
+{
+ DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
+ MarkTileDirty(x / 2, y / 2);
+}
+
+void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
+ BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
+}
+
+static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
+ int graphic, int frame,
+ int cut_mode, int mask_mode)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int dst_x, dst_y;
+ int width = TILEX, height = TILEY;
+ int cx = 0, cy = 0;
+
+ if (dx || dy) // shifted graphic
+ {
+ if (x < BX1) // object enters playfield from the left
+ {
+ x = BX1;
+ width = dx;
+ cx = TILEX - dx;
+ dx = 0;
+ }
+ else if (x > BX2) // object enters playfield from the right
+ {
+ x = BX2;
+ width = -dx;
+ dx = TILEX + dx;
+ }
+ else if (x == BX1 && dx < 0) // object leaves playfield to the left
+ {
+ width += dx;
+ cx = -dx;
+ dx = 0;
+ }
+ else if (x == BX2 && dx > 0) // object leaves playfield to the right
+ width -= dx;
+ else if (dx) // general horizontal movement
+ MarkTileDirty(x + SIGN(dx), y);
+
+ if (y < BY1) // object enters playfield from the top
+ {
+ if (cut_mode == CUT_BELOW) // object completely above top border
+ return;
+
+ y = BY1;
+ height = dy;
+ cy = TILEY - dy;
+ dy = 0;
+ }
+ else if (y > BY2) // object enters playfield from the bottom
+ {
+ y = BY2;
+ height = -dy;
+ dy = TILEY + dy;
+ }
+ else if (y == BY1 && dy < 0) // object leaves playfield to the top
+ {
+ height += dy;
+ cy = -dy;
+ dy = 0;
+ }
+ else if (dy > 0 && cut_mode == CUT_ABOVE)
+ {
+ if (y == BY2) // object completely above bottom border
+ return;
+
+ height = dy;
+ cy = TILEY - dy;
+ dy = TILEY;
+ MarkTileDirty(x, y + 1);
+ } // object leaves playfield to the bottom
+ else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
+ height -= dy;
+ else if (dy) // general vertical movement
+ MarkTileDirty(x, y + SIGN(dy));
+ }
+
+#if DEBUG
+ if (!IN_SCR_FIELD(x, y))
+ {
+ printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
+ printf("DrawGraphicShifted(): This should never happen!\n");
+ return;
+ }