added configurability of different window size for title screens
[rocksndiamonds.git] / src / tools.c
index f479854086749b99292e5cb40b06b59f14206f7b..3cf3b9609618d50b46346dba54d7bed7c7f32937 100644 (file)
@@ -227,7 +227,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");
 }
 
@@ -286,7 +286,7 @@ void RedrawPlayfield()
 
 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
 {
-  Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
+  Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
 
   BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
 }
@@ -330,8 +330,8 @@ void DrawMaskedBorder_ALL()
 void DrawMaskedBorder(int redraw_mask)
 {
   /* never draw masked screen borders on borderless screens */
-  if (effectiveGameStatus() == GAME_MODE_LOADING ||
-      effectiveGameStatus() == GAME_MODE_TITLE)
+  if (game_status == GAME_MODE_LOADING ||
+      game_status == GAME_MODE_TITLE)
     return;
 
   if (redraw_mask & REDRAW_ALL)
@@ -419,140 +419,16 @@ void BlitScreenToBitmap(Bitmap *target_bitmap)
   redraw_mask |= REDRAW_FIELD;
 }
 
-void BackToFront_OLD()
+void DrawFramesPerSecond()
 {
-  DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
-
-#if 0
-  /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
-  /* (force full redraw) */
-  if (game_status == GAME_MODE_PLAYING)
-    redraw_mask |= REDRAW_FIELD;
-#endif
-
-  if (redraw_mask == REDRAW_NONE)
-    return;
-
-#if 0
-  printf("::: ");
-  if (redraw_mask & REDRAW_ALL)
-    printf("[REDRAW_ALL]");
-  if (redraw_mask & REDRAW_FIELD)
-    printf("[REDRAW_FIELD]");
-  if (redraw_mask & REDRAW_DOOR_1)
-    printf("[REDRAW_DOOR_1]");
-  if (redraw_mask & REDRAW_DOOR_2)
-    printf("[REDRAW_DOOR_2]");
-  if (redraw_mask & REDRAW_FROM_BACKBUFFER)
-    printf("[REDRAW_FROM_BACKBUFFER]");
-  printf(" [%d]\n", FrameCounter);
-#endif
-
-  if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
-  {
-    static boolean last_frame_skipped = FALSE;
-    boolean skip_even_when_not_scrolling = TRUE;
-    boolean just_scrolling = (ScreenMovDir != 0);
-    boolean verbose = FALSE;
-
-    if (global.fps_slowdown_factor > 1 &&
-       (FrameCounter % global.fps_slowdown_factor) &&
-       (just_scrolling || skip_even_when_not_scrolling))
-    {
-      redraw_mask &= ~REDRAW_MAIN;
-
-      last_frame_skipped = TRUE;
-
-      if (verbose)
-       printf("FRAME SKIPPED\n");
-    }
-    else
-    {
-      if (last_frame_skipped)
-       redraw_mask |= REDRAW_FIELD;
-
-      last_frame_skipped = FALSE;
-
-      if (verbose)
-       printf("frame not skipped\n");
-    }
-  }
-
-  /* synchronize X11 graphics at this point; if we would synchronize the
-     display immediately after the buffer switching (after the XFlush),
-     this could mean that we have to wait for the graphics to complete,
-     although we could go on doing calculations for the next frame */
-
-  /* SyncDisplay(); */
-
-  /* never draw masked border to backbuffer when using playfield buffer */
-  if (game_status != GAME_MODE_PLAYING ||
-      redraw_mask & REDRAW_FROM_BACKBUFFER ||
-      buffer == backbuffer)
-    DrawMaskedBorder(redraw_mask);
-  else
-    DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
-
-  if (redraw_mask & REDRAW_ALL)
-  {
-    BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
-
-    redraw_mask = REDRAW_NONE;
-  }
-
-  if (redraw_mask & REDRAW_FIELD)
-  {
-    if (game_status != GAME_MODE_PLAYING ||
-       redraw_mask & REDRAW_FROM_BACKBUFFER)
-    {
-      BlitBitmap(backbuffer, window,
-                REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
-    }
-    else
-    {
-      BlitScreenToBitmap_RND(window);
-    }
-
-    redraw_mask &= ~REDRAW_MAIN;
-  }
-
-  if (redraw_mask & REDRAW_DOORS)
-  {
-    if (redraw_mask & REDRAW_DOOR_1)
-      BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
-
-    if (redraw_mask & REDRAW_DOOR_2)
-      BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
-
-    if (redraw_mask & REDRAW_DOOR_3)
-      BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
-
-    redraw_mask &= ~REDRAW_DOORS;
-  }
-
-  if (redraw_mask & REDRAW_MICROLEVEL)
-  {
-    BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
-              SX, SY + 10 * TILEY);
-
-    redraw_mask &= ~REDRAW_MICROLEVEL;
-  }
-
-  if (redraw_mask & REDRAW_FPS)                /* display frames per second */
-  {
-    char text[100];
-    char info1[100];
-
-    sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
-    if (!global.fps_slowdown)
-      info1[0] = '\0';
-
-    sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
+  char text[100];
+  int font_nr = FONT_TEXT_2;
+  int font_width = getFontWidth(font_nr);
 
-    DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
-  }
+  sprintf(text, "%04.1f fps", global.frames_per_second);
 
-  redraw_mask = REDRAW_NONE;
+  DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
+             font_nr, BLIT_OPAQUE);
 }
 
 void BackToFront()
