added support for separate animations for 'main only' and 'type name' screen
[rocksndiamonds.git] / src / tools.c
index 5b830772cec84339a1a8931285118aca79ff2d66..8610e5103476b976aa2731326f38226e22fb99a8 100644 (file)
@@ -24,7 +24,8 @@
 
 
 /* select level set with EMC X11 graphics before activating EM GFX debugging */
-#define DEBUG_EM_GFX   0
+#define DEBUG_EM_GFX           FALSE
+#define DEBUG_FRAME_TIME       FALSE
 
 /* tool button identifiers */
 #define TOOL_CTRL_ID_YES       0
@@ -173,6 +174,9 @@ static int el_act2crm(int, int);
 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
 static int request_gadget_id = -1;
 
+static unsigned int sync_frame_delay = 0;
+static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY;
+
 static char *print_if_not_empty(int element)
 {
   static char *s = NULL;
@@ -227,7 +231,7 @@ void DumpTile(int x, int y)
   printf("  CustomValue: %d\n", CustomValue[x][y]);
   printf("  GfxElement:  %d\n", GfxElement[x][y]);
   printf("  GfxAction:   %d\n", GfxAction[x][y]);
-  printf("  GfxFrame:    %d\n", GfxFrame[x][y]);
+  printf("  GfxFrame:    %d [%d]\n", GfxFrame[x][y], FrameCounter);
   printf("\n");
 }
 
@@ -284,68 +288,126 @@ void RedrawPlayfield()
             gfx.sx, gfx.sy);
 }
 
-void DrawMaskedBorder_Rect(int x, int y, int width, int height)
+static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
+                                    int draw_target)
 {
-  Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
+  Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
+  Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
+
+  if (x == -1 && y == -1)
+    return;
 
-  BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
+  if (draw_target == DRAW_BORDER_TO_SCREEN)
+    BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
+  else
+    BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
 }
 
-void DrawMaskedBorder_FIELD()
+static void DrawMaskedBorderExt_FIELD(int draw_target)
 {
-  if (global.border_status >= GAME_MODE_TITLE &&
+  if (global.border_status >= GAME_MODE_MAIN &&
       global.border_status <= GAME_MODE_PLAYING &&
       border.draw_masked[global.border_status])
-    DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
+    DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+                            draw_target);
 }
 
-void DrawMaskedBorder_DOOR_1()
+static void DrawMaskedBorderExt_DOOR_1(int draw_target)
 {
+  // when drawing to backbuffer, never draw border over open doors
+  if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
+      (GetDoorState() & DOOR_OPEN_1))
+    return;
+
   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
       (global.border_status != GAME_MODE_EDITOR ||
        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
-    DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
+    DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
 }
 
-void DrawMaskedBorder_DOOR_2()
+static void DrawMaskedBorderExt_DOOR_2(int draw_target)
 {
+  // when drawing to backbuffer, never draw border over open doors
+  if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
+      (GetDoorState() & DOOR_OPEN_2))
+    return;
+
   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
       global.border_status != GAME_MODE_EDITOR)
-    DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
+    DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
 }
 
-void DrawMaskedBorder_DOOR_3()
+static void DrawMaskedBorderExt_DOOR_3(int draw_target)
 {
   /* currently not available */
 }
 
-void DrawMaskedBorder_ALL()
+static void DrawMaskedBorderExt_ALL(int draw_target)
 {
-  DrawMaskedBorder_FIELD();
-  DrawMaskedBorder_DOOR_1();
-  DrawMaskedBorder_DOOR_2();
-  DrawMaskedBorder_DOOR_3();
+  DrawMaskedBorderExt_FIELD(draw_target);
+  DrawMaskedBorderExt_DOOR_1(draw_target);
+  DrawMaskedBorderExt_DOOR_2(draw_target);
+  DrawMaskedBorderExt_DOOR_3(draw_target);
 }
 
