+ static int fade_type_skip = FADE_TYPE_NONE;
+ void (*draw_border_function)(void) = NULL;
+ Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
+ int x, y, width, height;
+ int fade_delay, post_delay;
+
+ if (fade_type == FADE_TYPE_FADE_OUT)
+ {
+ if (fade_type_skip != FADE_TYPE_NONE)
+ {
+ /* skip all fade operations until specified fade operation */
+ if (fade_type & fade_type_skip)
+ fade_type_skip = FADE_TYPE_NONE;
+
+ return;
+ }
+
+#if 1
+ FadeCrossSaveBackbuffer();
+#endif
+
+ if (fading.fade_mode & FADE_TYPE_TRANSFORM)
+ {
+#if 0
+ FadeCrossSaveBackbuffer();
+#endif
+
+ return;
+ }
+ }
+
+ redraw_mask |= fade_mask;
+
+ if (fade_type == FADE_TYPE_SKIP)
+ {
+ fade_type_skip = fade_mode;
+
+ return;
+ }
+
+ fade_delay = fading.fade_delay;
+ post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+
+ if (fade_type_skip != FADE_TYPE_NONE)
+ {
+ /* skip all fade operations until specified fade operation */
+ if (fade_type & fade_type_skip)
+ fade_type_skip = FADE_TYPE_NONE;
+
+ fade_delay = 0;
+ }
+
+ if (global.autoplay_leveldir)
+ {
+ return;
+ }
+
+ if (fade_mask == REDRAW_FIELD)
+ {
+ x = FADE_SX;
+ y = FADE_SY;
+ width = FADE_SXSIZE;
+ height = FADE_SYSIZE;
+
+ if (border.draw_masked_when_fading)
+ draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
+ else
+ DrawMaskedBorder_FIELD(); /* draw once */
+ }
+ else /* REDRAW_ALL */
+ {
+ x = 0;
+ y = 0;
+ width = WIN_XSIZE;
+ height = WIN_YSIZE;
+ }
+
+ if (!setup.fade_screens ||
+ fade_delay == 0 ||
+ fading.fade_mode == FADE_MODE_NONE)
+ {
+ if (fade_mode == FADE_MODE_FADE_OUT)
+ return;
+
+ BlitBitmap(backbuffer, window, x, y, width, height, x, y);
+
+ redraw_mask &= ~fade_mask;
+
+ return;
+ }
+
+ FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
+ draw_border_function);
+
+ if (fade_type == FADE_TYPE_FADE_OUT)
+ FadeCrossRestoreBackbuffer();
+
+ redraw_mask &= ~fade_mask;
+}
+
+static void SetAnimStatus_BeforeFadingOut()
+{
+ global.anim_status = GAME_MODE_PSEUDO_FADING;
+}
+
+static void SetAnimStatus_AfterFadingIn()
+{
+ global.anim_status = global.anim_status_next;
+
+ // force update of global animation status in case of rapid screen changes
+ redraw_mask = REDRAW_ALL;
+ BackToFront();
+}
+
+void FadeIn(int fade_mask)
+{
+#if 1
+ DrawMaskedBorder(REDRAW_ALL);
+#endif
+
+ if (fading.fade_mode & FADE_TYPE_TRANSFORM)
+ FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
+ else
+ FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
+
+ FADE_SX = REAL_SX;
+ FADE_SY = REAL_SY;
+ FADE_SXSIZE = FULL_SXSIZE;
+ FADE_SYSIZE = FULL_SYSIZE;
+
+ SetAnimStatus_AfterFadingIn();
+}
+
+void FadeOut(int fade_mask)
+{
+ SetAnimStatus_BeforeFadingOut();
+
+#if 0
+ DrawMaskedBorder(REDRAW_ALL);
+#endif
+
+ if (fading.fade_mode & FADE_TYPE_TRANSFORM)
+ FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
+ else
+ FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
+
+ global.border_status = game_status;
+}
+
+static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
+{
+ static struct TitleFadingInfo fading_leave_stored;
+
+ if (set)
+ fading_leave_stored = fading_leave;
+ else
+ fading = fading_leave_stored;
+}
+
+void FadeSetEnterMenu()
+{
+ fading = menu.enter_menu;
+
+ FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
+}
+
+void FadeSetLeaveMenu()
+{
+ fading = menu.leave_menu;
+
+ FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
+}
+
+void FadeSetEnterScreen()
+{
+ fading = menu.enter_screen[game_status];
+
+ FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
+}
+
+void FadeSetNextScreen()
+{
+ fading = menu.next_screen[game_status];
+
+ // (do not overwrite fade mode set by FadeSetEnterScreen)
+ // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
+}
+
+void FadeSetLeaveScreen()
+{
+ FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
+}
+
+void FadeSetFromType(int type)
+{
+ if (type & TYPE_ENTER_SCREEN)
+ FadeSetEnterScreen();
+ else if (type & TYPE_ENTER)
+ FadeSetEnterMenu();
+ else if (type & TYPE_LEAVE)
+ FadeSetLeaveMenu();
+}
+
+void FadeSetDisabled()
+{
+ static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
+
+ fading = fading_none;
+}
+
+void FadeSkipNextFadeIn()
+{
+ FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
+}
+
+void FadeSkipNextFadeOut()
+{
+ FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
+}
+
+Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
+{
+ boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
+
+ return (graphic == IMG_UNDEFINED ? NULL :
+ graphic_info[graphic].bitmap != NULL || redefined ?
+ graphic_info[graphic].bitmap :
+ graphic_info[default_graphic].bitmap);
+}
+
+Bitmap *getBackgroundBitmap(int graphic)
+{
+ return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
+}
+
+Bitmap *getGlobalBorderBitmap(int graphic)
+{
+ return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
+}
+
+Bitmap *getGlobalBorderBitmapFromGameStatus()
+{
+ int graphic =
+ (game_status == GAME_MODE_MAIN ||
+ game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
+ game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
+ game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
+ game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
+ IMG_GLOBAL_BORDER);
+
+ return getGlobalBorderBitmap(graphic);
+}
+
+void SetWindowBackgroundImageIfDefined(int graphic)
+{
+ if (graphic_info[graphic].bitmap)
+ SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
+}
+
+void SetMainBackgroundImageIfDefined(int graphic)
+{
+ if (graphic_info[graphic].bitmap)
+ SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
+}
+
+void SetDoorBackgroundImageIfDefined(int graphic)
+{
+ if (graphic_info[graphic].bitmap)
+ SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
+}
+
+void SetWindowBackgroundImage(int graphic)
+{
+ SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
+}
+
+void SetMainBackgroundImage(int graphic)
+{
+ SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
+}
+
+void SetDoorBackgroundImage(int graphic)
+{
+ SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
+}
+
+void SetPanelBackground()
+{
+ struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
+
+ BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
+ gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
+
+ SetDoorBackgroundBitmap(bitmap_db_panel);
+}
+
+void DrawBackground(int x, int y, int width, int height)
+{
+ /* "drawto" might still point to playfield buffer here (hall of fame) */
+ ClearRectangleOnBackground(backbuffer, x, y, width, height);
+
+ if (IN_GFX_FIELD_FULL(x, y))
+ redraw_mask |= REDRAW_FIELD;
+ else if (IN_GFX_DOOR_1(x, y))
+ redraw_mask |= REDRAW_DOOR_1;
+ else if (IN_GFX_DOOR_2(x, y))
+ redraw_mask |= REDRAW_DOOR_2;
+ else if (IN_GFX_DOOR_3(x, y))
+ redraw_mask |= REDRAW_DOOR_3;
+}
+
+void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
+{
+ struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
+
+ if (font->bitmap == NULL)
+ return;
+
+ DrawBackground(x, y, width, height);
+}
+
+void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
+{
+ struct GraphicInfo *g = &graphic_info[graphic];
+
+ if (g->bitmap == NULL)
+ return;
+
+ DrawBackground(x, y, width, height);
+}
+
+static int game_status_last = -1;
+static Bitmap *global_border_bitmap_last = NULL;
+static Bitmap *global_border_bitmap = NULL;
+static int real_sx_last = -1, real_sy_last = -1;
+static int full_sxsize_last = -1, full_sysize_last = -1;
+static int dx_last = -1, dy_last = -1;
+static int dxsize_last = -1, dysize_last = -1;
+static int vx_last = -1, vy_last = -1;
+static int vxsize_last = -1, vysize_last = -1;
+
+boolean CheckIfGlobalBorderHasChanged()
+{
+ // if game status has not changed, global border has not changed either
+ if (game_status == game_status_last)
+ return FALSE;
+
+ // determine and store new global border bitmap for current game status
+ global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
+
+ return (global_border_bitmap_last != global_border_bitmap);
+}
+
+boolean CheckIfGlobalBorderRedrawIsNeeded()
+{
+ // if game status has not changed, nothing has to be redrawn
+ if (game_status == game_status_last)
+ return FALSE;
+
+ // redraw if last screen was title screen
+ if (game_status_last == GAME_MODE_TITLE)
+ return TRUE;
+
+ // redraw if global screen border has changed
+ if (CheckIfGlobalBorderHasChanged())
+ return TRUE;
+
+ // redraw if position or size of playfield area has changed
+ if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
+ full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
+ return TRUE;
+
+ // redraw if position or size of door area has changed
+ if (dx_last != DX || dy_last != DY ||
+ dxsize_last != DXSIZE || dysize_last != DYSIZE)
+ return TRUE;
+
+ // redraw if position or size of tape area has changed
+ if (vx_last != VX || vy_last != VY ||
+ vxsize_last != VXSIZE || vysize_last != VYSIZE)
+ return TRUE;
+
+ return FALSE;
+}
+
+void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
+{
+ if (bitmap)
+ BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+ else
+ ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
+}
+
+void RedrawGlobalBorder()
+{
+ Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
+
+ RedrawGlobalBorderFromBitmap(bitmap);
+
+ redraw_mask = REDRAW_ALL;
+}
+
+static void RedrawGlobalBorderIfNeeded()
+{
+ if (game_status == game_status_last)
+ return;
+
+ // copy current draw buffer to later copy back areas that have not changed
+ if (game_status_last != GAME_MODE_TITLE)
+ BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+ if (CheckIfGlobalBorderRedrawIsNeeded())
+ {
+ // redraw global screen border (or clear, if defined to be empty)
+ RedrawGlobalBorderFromBitmap(global_border_bitmap);
+
+ // copy previous playfield and door areas, if they are defined on both
+ // previous and current screen and if they still have the same size
+
+ if (real_sx_last != -1 && real_sy_last != -1 &&
+ REAL_SX != -1 && REAL_SY != -1 &&
+ full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
+ BlitBitmap(bitmap_db_store, backbuffer,
+ real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
+ REAL_SX, REAL_SY);
+
+ if (dx_last != -1 && dy_last != -1 &&
+ DX != -1 && DY != -1 &&
+ dxsize_last == DXSIZE && dysize_last == DYSIZE)
+ BlitBitmap(bitmap_db_store, backbuffer,
+ dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
+
+ if (vx_last != -1 && vy_last != -1 &&
+ VX != -1 && VY != -1 &&
+ vxsize_last == VXSIZE && vysize_last == VYSIZE)
+ BlitBitmap(bitmap_db_store, backbuffer,
+ vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
+
+ redraw_mask = REDRAW_ALL;
+ }
+
+ game_status_last = game_status;
+
+ global_border_bitmap_last = global_border_bitmap;
+
+ real_sx_last = REAL_SX;
+ real_sy_last = REAL_SY;
+ full_sxsize_last = FULL_SXSIZE;
+ full_sysize_last = FULL_SYSIZE;
+ dx_last = DX;
+ dy_last = DY;
+ dxsize_last = DXSIZE;
+ dysize_last = DYSIZE;
+ vx_last = VX;
+ vy_last = VY;
+ vxsize_last = VXSIZE;
+ vysize_last = VYSIZE;
+}
+
+void ClearField()
+{
+ RedrawGlobalBorderIfNeeded();
+
+ /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
+ /* (when entering hall of fame after playing) */
+ DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
+
+ /* !!! maybe this should be done before clearing the background !!! */
+ if (game_status == GAME_MODE_PLAYING)
+ {
+ ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
+ SetDrawtoField(DRAW_FIELDBUFFER);
+ }
+ else
+ {
+ SetDrawtoField(DRAW_BACKBUFFER);
+ }
+}
+
+void MarkTileDirty(int x, int y)
+{
+ redraw_mask |= REDRAW_FIELD;
+}
+
+void SetBorderElement()
+{
+ int x, y;
+
+ BorderElement = EL_EMPTY;
+
+ 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 FloodFillLevel(int from_x, int from_y, int fill_element,
+ short field[MAX_LEV_FIELDX][MAX_LEV_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)
+ FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
+ }
+
+ safety--;
+}
+
+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 getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
+ Bitmap **bitmap, int *x, int *y,
+ boolean get_backside)
+{
+ struct GraphicInfo *g = &graphic_info[graphic];
+ Bitmap *src_bitmap = g->bitmap;
+ int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
+ int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
+ int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
+
+ // if no in-game graphics defined, always use standard graphic size
+ if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
+ tilesize = TILESIZE;
+
+ if (tilesize == gfx.standard_tile_size)
+ src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
+ else if (tilesize == game.tile_size)
+ src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
+ else
+ src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
+
+ 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;
+
+ src_x = pos % max_width;
+ src_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;
+
+ src_x = src_x % g->width + pos / max_height * g->width;
+ src_y = pos % max_height;
+ }
+ else /* frames are ordered diagonally */
+ {
+ src_x = src_x + frame * g->offset_x;
+ src_y = src_y + frame * g->offset_y;
+ }
+
+ *bitmap = src_bitmap;
+ *x = src_x * tilesize / g->tile_size;
+ *y = src_y * tilesize / g->tile_size;
+}
+
+void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
+ int *x, int *y, boolean get_backside)
+{
+ getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
+ get_backside);
+}
+
+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);
+}
+
+inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
+ 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 (TILESIZE_VAR != TILESIZE)
+ return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
+ get_backside);