@@ -560,13 +436,13 @@ void BackToFront()
   if (redraw_mask == REDRAW_NONE)
     return;
 
-  // redraw playfield if anything inside main playfield area needs redraw
-  if (redraw_mask & REDRAW_MAIN)
-    redraw_mask |= REDRAW_FIELD;
-
   // draw masked border to all viewports, if defined
   DrawMaskedBorder(redraw_mask);
 
+  // draw frames per second (only if debug mode is enabled)
+  if (redraw_mask & REDRAW_FPS)
+    DrawFramesPerSecond();
+
   // redraw complete window if both playfield and (some) doors need redraw
   if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
     redraw_mask = REDRAW_ALL;
@@ -605,6 +481,16 @@ static void FadeCrossSaveBackbuffer()
   BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 }
 
+static void FadeCrossRestoreBackbuffer()
+{
+  int redraw_mask_last = redraw_mask;
+
+  BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+  // do not change redraw mask when restoring backbuffer after cross-fading
+  redraw_mask = redraw_mask_last;
+}
+
 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
 {
   static int fade_type_skip = FADE_TYPE_NONE;
@@ -624,9 +510,15 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
       return;
     }
 
+#if 1
+    FadeCrossSaveBackbuffer();
+#endif
+
     if (fading.fade_mode & FADE_TYPE_TRANSFORM)
     {
+#if 0
       FadeCrossSaveBackbuffer();
+#endif
 
       return;
     }
@@ -660,10 +552,10 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
 
   if (fade_mask == REDRAW_FIELD)
   {
-    x = REAL_SX;
-    y = REAL_SY;
-    width  = FULL_SXSIZE;
-    height = FULL_SYSIZE;
+    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 */
@@ -695,6 +587,9 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
   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;
 }
 
@@ -704,6 +599,11 @@ void FadeIn(int fade_mask)
     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;
 }
 
 void FadeOut(int fade_mask)
@@ -787,6 +687,39 @@ 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)
@@ -807,26 +740,17 @@ void SetDoorBackgroundImageIfDefined(int graphic)
 
 void SetWindowBackgroundImage(int graphic)
 {
-  SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
-                           graphic_info[graphic].bitmap ?
-                           graphic_info[graphic].bitmap :
-                           graphic_info[IMG_BACKGROUND].bitmap);
+  SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
 }
 
 void SetMainBackgroundImage(int graphic)
 {
-  SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
-                         graphic_info[graphic].bitmap ?
-                         graphic_info[graphic].bitmap :
-                         graphic_info[IMG_BACKGROUND].bitmap);
+  SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
 }
 
 void SetDoorBackgroundImage(int graphic)
 {
-  SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
-                         graphic_info[graphic].bitmap ?
-                         graphic_info[graphic].bitmap :
-                         graphic_info[IMG_BACKGROUND].bitmap);
+  SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
 }
 
 void SetPanelBackground()