-void DrawMaskedBorder(int redraw_mask)
+static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
 {
   /* never draw masked screen borders on borderless screens */
-  if (effectiveGameStatus() == GAME_MODE_LOADING ||
-      effectiveGameStatus() == GAME_MODE_TITLE)
+  if (global.border_status == GAME_MODE_LOADING ||
+      global.border_status == GAME_MODE_TITLE)
     return;
 
   if (redraw_mask & REDRAW_ALL)
-    DrawMaskedBorder_ALL();
+    DrawMaskedBorderExt_ALL(draw_target);
   else
   {
     if (redraw_mask & REDRAW_FIELD)
-      DrawMaskedBorder_FIELD();
+      DrawMaskedBorderExt_FIELD(draw_target);
     if (redraw_mask & REDRAW_DOOR_1)
-      DrawMaskedBorder_DOOR_1();
+      DrawMaskedBorderExt_DOOR_1(draw_target);
     if (redraw_mask & REDRAW_DOOR_2)
-      DrawMaskedBorder_DOOR_2();
+      DrawMaskedBorderExt_DOOR_2(draw_target);
     if (redraw_mask & REDRAW_DOOR_3)
-      DrawMaskedBorder_DOOR_3();
+      DrawMaskedBorderExt_DOOR_3(draw_target);
+  }
+}
+
+void DrawMaskedBorder_FIELD()
+{
+  DrawMaskedBorderExt_FIELD(DRAW_BORDER_TO_BACKBUFFER);
+}
+
+void DrawMaskedBorder(int redraw_mask)
+{
+  DrawMaskedBorderExt(redraw_mask, DRAW_BORDER_TO_BACKBUFFER);
+}
+
+void DrawMaskedBorderToTarget(int draw_target)
+{
+  if (draw_target == DRAW_BORDER_TO_BACKBUFFER ||
+      draw_target == DRAW_BORDER_TO_SCREEN)
+  {
+    DrawMaskedBorderExt(REDRAW_ALL, draw_target);
+  }
+  else
+  {
+    int last_border_status = global.border_status;
+
+    if (draw_target == DRAW_BORDER_TO_FADE_SOURCE)
+    {
+      global.border_status = gfx.fade_border_source_status;
+      gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
+    }
+    else if (draw_target == DRAW_BORDER_TO_FADE_TARGET)
+    {
+      global.border_status = gfx.fade_border_target_status;
+      gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
+    }
+
+    DrawMaskedBorderExt(REDRAW_ALL, draw_target);
+
+    global.border_status = last_border_status;
+    gfx.masked_border_bitmap_ptr = backbuffer;
   }
 }
 
@@ -431,13 +493,61 @@ void DrawFramesPerSecond()
              font_nr, BLIT_OPAQUE);
 }
 
+#if DEBUG_FRAME_TIME
+static void PrintFrameTimeDebugging()
+{
+  static unsigned int last_counter = 0;
+  unsigned int counter = Counter();
+  int diff_1 = counter - last_counter;
+  int diff_2 = diff_1 - GAME_FRAME_DELAY;
+  int diff_2_max = 20;
+  int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
+  char diff_bar[2 * diff_2_max + 5];
+  int pos = 0;
+  int i;
+
+  diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
+
+  for (i = 0; i < diff_2_max; i++)
+    diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
+                      i >= diff_2_max - diff_2_cut ? '-' : ' ');
+
+  diff_bar[pos++] = '|';
+
+  for (i = 0; i < diff_2_max; i++)
+    diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
+
+  diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
+
+  diff_bar[pos++] = '\0';
+
+  Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
+       counter,
+       diff_1,
+       (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
+       diff_bar);
+
+  last_counter = counter;
+}
+#endif
+
 void BackToFront()
 {
+  static int last_redraw_mask = REDRAW_NONE;
+
+  // force screen redraw in every frame to continue drawing global animations
+  // (but always use the last redraw mask to prevent unwanted side effects)
   if (redraw_mask == REDRAW_NONE)
-    return;
+    redraw_mask = last_redraw_mask;
 
+  last_redraw_mask = redraw_mask;
+
+#if 1
+  // masked border now drawn immediately when blitting backbuffer to window
+#else
   // draw masked border to all viewports, if defined
   DrawMaskedBorder(redraw_mask);
+#endif
 
   // draw frames per second (only if debug mode is enabled)
   if (redraw_mask & REDRAW_FPS)
@@ -474,6 +584,10 @@ void BackToFront()
   }
 
   redraw_mask = REDRAW_NONE;
+
+#if DEBUG_FRAME_TIME
+  PrintFrameTimeDebugging();
+#endif
 }
 
 static void FadeCrossSaveBackbuffer()
@@ -593,8 +707,43 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
   redraw_mask &= ~fade_mask;
 }
 
+static void SetScreenStates_BeforeFadingIn()
+{
+}
+
+static void SetScreenStates_AfterFadingIn()
+{
+  // store new source screen (to use correct masked border for fading)
+  gfx.fade_border_source_status = global.border_status;
+
+  global.anim_status = global.anim_status_next;
+
+  // force update of global animation status in case of rapid screen changes
+  redraw_mask = REDRAW_ALL;
+  BackToFront();
+}
+
+static void SetScreenStates_BeforeFadingOut()
+{
+  // store new target screen (to use correct masked border for fading)
+  gfx.fade_border_target_status = game_status;
+
+  global.anim_status = GAME_MODE_PSEUDO_FADING;
+}
+
+static void SetScreenStates_AfterFadingOut()
+{
+  global.border_status = game_status;
+}
+
 void FadeIn(int fade_mask)
 {
+  SetScreenStates_BeforeFadingIn();
+
+#if 1
+  DrawMaskedBorder(REDRAW_ALL);
+#endif
+
   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
   else
@@ -604,16 +753,24 @@ void FadeIn(int fade_mask)
   FADE_SY = REAL_SY;
   FADE_SXSIZE = FULL_SXSIZE;
   FADE_SYSIZE = FULL_SYSIZE;
+
+  SetScreenStates_AfterFadingIn();
 }
 
 void FadeOut(int fade_mask)
 {
+  SetScreenStates_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;
+  SetScreenStates_AfterFadingOut();
 }
 
 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
@@ -649,7 +806,7 @@ void FadeSetEnterScreen()
 
 void FadeSetNextScreen()
 {
-  fading = menu.next_screen;
+  fading = menu.next_screen[game_status];
 
   // (do not overwrite fade mode set by FadeSetEnterScreen)
   // FadeSetLeaveNext(fading, TRUE);   /* (keep same fade mode) */
@@ -707,13 +864,15 @@ Bitmap *getGlobalBorderBitmap(int graphic)
   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
 }
 
-Bitmap *getGlobalBorderBitmapFromGameStatus()
+Bitmap *getGlobalBorderBitmapFromStatus(int status)
 {
-  int graphic = (game_status == GAME_MODE_MAIN    ? 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);
+  int graphic =
+    (status == GAME_MODE_MAIN ||
+     status == GAME_MODE_PSEUDO_TYPENAME       ? IMG_GLOBAL_BORDER_MAIN :
+     status == GAME_MODE_SCORES                        ? IMG_GLOBAL_BORDER_SCORES :
+     status == GAME_MODE_EDITOR                        ? IMG_GLOBAL_BORDER_EDITOR :
+     status == GAME_MODE_PLAYING               ? IMG_GLOBAL_BORDER_PLAYING :
+     IMG_GLOBAL_BORDER);
 
   return getGlobalBorderBitmap(graphic);
 }
@@ -813,7 +972,7 @@ boolean CheckIfGlobalBorderHasChanged()
     return FALSE;
 
   // determine and store new global border bitmap for current game status
-  global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
+  global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
 
   return (global_border_bitmap_last != global_border_bitmap);
 }
@@ -824,6 +983,10 @@ boolean CheckIfGlobalBorderRedrawIsNeeded()
   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;
@@ -856,7 +1019,7 @@ void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
 
 void RedrawGlobalBorder()
 {
-  Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
+  Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
 
   RedrawGlobalBorderFromBitmap(bitmap);
 
@@ -869,7 +1032,8 @@ static void RedrawGlobalBorderIfNeeded()
     return;
 
   // copy current draw buffer to later copy back areas that have not changed
-  BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+  if (game_status_last != GAME_MODE_TITLE)
+    BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
   if (CheckIfGlobalBorderRedrawIsNeeded())
   {
@@ -1001,7 +1165,7 @@ void SetRandomAnimationValue(int x, int y)
   gfx.anim_random_frame = GfxRandom[x][y];
 }
 
-inline int getGraphicAnimationFrame(int graphic, int sync_frame)
+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)
@@ -1058,8 +1222,8 @@ void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
   }
 
   *bitmap = src_bitmap;
-  *x = src_x * tilesize / TILESIZE;
-  *y = src_y * tilesize / TILESIZE;
+  *x = src_x * tilesize / g->tile_size;
+  *y = src_y * tilesize / g->tile_size;
 }
 
 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
@@ -1086,8 +1250,8 @@ void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
 }
 
-inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
-                               int *x, int *y, boolean get_backside)
+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);
@@ -1120,6 +1284,9 @@ inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
     *x = src_x + frame * g->offset_x;
     *y = src_y + frame * g->offset_y;
   }
+
+  *x = *x * TILESIZE_VAR / g->tile_size;
+  *y = *y * TILESIZE_VAR / g->tile_size;
 }
 
 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