@@ -874,8 +798,137 @@ void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
   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
+  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);
@@ -954,7 +1007,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)
@@ -1039,8 +1092,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);
@@ -2095,7 +2148,7 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
                   level.envelope[envelope_nr].autowrap,
                   level.envelope[envelope_nr].centered, FALSE);
 
-    redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    redraw_mask |= REDRAW_FIELD;
     BackToFront();
 
     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
@@ -2146,25 +2199,57 @@ void ShowEnvelope(int envelope_nr)
   BackToFront();
 }
 
-static void setRequestCenterPosition(int *x, int *y)
+static void setRequestBasePosition(int *x, int *y)
 {
-  int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
-  int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
+  int sx_base, sy_base;
 
-  *x = sx_center;
-  *y = sy_center;
+  if (request.x != -1)
+    sx_base = request.x;
+  else if (request.align == ALIGN_LEFT)
+    sx_base = SX;
+  else if (request.align == ALIGN_RIGHT)
+    sx_base = SX + SXSIZE;
+  else
+    sx_base = SX + SXSIZE / 2;
+
+  if (request.y != -1)
+    sy_base = request.y;
+  else if (request.valign == VALIGN_TOP)
+    sy_base = SY;
+  else if (request.valign == VALIGN_BOTTOM)
+    sy_base = SY + SYSIZE;
+  else
+    sy_base = SY + SYSIZE / 2;
+
+  *x = sx_base;
+  *y = sy_base;
 }
 
-static void setRequestPosition(int *x, int *y, boolean add_border_size)
+static void setRequestPositionExt(int *x, int *y, int width, int height,
+                                 boolean add_border_size)
 {
   int border_size = request.border_size;
-  int sx_center, sy_center;
+  int sx_base, sy_base;
   int sx, sy;
 
-  setRequestCenterPosition(&sx_center, &sy_center);
+  setRequestBasePosition(&sx_base, &sy_base);
 
-  sx = sx_center - request.width  / 2;
-  sy = sy_center - request.height / 2;
+  if (request.align == ALIGN_LEFT)
+    sx = sx_base;
+  else if (request.align == ALIGN_RIGHT)
+    sx = sx_base - width;
+  else
+    sx = sx_base - width  / 2;
+
+  if (request.valign == VALIGN_TOP)
+    sy = sy_base;
+  else if (request.valign == VALIGN_BOTTOM)
+    sy = sy_base - height;
+  else
+    sy = sy_base - height / 2;
+
+  sx = MAX(0, MIN(sx, WIN_XSIZE - width));
+  sy = MAX(0, MIN(sy, WIN_YSIZE - height));
 
   if (add_border_size)
   {
@@ -2176,8 +2261,14 @@ static void setRequestPosition(int *x, int *y, boolean add_border_size)
   *y = sy;
 }
 
+static void setRequestPosition(int *x, int *y, boolean add_border_size)
+{
+  setRequestPositionExt(x, y, request.width, request.height, add_border_size);
+}
+
 void DrawEnvelopeRequest(char *text)
 {
+  int last_game_status = game_status;  /* save current game status */
   char *text_final = text;
   char *text_door_style = NULL;
   int graphic = IMG_BACKGROUND_REQUEST;
@@ -2189,19 +2280,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;
 
@@ -2238,10 +2335,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 */
+  game_status = 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);
 
+  game_status = last_game_status;      /* restore current game status */
+
   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
     RedrawGadget(tool_gadget[i]);
 
@@ -2264,11 +2366,9 @@ 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 width = request.width;
-  int height = request.height;
-  int tile_size = request.step_offset;
-  int max_xsize = width  / tile_size;
-  int max_ysize = height / tile_size;
+  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;
   int max_ysize_inner = max_ysize - 2;
 
@@ -2307,17 +2407,14 @@ void AnimateEnvelopeRequest(int anim_mode, int action)
     int ysize_size_top  = (ysize - 1) * tile_size;
     int max_xsize_pos = (max_xsize - 1) * tile_size;
     int max_ysize_pos = (max_ysize - 1) * tile_size;
-    int sx_center, sy_center;
+    int width  = xsize * tile_size;
+    int height = ysize * tile_size;
     int src_x, src_y;
     int dst_x, dst_y;
     int xx, yy;
 
-    setRequestCenterPosition(&sx_center, &sy_center);
-
-    src_x = sx_center - width  / 2;
-    src_y = sy_center - height / 2;
-    dst_x = sx_center - xsize * tile_size / 2;
-    dst_y = sy_center - ysize * tile_size / 2;
+    setRequestPosition(&src_x, &src_y, FALSE);
+    setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
 
     BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
@@ -2341,7 +2438,7 @@ void AnimateEnvelopeRequest(int anim_mode, int action)
       }
     }
 
-    redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    redraw_mask |= REDRAW_FIELD;
 
     DoAnimation();
     BackToFront();
@@ -2352,11 +2449,12 @@ 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;
-  int anim_mode = graphic_info[graphic].anim_mode;
+  int anim_mode_1 = request.anim_mode;                 /* (higher priority) */
+  int anim_mode_2 = graphic_info[graphic].anim_mode;   /* (lower priority) */
+  int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
                        anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
 
@@ -2394,9 +2492,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)
@@ -2421,8 +2516,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)
@@ -2533,7 +2626,7 @@ static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
     }
   }
 
-  redraw_mask |= REDRAW_MICROLEVEL;
+  redraw_mask |= REDRAW_FIELD;
 }
 
 #define MICROLABEL_EMPTY               0
@@ -2602,7 +2695,7 @@ static void DrawPreviewLevelLabelExt(int mode)
   if (strlen(label_text) > 0)
     DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
 
-  redraw_mask |= REDRAW_MICROLEVEL;
+  redraw_mask |= REDRAW_FIELD;
 }
 
 static void DrawPreviewLevelExt(boolean restart)
@@ -2776,8 +2869,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);
 
@@ -2787,9 +2881,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);
 
@@ -2799,7 +2892,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);
 
@@ -2812,7 +2905,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);
 
@@ -2836,7 +2929,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);
 
@@ -3320,7 +3413,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;
@@ -3485,11 +3577,7 @@ static int RequestHandleEvents(unsigned int req_state)
        Delay(10);
     }
 
-    game_status = GAME_MODE_PSEUDO_DOOR;
-
     BackToFront();
-
-    game_status = last_game_status;    /* restore current game status */
   }
 
   return result;
@@ -4267,6 +4355,9 @@ unsigned int MoveDoor(unsigned int door_state)
 
          width = g->width - src_xx;
 
+         if (width > door_rect->width)
+           width = door_rect->width;
+
          // printf("::: k == %d [%d] \n", k, start_step);
        }
 
@@ -4353,6 +4444,29 @@ unsigned int MoveDoor(unsigned int door_state)
   return (door1 | door2);
 }
 
+static boolean useSpecialEditorDoor()
+{
+  int graphic = IMG_GLOBAL_BORDER_EDITOR;
+  boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
+
+  // do not draw special editor door if editor border defined or redefined
+  if (graphic_info[graphic].bitmap != NULL || redefined)
+    return FALSE;
+
+  // do not draw special editor door if global border defined to be empty
+  if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
+    return FALSE;
+
+  // do not draw special editor door if viewport definitions do not match
+  if (EX != VX ||
+      EY >= VY ||
+      EXSIZE != VXSIZE ||
+      EY + EYSIZE != VY + VYSIZE)
+    return FALSE;
+
+  return TRUE;
+}
+
 void DrawSpecialEditorDoor()
 {
   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
@@ -4364,6 +4478,9 @@ void DrawSpecialEditorDoor()
   int vy = VY - outer_border;
   int exsize = EXSIZE + 2 * outer_border;
 
+  if (!useSpecialEditorDoor())
+    return;
+
   /* draw bigger level editor toolbox window */
   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
             top_border_width, top_border_height, ex, ey - top_border_height);