@@ -1229,13 +1396,12 @@ void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
                                 int graphic, int frame)
 {
-  struct GraphicInfo *g = &graphic_info[graphic];
   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, g->width, g->height,
+  BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
                   dst_x, dst_y);
 }
 
@@ -1296,20 +1462,20 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
       width = -dx;
       dx = TILEX + dx;
     }
-    else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
+    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 */
+    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 */
+      if (cut_mode == CUT_BELOW) /* object completely above top border */
        return;
 
       y = BY1;
@@ -1323,7 +1489,7 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
       height = -dy;
       dy = TILEY + dy;
     }
-    else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
+    else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
     {
       height += dy;
       cy = -dy;
@@ -2273,19 +2439,25 @@ void DrawEnvelopeRequest(char *text)
   int border_size = request.border_size;
   int line_spacing = request.line_spacing;
   int line_height = font_height + line_spacing;
-  int text_width = request.width - 2 * border_size;
-  int text_height = request.height - 2 * border_size;
-  int line_length = text_width / font_width;
-  int max_lines = text_height / line_height;
+  int max_text_width  = request.width  - 2 * border_size;
+  int max_text_height = request.height - 2 * border_size;
+  int line_length = max_text_width  / font_width;
+  int max_lines   = max_text_height / line_height;
+  int text_width = line_length * font_width;
   int width = request.width;
   int height = request.height;
-  int tile_size = request.step_offset;
+  int tile_size = MAX(request.step_offset, 1);
   int x_steps = width  / tile_size;
   int y_steps = height / tile_size;
+  int sx_offset = border_size;
+  int sy_offset = border_size;
   int sx, sy;
   int i, x, y;
 
-  if (request.wrap_single_words)
+  if (request.centered)
+    sx_offset = (request.width - text_width) / 2;
+
+  if (request.wrap_single_words && !request.autowrap)
   {
     char *src_text_ptr, *dst_text_ptr;
 
@@ -2322,10 +2494,15 @@ void DrawEnvelopeRequest(char *text)
                                  x, y, x_steps, y_steps,
                                  tile_size, tile_size);
 
-  DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
+  /* force DOOR font inside door area */
+  SetFontStatus(GAME_MODE_PSEUDO_DOOR);
+
+  DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
                 line_length, -1, max_lines, line_spacing, mask_mode,
                 request.autowrap, request.centered, FALSE);
 
+  ResetFontStatus();
+
   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
     RedrawGadget(tool_gadget[i]);
 
@@ -2348,7 +2525,7 @@ void AnimateEnvelopeRequest(int anim_mode, int action)
   int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
   unsigned int anim_delay = 0;
 
-  int tile_size = request.step_offset;
+  int tile_size = MAX(request.step_offset, 1);
   int max_xsize = request.width  / tile_size;
   int max_ysize = request.height / tile_size;
   int max_xsize_inner = max_xsize - 2;
@@ -2370,13 +2547,6 @@ void AnimateEnvelopeRequest(int anim_mode, int action)
     ystart = yend;
     end = 0;
   }
-  else
-  {
-    if (action == ACTION_OPENING)
-      PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
-    else if (action == ACTION_CLOSING)
-      PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
-  }
 
   for (i = start; i <= end; i++)
   {
@@ -2431,7 +2601,6 @@ void AnimateEnvelopeRequest(int anim_mode, int action)
 
 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
 {
-  int last_game_status = game_status;  /* save current game status */
   int graphic = IMG_BACKGROUND_REQUEST;
   int sound_opening = SND_REQUEST_OPENING;
   int sound_closing = SND_REQUEST_CLOSING;
@@ -2475,9 +2644,6 @@ void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
       InitAnimation();
   }
 
-  /* force DOOR font inside door area */
-  game_status = GAME_MODE_PSEUDO_DOOR;
-
   game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
 
   if (action == ACTION_OPENING)
@@ -2502,8 +2668,6 @@ void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
 
   game.envelope_active = FALSE;
 
-  game_status = last_game_status;      /* restore current game status */
-
   if (action == ACTION_CLOSING)
   {
     if (game_status != GAME_MODE_MAIN)
@@ -2696,7 +2860,6 @@ static void DrawPreviewLevelExt(boolean restart)
   boolean show_level_border = (BorderElement != EL_EMPTY);
   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
-  int last_game_status = game_status;          /* save current game status */
 
   if (restart)
   {
@@ -2742,8 +2905,6 @@ static void DrawPreviewLevelExt(boolean restart)
        DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
     }
 
-    game_status = last_game_status;    /* restore current game status */
-
     return;
   }
 
@@ -2843,8 +3004,6 @@ static void DrawPreviewLevelExt(boolean restart)
 
     DrawPreviewLevelLabelExt(label_state);
   }