@@ -4385,6 +4502,9 @@ void UndrawSpecialEditorDoor()
   int exsize = EXSIZE + 2 * outer_border;
   int eysize = EYSIZE + 2 * outer_border;
 
+  if (!useSpecialEditorDoor())
+    return;
+
   /* draw normal tape recorder window */
   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
   {
@@ -8004,15 +8124,31 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded()
   }
 }
 
+void JoinRectangles(int *x, int *y, int *width, int *height,
+                   int x2, int y2, int width2, int height2)
+{
+  // do not join with "off-screen" rectangle
+  if (x2 == -1 || y2 == -1)
+    return;
+
+  *x = MIN(*x, x2);
+  *y = MIN(*y, y2);
+  *width = MAX(*width, width2);
+  *height = MAX(*height, height2);
+}
+
 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;
@@ -8047,13 +8183,12 @@ void ChangeViewportPropertiesIfNeeded()
   boolean init_video_buffer = FALSE;
   boolean init_gadgets_and_toons = FALSE;
   boolean init_em_graphics = FALSE;
-  boolean drawing_area_changed = 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;
@@ -8095,6 +8230,48 @@ void ChangeViewportPropertiesIfNeeded()
       new_tilesize_var != TILESIZE_VAR
       )
   {
+    // ------------------------------------------------------------------------
+    // determine next fading area for changed viewport definitions
+    // ------------------------------------------------------------------------
+
+    // start with current playfield area (default fading area)
+    FADE_SX = REAL_SX;
+    FADE_SY = REAL_SY;
+    FADE_SXSIZE = FULL_SXSIZE;
+    FADE_SYSIZE = FULL_SYSIZE;
+
+    // add new playfield area if position or size has changed
+    if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
+       new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
+    {
+      JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
+                    new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
+    }
+
+    // add current and new door 1 area if position or size has changed
+    if (new_dx != DX || new_dy != DY ||
+       new_dxsize != DXSIZE || new_dysize != DYSIZE)
+    {
+      JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
+                    DX, DY, DXSIZE, DYSIZE);
+      JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
+                    new_dx, new_dy, new_dxsize, new_dysize);
+    }
+
+    // add current and new door 2 area if position or size has changed
+    if (new_dx != VX || new_dy != VY ||
+       new_dxsize != VXSIZE || new_dysize != VYSIZE)
+    {
+      JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
+                    VX, VY, VXSIZE, VYSIZE);
+      JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
+                    new_vx, new_vy, new_vxsize, new_vysize);
+    }
+
+    // ------------------------------------------------------------------------
+    // handle changed tile size
+    // ------------------------------------------------------------------------
+
     if (new_tilesize_var != TILESIZE_VAR)
     {
       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
@@ -8106,19 +8283,6 @@ void ChangeViewportPropertiesIfNeeded()
       init_em_graphics = TRUE;
     }
 
-    if (new_sx != SX ||
-       new_sy != SY ||
-       new_sxsize != SXSIZE ||
-       new_sysize != SYSIZE ||
-       new_real_sx != REAL_SX ||
-       new_real_sy != REAL_SY ||
-       new_full_sxsize != FULL_SXSIZE ||
-       new_full_sysize != FULL_SYSIZE)
-    {
-      if (!init_video_buffer)
-       drawing_area_changed = TRUE;
-    }
-
     SX = new_sx;
     SY = new_sy;
     DX = new_dx;
@@ -8160,8 +8324,6 @@ void ChangeViewportPropertiesIfNeeded()
     SCR_FIELDX = new_scr_fieldx;
     SCR_FIELDY = new_scr_fieldy;
 
-    gfx.drawing_area_changed = drawing_area_changed;
-
     SetDrawDeactivationMask(REDRAW_NONE);
     SetDrawBackgroundMask(REDRAW_FIELD);
   }