-
-  game_status = last_game_status;      /* restore current game status */
 }
 
 void DrawPreviewLevelInitial()
@@ -2857,8 +3016,9 @@ void DrawPreviewLevelAnimation()
   DrawPreviewLevelExt(FALSE);
 }
 
-inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
-                                   int graphic, int sync_frame, int mask_mode)
+inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
+                                          int graphic, int sync_frame,
+                                          int mask_mode)
 {
   int frame = getGraphicAnimationFrame(graphic, sync_frame);
 
@@ -2868,9 +3028,8 @@ inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
 }
 
-inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
-                                        int graphic, int sync_frame,
-                                        int mask_mode)
+void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
+                                 int graphic, int sync_frame, int mask_mode)
 {
   int frame = getGraphicAnimationFrame(graphic, sync_frame);
 
@@ -2880,7 +3039,7 @@ inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
 }
 
-inline void DrawGraphicAnimation(int x, int y, int graphic)
+inline static void DrawGraphicAnimation(int x, int y, int graphic)
 {
   int lx = LEVELX(x), ly = LEVELY(y);
 
@@ -2893,7 +3052,7 @@ inline void DrawGraphicAnimation(int x, int y, int graphic)
   MarkTileDirty(x, y);
 }
 
-inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
+void DrawFixedGraphicAnimation(int x, int y, int graphic)
 {
   int lx = LEVELX(x), ly = LEVELY(y);
 
@@ -2917,7 +3076,7 @@ void DrawLevelElementAnimation(int x, int y, int element)
   DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
 }
 
-inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
+void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
 {
   int sx = SCREENX(x), sy = SCREENY(y);
 
@@ -3388,8 +3547,7 @@ void WaitForEventToContinue()
 
     DoAnimation();
 
-    /* don't eat all CPU time */
-    Delay(10);
+    WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
   }
 }
 
@@ -3401,7 +3559,6 @@ static int RequestHandleEvents(unsigned int req_state)
 {
   boolean level_solved = (game_status == GAME_MODE_PLAYING &&
                          local_player->LevelSolved_GameEnd);
-  int last_game_status = game_status;  /* save current game status */
   int width  = request.width;
   int height = request.height;
   int sx, sy;
@@ -3561,16 +3718,11 @@ static int RequestHandleEvents(unsigned int req_state)
     else
     {
       DoAnimation();
-
-      if (!PendingEvent())     /* delay only if no pending events */
-       Delay(10);
     }
 
-    game_status = GAME_MODE_PSEUDO_DOOR;
-
     BackToFront();
 
-    game_status = last_game_status;    /* restore current game status */
+    WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
   }
 
   return result;
@@ -3579,7 +3731,6 @@ static int RequestHandleEvents(unsigned int req_state)
 static boolean RequestDoor(char *text, unsigned int req_state)
 {
   unsigned int old_door_state;
-  int last_game_status = game_status;  /* save current game status */
   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
   int font_nr = FONT_TEXT_2;
   char *text_ptr;
@@ -3636,7 +3787,7 @@ static boolean RequestDoor(char *text, unsigned int req_state)
   DrawBackground(DX, DY, DXSIZE, DYSIZE);
 
   /* force DOOR font inside door area */
-  game_status = GAME_MODE_PSEUDO_DOOR;
+  SetFontStatus(GAME_MODE_PSEUDO_DOOR);
 
   /* write text for request */
   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
@@ -3676,7 +3827,7 @@ static boolean RequestDoor(char *text, unsigned int req_state)
     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
   }
 
-  game_status = last_game_status;      /* restore current game status */
+  ResetFontStatus();
 
   if (req_state & REQ_ASK)
   {
@@ -4120,7 +4271,7 @@ unsigned int MoveDoor(unsigned int door_state)
     { DX, DY, DXSIZE, DYSIZE },
     { VX, VY, VXSIZE, VYSIZE }
   };
-  static int door1 = DOOR_OPEN_1;
+  static int door1 = DOOR_CLOSE_1;
   static int door2 = DOOR_CLOSE_2;
   unsigned int door_delay = 0;
   unsigned int door_delay_value;
@@ -4434,6 +4585,10 @@ unsigned int MoveDoor(unsigned int door_state)
   if (door_state & DOOR_ACTION_2)
     door2 = door_state & DOOR_ACTION_2;
 
+  // draw masked border over door area
+  DrawMaskedBorder(REDRAW_DOOR_1);
+  DrawMaskedBorder(REDRAW_DOOR_2);
+
   return (door1 | door2);
 }
 
@@ -8049,9 +8204,6 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded()
 {
   boolean change_fullscreen = (setup.fullscreen !=
                               video.fullscreen_enabled);
-  boolean change_fullscreen_mode = (video.fullscreen_enabled &&
-                                   !strEqual(setup.fullscreen_mode,
-                                             video.fullscreen_mode_current));
   boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
                                           setup.window_scaling_percent !=
                                           video.window_scaling_percent);
@@ -8081,7 +8233,6 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded()
 #endif
 
   if (change_fullscreen ||
-      change_fullscreen_mode ||
       change_window_scaling_percent)
   {
     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
@@ -8089,12 +8240,6 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded()
     /* save backbuffer content which gets lost when toggling fullscreen mode */
     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
-    if (change_fullscreen_mode)
-    {
-      /* keep fullscreen, but change fullscreen mode (screen resolution) */
-      video.fullscreen_enabled = FALSE;                /* force new fullscreen mode */
-    }
-
     if (change_window_scaling_percent)
     {
       /* keep window mode, but change window scaling */
@@ -8130,15 +8275,62 @@ void JoinRectangles(int *x, int *y, int *width, int *height,
   *height = MAX(*height, height2);
 }
 
+void SetAnimStatus(int anim_status_new)
+{
+  if (anim_status_new == GAME_MODE_MAIN)
+    anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
+
+  global.anim_status_next = anim_status_new;
+
+  // directly set screen modes that are entered without fading
+  if ((global.anim_status      == GAME_MODE_PSEUDO_MAINONLY &&
+       global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
+      (global.anim_status      == GAME_MODE_PSEUDO_TYPENAME &&
+       global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
+    global.anim_status = global.anim_status_next;
+}
+
+void SetGameStatus(int game_status_new)
+{
+  game_status = game_status_new;
+
+  SetAnimStatus(game_status_new);
+}
+
+void SetFontStatus(int game_status_new)
+{
+  static int last_game_status = -1;
+
+  if (game_status_new != -1)
+  {
+    // set game status for font use after storing last game status
+    last_game_status = game_status;
+    game_status = game_status_new;
+  }
+  else
+  {
+    // reset game status after font use from last stored game status
+    game_status = last_game_status;
+  }
+}
+
+void ResetFontStatus()
+{
+  SetFontStatus(-1);
+}
+
 void ChangeViewportPropertiesIfNeeded()
 {
   int gfx_game_mode = game_status;
   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
                        game_status);
+  struct RectWithBorder *vp_window    = &viewport.window[gfx_game_mode];
   struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
-  struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
-  struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
-  struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
+  struct RectWithBorder *vp_door_1    = &viewport.door_1[gfx_game_mode];
+  struct RectWithBorder *vp_door_2    = &viewport.door_2[gfx_game_mode2];
+  struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
+  int new_win_xsize    = vp_window->width;
+  int new_win_ysize    = vp_window->height;
   int border_size      = vp_playfield->border_size;
   int new_sx           = vp_playfield->x + border_size;
   int new_sy           = vp_playfield->y + border_size;
@@ -8174,14 +8366,15 @@ void ChangeViewportPropertiesIfNeeded()
   boolean init_gadgets_and_toons = FALSE;
   boolean init_em_graphics = FALSE;
 
-  if (viewport.window.width  != WIN_XSIZE ||
-      viewport.window.height != WIN_YSIZE)
+  if (new_win_xsize != WIN_XSIZE ||
+      new_win_ysize != WIN_YSIZE)
   {
-    WIN_XSIZE = viewport.window.width;
-    WIN_YSIZE = viewport.window.height;
+    WIN_XSIZE = new_win_xsize;
+    WIN_YSIZE = new_win_ysize;
 
     init_video_buffer = TRUE;
     init_gfx_buffers = TRUE;
+    init_gadgets_and_toons = TRUE;
 
     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
   }
@@ -8323,6 +8516,7 @@ void ChangeViewportPropertiesIfNeeded()
     // printf("::: init_video_buffer\n");
 
     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
+    InitImageTextures();
   }
 
   if (init_gadgets_and_toons)
@@ -8331,6 +8525,7 @@ void ChangeViewportPropertiesIfNeeded()
 
     InitGadgets();
     InitToons();
+    InitGlobalAnimations();
   }
 
   if (init_em_graphics)