updated contact info in source file headers
[rocksndiamonds.git] / src / tools.c
index f7735c70b012d6d945adeef60a57266b63f955a9..0a90d675befda42be650718dc64515d66386981f 100644 (file)
@@ -1,15 +1,15 @@
-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
-*----------------------------------------------------------*
-* (c) 1995-2006 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* tools.c                                                  *
-***********************************************************/
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// tools.c
+// ============================================================================
+
+#include <math.h>
 
 #include "libgame/libgame.h"
 
 
 #define NUM_TOOL_BUTTONS       7
 
+/* constants for number of doors and door parts */
+#define NUM_DOORS              2
+#define NUM_PANELS             NUM_DOORS
+// #define NUM_PANELS          0
+#define MAX_PARTS_PER_DOOR     8
+#define MAX_DOOR_PARTS         (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
+#define DOOR_PART_IS_PANEL(i)  ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
+
+
+struct DoorPartOrderInfo
+{
+  int nr;
+  int sort_priority;
+};
+
+static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
+
+struct DoorPartControlInfo
+{
+  int door_token;
+  int graphic;
+  struct DoorPartPosInfo *pos;
+};
+
+static struct DoorPartControlInfo door_part_controls[] =
+{
+  {
+    DOOR_1,
+    IMG_DOOR_1_GFX_PART_1,
+    &door_1.part_1
+  },
+  {
+    DOOR_1,
+    IMG_DOOR_1_GFX_PART_2,
+    &door_1.part_2
+  },
+  {
+    DOOR_1,
+    IMG_DOOR_1_GFX_PART_3,
+    &door_1.part_3
+  },
+  {
+    DOOR_1,
+    IMG_DOOR_1_GFX_PART_4,
+    &door_1.part_4
+  },
+  {
+    DOOR_1,
+    IMG_DOOR_1_GFX_PART_5,
+    &door_1.part_5
+  },
+  {
+    DOOR_1,
+    IMG_DOOR_1_GFX_PART_6,
+    &door_1.part_6
+  },
+  {
+    DOOR_1,
+    IMG_DOOR_1_GFX_PART_7,
+    &door_1.part_7
+  },
+  {
+    DOOR_1,
+    IMG_DOOR_1_GFX_PART_8,
+    &door_1.part_8
+  },
+
+  {
+    DOOR_2,
+    IMG_DOOR_2_GFX_PART_1,
+    &door_2.part_1
+  },
+  {
+    DOOR_2,
+    IMG_DOOR_2_GFX_PART_2,
+    &door_2.part_2
+  },
+  {
+    DOOR_2,
+    IMG_DOOR_2_GFX_PART_3,
+    &door_2.part_3
+  },
+  {
+    DOOR_2,
+    IMG_DOOR_2_GFX_PART_4,
+    &door_2.part_4
+  },
+  {
+    DOOR_2,
+    IMG_DOOR_2_GFX_PART_5,
+    &door_2.part_5
+  },
+  {
+    DOOR_2,
+    IMG_DOOR_2_GFX_PART_6,
+    &door_2.part_6
+  },
+  {
+    DOOR_2,
+    IMG_DOOR_2_GFX_PART_7,
+    &door_2.part_7
+  },
+  {
+    DOOR_2,
+    IMG_DOOR_2_GFX_PART_8,
+    &door_2.part_8
+  },
+
+  {
+    DOOR_1,
+    IMG_BACKGROUND_PANEL,
+    &door_1.panel
+  },
+  {
+    DOOR_2,
+    IMG_BACKGROUND_TAPE,
+    &door_2.panel
+  },
+
+  {
+    -1,
+    -1,
+    NULL
+  }
+};
+
+
 /* forward declaration for internal use */
 static void UnmapToolButtons();
 static void HandleToolButtons(struct GadgetInfo *);
@@ -167,6 +294,68 @@ void SetDrawtoField(int mode)
   }
 }
 
+#if 1
+
+static void RedrawPlayfield_RND()
+{
+  if (game.envelope_active)
+    return;
+
+#if 1
+  DrawLevel(REDRAW_ALL);
+#else
+  int x, y;
+
+  SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
+  // SetDrawBackgroundMask(REDRAW_FIELD);      // !!! CHECK THIS !!!
+  SetDrawBackgroundMask(REDRAW_ALL);   // !!! CHECK THIS !!!
+
+  for (x = BX1; x <= BX2; x++)
+    for (y = BY1; y <= BY2; y++)
+      DrawScreenField(x, y);
+
+  redraw_mask |= REDRAW_FIELD;
+#endif
+  DrawAllPlayers();
+
+#if 0
+#if NEW_TILESIZE
+  BlitScreenToBitmap(backbuffer);
+#else
+  /* blit playfield from scroll buffer to normal back buffer */
+  if (setup.soft_scrolling)
+  {
+    int fx = FX, fy = FY;
+
+    fx += (ScreenMovDir & (MV_LEFT|MV_RIGHT) ? ScreenGfxPos : 0);
+    fy += (ScreenMovDir & (MV_UP|MV_DOWN)    ? ScreenGfxPos : 0);
+
+    BlitBitmap(fieldbuffer, backbuffer, fx,fy, SXSIZE,SYSIZE, SX,SY);
+  }
+#endif
+#endif
+}
+
+void RedrawPlayfield()
+{
+  if (game_status != GAME_MODE_PLAYING)
+    return;
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    RedrawPlayfield_EM(TRUE);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    RedrawPlayfield_SP(TRUE);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    RedrawPlayfield_RND();
+
+  BlitScreenToBitmap(backbuffer);
+
+  BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
+            gfx.sx, gfx.sy);
+}
+
+#else
+
 void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
 {
   if (game_status == GAME_MODE_PLAYING &&
@@ -233,6 +422,8 @@ void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
   BlitBitmap(drawto, window, x, y, width, height, x, y);
 }
 
+#endif
+
 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
 {
   Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
@@ -304,10 +495,12 @@ void DrawMaskedBorder(int redraw_mask)
   }
 }
 
-void BlitScreenToBitmap(Bitmap *target_bitmap)
+static void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
 {
   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
   int fx = FX, fy = FY;
+  int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
+  int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
 
 #if NEW_TILESIZE
   int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
@@ -358,6 +551,28 @@ void BlitScreenToBitmap(Bitmap *target_bitmap)
         fx, fy);
 #endif
 
+#if 1
+  if (full_lev_fieldx <= SCR_FIELDX)
+  {
+    // printf(":1: PLAYFIELD FITS TO SCREEN [%d, %d, %d]\n", fx, ffx, dx_var);
+
+    if (EVEN(SCR_FIELDX))
+      fx = 2 * TILEX_VAR - (ODD(lev_fieldx)  ? TILEX_VAR / 2 : 0);
+    else
+      fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
+
+    // printf(":2: PLAYFIELD FITS TO SCREEN [%d, %d, %d]\n", fx, ffx, dx_var);
+  }
+
+  if (full_lev_fieldy <= SCR_FIELDY)
+  {
+    if (EVEN(SCR_FIELDY))
+      fy = 2 * TILEY_VAR - (ODD(lev_fieldy)  ? TILEY_VAR / 2 : 0);
+    else
+      fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
+  }
+#endif
+
   if (border.draw_masked[GAME_MODE_PLAYING])
   {
     if (buffer != backbuffer)
@@ -377,6 +592,16 @@ void BlitScreenToBitmap(Bitmap *target_bitmap)
   }
 }
 
+void BlitScreenToBitmap(Bitmap *target_bitmap)
+{
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    BlitScreenToBitmap_EM(target_bitmap);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    BlitScreenToBitmap_SP(target_bitmap);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    BlitScreenToBitmap_RND(target_bitmap);
+}
+
 void BackToFront()
 {
   int x, y;
@@ -486,15 +711,37 @@ void BackToFront()
 
   if (redraw_mask & REDRAW_ALL)
   {
+#if 0
+    if (game_status != GAME_MODE_PLAYING ||
+       redraw_mask & REDRAW_FROM_BACKBUFFER)
+    {
+#if 0
+      printf("::: REDRAW_ALL [%d]\n", FrameCounter);
+#endif
+
+      BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+      redraw_mask = REDRAW_NONE;
+    }
+    else
+    {
+      redraw_mask &= ~REDRAW_ALL;
+    }
+#else
+#if 0
+    printf("::: REDRAW_ALL [%d]\n", FrameCounter);
+#endif
+
     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
     redraw_mask = REDRAW_NONE;
+#endif
   }
 
   if (redraw_mask & REDRAW_FIELD)
   {
 #if 0
-    printf("::: REDRAW_FIELD\n");
+    printf("::: REDRAW_FIELD [%d]\n", FrameCounter);
 #endif
 
     if (game_status != GAME_MODE_PLAYING ||
@@ -506,7 +753,7 @@ void BackToFront()
     else
     {
 #if 1
-      BlitScreenToBitmap(window);
+      BlitScreenToBitmap_RND(window);
 #else
       int fx = FX, fy = FY;
 
@@ -888,11 +1135,28 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
       ClearRectangle(backbuffer, x, y, width, height);
 #endif
 
+#if 1
+
+#if 1
+    BlitBitmap(backbuffer, window, x, y, width, height, x, y);
+
+    redraw_mask &= ~fade_mask;
+#else
+    /* always redraw area that was explicitly marked to fade */
+    redraw_mask |= fade_mask;
+
+    BackToFront();
+#endif
+
+#else
+
 #if 1
     BlitBitmap(backbuffer, window, x, y, width, height, x, y);
     redraw_mask = REDRAW_NONE;
+    // (^^^ WRONG; should be "redraw_mask &= ~fade_mask" if done this way)
 #else
     BackToFront();
+#endif
 #endif
 
     return;
@@ -1088,6 +1352,17 @@ void DrawBackground(int x, int y, int width, int height)
 #endif
 
 #if 1
+
+#if 1
+  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;
+#else
   /* (this only works for the current arrangement of playfield and panels) */
   if (x < gfx.dx)
     redraw_mask |= REDRAW_FIELD;
@@ -1095,6 +1370,8 @@ void DrawBackground(int x, int y, int width, int height)
     redraw_mask |= REDRAW_DOOR_1;
   else
     redraw_mask |= REDRAW_DOOR_2;
+#endif
+
 #else
   /* (this is just wrong (when drawing to one of the two door panel areas)) */
   redraw_mask |= REDRAW_FIELD;
@@ -1241,12 +1518,28 @@ void getSizedGraphicSourceExt(int graphic, int frame, int tilesize_raw,
   Bitmap *src_bitmap = g->bitmap;
   int tilesize = MIN(MAX(1, tilesize_raw), TILESIZE);
   int offset_calc_pos = log_2(tilesize);
+  int bitmap_width  = src_bitmap->width;
+  int bitmap_height = src_bitmap->height;
   int width_mult  = offset_calc[offset_calc_pos].width_mult;
   int width_div   = offset_calc[offset_calc_pos].width_div;
   int height_mult = offset_calc[offset_calc_pos].height_mult;
   int height_div  = offset_calc[offset_calc_pos].height_div;
-  int startx = src_bitmap->width * width_mult / width_div;
-  int starty = src_bitmap->height * height_mult / height_div;
+  int startx = bitmap_width * width_mult / width_div;
+  int starty = bitmap_height * height_mult / height_div;
+
+#if NEW_GAME_TILESIZE
+
+  int src_x = (g->src_x + (get_backside ? g->offset2_x : 0)) *
+    tilesize_raw / TILESIZE;
+  int src_y = (g->src_y + (get_backside ? g->offset2_y : 0)) *
+    tilesize_raw / TILESIZE;
+  int width = g->width * tilesize_raw / TILESIZE;
+  int height = g->height * tilesize_raw / TILESIZE;
+  int offset_x = g->offset_x * tilesize_raw / TILESIZE;
+  int offset_y = g->offset_y * tilesize_raw / TILESIZE;
+
+#else
+
 #if NEW_TILESIZE
   int src_x = (g->src_x + (get_backside ? g->offset2_x : 0)) *
     tilesize / TILESIZE;
@@ -1261,6 +1554,34 @@ void getSizedGraphicSourceExt(int graphic, int frame, int tilesize_raw,
   int offset_x = g->offset_x * tilesize / TILESIZE;
   int offset_y = g->offset_y * tilesize / TILESIZE;
 
+#endif
+
+#if NEW_GAME_TILESIZE
+  if (game.tile_size != TILESIZE)
+  {
+    int bitmap_width_std =
+      bitmap_width * TILESIZE / (TILESIZE + game.tile_size);
+    int bitmap_height_std =
+      bitmap_height * TILESIZE / game.tile_size * 3 / 2;
+
+    if (tilesize_raw == game.tile_size)
+    {
+      startx = bitmap_width_std;
+      starty = 0;
+    }
+    else
+    {
+      bitmap_width = bitmap_width_std;
+
+      if (game.tile_size > TILESIZE * 3 / 2)
+       bitmap_height = bitmap_height_std;
+
+      startx = bitmap_width * width_mult / width_div;
+      starty = bitmap_height * height_mult / height_div;
+    }
+  }
+#endif
+
   if (g->offset_y == 0)                /* frames are ordered horizontally */
   {
     int max_width = g->anim_frames_per_line * width;
@@ -1911,8 +2232,8 @@ static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
 #if NEW_TILESIZE
   width  = crumbled_border_size * TILESIZE_VAR / TILESIZE;
   height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
-  cx = (dx > 0 ? TILEX - crumbled_border_size : 0) * TILESIZE_VAR / TILESIZE;
-  cy = (dy > 0 ? TILEY - crumbled_border_size : 0) * TILESIZE_VAR / TILESIZE;
+  cx = (dx > 0 ? TILESIZE_VAR - width  : 0);
+  cy = (dy > 0 ? TILESIZE_VAR - height : 0);
 
   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
             width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
@@ -1935,6 +2256,8 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
   int width, height, bx, by, cx, cy;
   int sx = SCREENX(x), sy = SCREENY(y);
   int crumbled_border_size = graphic_info[graphic].border_size;
+  int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
+  int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
   int i;
 
   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
@@ -1942,10 +2265,10 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
   /* draw simple, sloppy, non-corner-accurate crumbled border */
 
 #if 1
-  width  = (dir == 1 || dir == 2 ? crumbled_border_size : TILEX);
-  height = (dir == 0 || dir == 3 ? crumbled_border_size : TILEY);
-  cx = (dir == 2 ? TILEX - crumbled_border_size : 0);
-  cy = (dir == 3 ? TILEY - crumbled_border_size : 0);
+  width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
+  height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
+  cx = (dir == 2 ? crumbled_border_pos_var : 0);
+  cy = (dir == 3 ? crumbled_border_pos_var : 0);
 #else
   if (dir == 1 || dir == 2)            /* left or right crumbled border */
   {
@@ -1965,12 +2288,12 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
 
 #if NEW_TILESIZE
   BlitBitmap(src_bitmap, drawto_field,
-            src_x + cx * TILESIZE_VAR / TILESIZE,
-            src_y + cy * TILESIZE_VAR / TILESIZE,
-            width * TILESIZE_VAR / TILESIZE,
-            height * TILESIZE_VAR / TILESIZE,
-            FX + sx * TILEX_VAR + cx * TILESIZE_VAR / TILESIZE,
-            FY + sy * TILEY_VAR + cy * TILESIZE_VAR / TILESIZE);
+            src_x + cx,
+            src_y + cy,
+            width,
+            height,
+            FX + sx * TILEX_VAR + cx,
+            FY + sy * TILEY_VAR + cy);
 #else
   BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
             width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
@@ -1984,7 +2307,7 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
   /* correct corners of crumbled border, if needed */
 
 #if 1
-  for (i = -1; i <= 1; i+=2)
+  for (i = -1; i <= 1; i += 2)
   {
     int xx = x + (dir == 0 || dir == 3 ? i : 0);
     int yy = y + (dir == 1 || dir == 2 ? i : 0);
@@ -1998,13 +2321,13 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
     {
       /* no crumbled corner, but continued crumbled border */
 
-      int c1 = (dir == 2 || dir == 3 ? TILESIZE - crumbled_border_size : 0);
-      int c2 = (i == 1 ? TILESIZE - crumbled_border_size : 0);
-      int b1 = (i == 1 ? crumbled_border_size :
-               TILESIZE - 2 * crumbled_border_size);
+      int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
+      int c2 = (i == 1 ? crumbled_border_pos_var : 0);
+      int b1 = (i == 1 ? crumbled_border_size_var :
+               TILESIZE_VAR - 2 * crumbled_border_size_var);
 
-      width  = crumbled_border_size;
-      height = crumbled_border_size;
+      width  = crumbled_border_size_var;
+      height = crumbled_border_size_var;
 
       if (dir == 1 || dir == 2)
       {
@@ -2023,12 +2346,12 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
 
 #if NEW_TILESIZE
       BlitBitmap(src_bitmap, drawto_field,
-                src_x + bx * TILESIZE_VAR / TILESIZE,
-                src_y + by * TILESIZE_VAR / TILESIZE,
-                width * TILESIZE_VAR / TILESIZE,
-                height * TILESIZE_VAR / TILESIZE,
-                FX + sx * TILEX_VAR + cx * TILESIZE_VAR / TILESIZE,
-                FY + sy * TILEY_VAR + cy * TILESIZE_VAR / TILESIZE);
+                src_x + bx,
+                src_y + by,
+                width,
+                height,
+                FX + sx * TILEX_VAR + cx,
+                FY + sy * TILEY_VAR + cy);
 #else
       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
                 width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
@@ -2465,49 +2788,59 @@ void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
 }
 
-void DrawEnvelopeBackground(int envelope_nr, int startx, int starty,
-                           int x, int y, int xsize, int ysize, int font_nr)
+void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
+                                int x, int y, int xsize, int ysize,
+                                int tile_width, int tile_height)
 {
-  int font_width  = getFontWidth(font_nr);
-  int font_height = getFontHeight(font_nr);
-  int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
   Bitmap *src_bitmap;
   int src_x, src_y;
-  int dst_x = SX + startx + x * font_width;
-  int dst_y = SY + starty + y * font_height;
+  int dst_x = startx + x * tile_width;
+  int dst_y = starty + y * tile_height;
   int width  = graphic_info[graphic].width;
   int height = graphic_info[graphic].height;
-  int inner_width  = MAX(width  - 2 * font_width,  font_width);
-  int inner_height = MAX(height - 2 * font_height, font_height);
-  int inner_sx = (width >= 3 * font_width ? font_width : 0);
-  int inner_sy = (height >= 3 * font_height ? font_height : 0);
+  int inner_width_raw  = MAX(width  - 2 * tile_width,  tile_width);
+  int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
+  int inner_width  = inner_width_raw  - (inner_width_raw  % tile_width);
+  int inner_height = inner_height_raw - (inner_height_raw % tile_height);
+  int inner_sx = (width  >= 3 * tile_width  ? tile_width  : 0);
+  int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
   boolean draw_masked = graphic_info[graphic].draw_masked;
 
   getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
 
-  if (src_bitmap == NULL || width < font_width || height < font_height)
+  if (src_bitmap == NULL || width < tile_width || height < tile_height)
   {
-    ClearRectangle(drawto, dst_x, dst_y, font_width, font_height);
+    ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
     return;
   }
 
-  src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - font_width  :
-           inner_sx + (x - 1) * font_width  % inner_width);
-  src_y += (y == 0 ? 0 : y == ysize - 1 ? height - font_height :
-           inner_sy + (y - 1) * font_height % inner_height);
+  src_x += (x == 0 ? 0 : x == xsize - 1 ? width  - tile_width  :
+           inner_sx + (x - 1) * tile_width  % inner_width);
+  src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
+           inner_sy + (y - 1) * tile_height % inner_height);
 
   if (draw_masked)
   {
     SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
                  dst_x - src_x, dst_y - src_y);
-    BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, font_width, font_height,
+    BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
                     dst_x, dst_y);
   }
   else
-    BlitBitmap(src_bitmap, drawto, src_x, src_y, font_width, font_height,
+    BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
               dst_x, dst_y);
 }
 
+void DrawEnvelopeBackground(int graphic, int startx, int starty,
+                           int x, int y, int xsize, int ysize, int font_nr)
+{
+  int font_width  = getFontWidth(font_nr);
+  int font_height = getFontHeight(font_nr);
+
+  DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
+                             font_width, font_height);
+}
+
 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
 {
   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
@@ -2535,8 +2868,8 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
   {
     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
-    int sx = (SXSIZE - xsize * font_width)  / 2;
-    int sy = (SYSIZE - ysize * font_height) / 2;
+    int sx = SX + (SXSIZE - xsize * font_width)  / 2;
+    int sy = SY + (SYSIZE - ysize * font_height) / 2;
     int xx, yy;
 
     SetDrawtoField(DRAW_BUFFERED);
@@ -2549,17 +2882,18 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
 
     SetDrawtoField(DRAW_BACKBUFFER);
 
-    for (yy = 0; yy < ysize; yy++) for (xx = 0; xx < xsize; xx++)
-      DrawEnvelopeBackground(envelope_nr, sx,sy, xx,yy, xsize, ysize, font_nr);
+    for (yy = 0; yy < ysize; yy++)
+      for (xx = 0; xx < xsize; xx++)
+       DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
 
 #if 1
-    DrawTextBuffer(SX + sx + font_width, SY + sy + font_height,
+    DrawTextBuffer(sx + font_width, sy + font_height,
                   level.envelope[envelope_nr].text, font_nr, max_xsize,
                   xsize - 2, ysize - 2, 0, mask_mode,
                   level.envelope[envelope_nr].autowrap,
                   level.envelope[envelope_nr].centered, FALSE);
 #else
-    DrawTextToTextArea(SX + sx + font_width, SY + sy + font_height,
+    DrawTextToTextArea(sx + font_width, sy + font_height,
                       level.envelope[envelope_nr].text, font_nr, max_xsize,
                       xsize - 2, ysize - 2, mask_mode);
 #endif
@@ -2571,67 +2905,342 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
   }
 }
 
-void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
+void ShowEnvelope(int envelope_nr)
 {
-#if 1
-  int envelope_nr = 0;
-#endif
+  int element = EL_ENVELOPE_1 + envelope_nr;
   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
-  Bitmap *src_bitmap = graphic_info[graphic].bitmap;
-  int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
+  int sound_opening = element_info[element].sound[ACTION_OPENING];
+  int sound_closing = element_info[element].sound[ACTION_CLOSING];
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
   boolean no_delay = (tape.warp_forward);
-  unsigned int anim_delay = 0;
-  int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
-  int anim_delay_value = (no_delay ? 0 : frame_delay_value);
-#if 1
-  int max_word_len = maxWordLengthInString(text);
-  int font_nr = (max_word_len > 7 ? FONT_TEXT_1 : FONT_TEXT_2);
-#else
-  int font_nr = FONT_ENVELOPE_1 + envelope_nr;
-#endif
-  int font_width = getFontWidth(font_nr);
-  int font_height = getFontHeight(font_nr);
-#if 1
+  int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
+  int wait_delay_value = (no_delay ? 0 : normal_delay_value);
+  int anim_mode = graphic_info[graphic].anim_mode;
+  int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
+                       anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
 
-#if 1
-  int max_xsize = DXSIZE / font_width;
-  int max_ysize = DYSIZE / font_height;
-#else
-  int max_xsize = 7;   /* tools.c: MAX_REQUEST_LINE_FONT1_LEN == 7 */
-  int max_ysize = 13;  /* tools.c: MAX_REQUEST_LINES == 13 */
-#endif
+  game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
 
-#else
-  int max_xsize = level.envelope[envelope_nr].xsize;
-  int max_ysize = level.envelope[envelope_nr].ysize;
-#endif
-  int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
-  int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
-  int xend = max_xsize;
-  int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
-  int xstep = (xstart < xend ? 1 : 0);
-  int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
-  int x, y;
+  PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
 
-#if 1
-  char *text_ptr;
-  char *text_copy = getStringCopy(text);
-#else
-#if 1
-  font_nr = FONT_TEXT_2;
+  if (anim_mode == ANIM_DEFAULT)
+    AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
 
-  if (maxWordLengthInString(text) > 7) /* MAX_REQUEST_LINE_FONT1_LEN == 7 */
-  {
-    max_xsize = 10;    /* tools.c: MAX_REQUEST_LINE_FONT2_LEN == 10 */
-    font_nr = FONT_TEXT_1;
-  }
-#else
-  int max_word_len = 0;
-  char *text_ptr;
-  char *text_copy = getStringCopy(text);
+  AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
 
-  font_nr = FONT_TEXT_2;
+  if (tape.playing)
+    Delay(wait_delay_value);
+  else
+    WaitForEventToContinue();
+
+  PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
+
+  if (anim_mode != ANIM_NONE)
+    AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
+
+  if (anim_mode == ANIM_DEFAULT)
+    AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
+
+  game.envelope_active = FALSE;
+
+  SetDrawtoField(DRAW_BUFFERED);
+
+  redraw_mask |= REDRAW_FIELD;
+  BackToFront();
+}
+
+static void setRequestPosition(int *x, int *y, boolean add_border_size)
+{
+  int border_size = request.border_size;
+  int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
+  int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
+  int sx = sx_center - request.width  / 2;
+  int sy = sy_center - request.height / 2;
+
+  if (add_border_size)
+  {
+    sx += border_size;
+    sy += border_size;
+  }
+
+  *x = sx;
+  *y = sy;
+}
+
+void DrawEnvelopeRequest(char *text)
+{
+  char *text_final = text;
+  char *text_door_style = NULL;
+  int graphic = IMG_BACKGROUND_REQUEST;
+  Bitmap *src_bitmap = graphic_info[graphic].bitmap;
+  int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
+  int font_nr = FONT_REQUEST;
+  int font_width = getFontWidth(font_nr);
+  int font_height = getFontHeight(font_nr);
+  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 width = request.width;
+  int height = request.height;
+  int tile_size = request.step_offset;
+  int x_steps = width  / tile_size;
+  int y_steps = height / tile_size;
+  int sx, sy;
+  int i, x, y;
+
+  if (request.wrap_single_words)
+  {
+    char *src_text_ptr, *dst_text_ptr;
+
+    text_door_style = checked_malloc(2 * strlen(text) + 1);
+
+    src_text_ptr = text;
+    dst_text_ptr = text_door_style;
+
+    while (*src_text_ptr)
+    {
+      if (*src_text_ptr == ' ' ||
+         *src_text_ptr == '?' ||
+         *src_text_ptr == '!')
+       *dst_text_ptr++ = '\n';
+
+      if (*src_text_ptr != ' ')
+       *dst_text_ptr++ = *src_text_ptr;
+
+      src_text_ptr++;
+    }
+
+    *dst_text_ptr = '\0';
+
+    text_final = text_door_style;
+  }
+
+  setRequestPosition(&sx, &sy, FALSE);
+
+  ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
+
+  for (y = 0; y < y_steps; y++)
+    for (x = 0; x < x_steps; x++)
+      DrawEnvelopeBackgroundTiles(graphic, sx, sy,
+                                 x, y, x_steps, y_steps,
+                                 tile_size, tile_size);
+
+  DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
+                line_length, -1, max_lines, line_spacing, mask_mode,
+                request.autowrap, request.centered, FALSE);
+
+  for (i = 0; i < NUM_TOOL_BUTTONS; i++)
+    RedrawGadget(tool_gadget[i]);
+
+  // store readily prepared envelope request for later use when animating
+  BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+#if 0
+  // !!! TEST !!!
+  BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+  BlitBitmap(bitmap_db_cross, backbuffer, sx, sy, width, height, sx, sy);
+
+  redraw_mask = REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+  BackToFront();
+
+  Delay(3000);
+
+  BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+  redraw_mask = REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+  BackToFront();
+
+  Delay(1000);
+#endif
+
+  if (text_door_style)
+    free(text_door_style);
+}
+
+#if 1
+
+void AnimateEnvelopeRequest(int anim_mode, int action)
+{
+  int graphic = IMG_BACKGROUND_REQUEST;
+  boolean draw_masked = graphic_info[graphic].draw_masked;
+#if 1
+  int delay_value_normal = request.step_delay;
+  int delay_value_fast = delay_value_normal / 2;
+#else
+  int delay_value_normal = GameFrameDelay;
+  int delay_value_fast = FfwdFrameDelay;
+#endif
+  boolean ffwd_delay = (tape.playing && tape.fast_forward);
+  boolean no_delay = (tape.warp_forward);
+  int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
+  int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0);
+  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 max_xsize_inner = max_xsize - 2;
+  int max_ysize_inner = max_ysize - 2;
+
+  int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
+  int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
+  int xend = max_xsize_inner;
+  int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
+  int xstep = (xstart < xend ? 1 : 0);
+  int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
+  int x, y;
+
+  for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
+  {
+    int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
+    int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
+    int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
+    int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
+    int src_x = sx_center - width  / 2;
+    int src_y = sy_center - height / 2;
+    int dst_x = sx_center - xsize * tile_size / 2;
+    int dst_y = sy_center - ysize * tile_size / 2;
+    int xsize_size_left = (xsize - 1) * tile_size;
+    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 xx, yy;
+
+    BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+#if 1
+    for (yy = 0; yy < 2; yy++)
+    {
+      for (xx = 0; xx < 2; xx++)
+      {
+       int src_xx = src_x + xx * max_xsize_pos;
+       int src_yy = src_y + yy * max_ysize_pos;
+       int dst_xx = dst_x + xx * xsize_size_left;
+       int dst_yy = dst_y + yy * ysize_size_top;
+       int xx_size = (xx ? tile_size : xsize_size_left);
+       int yy_size = (yy ? tile_size : ysize_size_top);
+
+       if (draw_masked)
+         BlitBitmapMasked(bitmap_db_cross, backbuffer,
+                          src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
+       else
+         BlitBitmap(bitmap_db_cross, backbuffer,
+                    src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
+      }
+    }
+#else
+    BlitBitmap(bitmap_db_cross, backbuffer,
+              src_x, src_y,
+              xsize_size_left, ysize_size_top,
+              dst_x, dst_y);
+    BlitBitmap(bitmap_db_cross, backbuffer,
+              src_x + max_xsize_pos, src_y,
+              tile_size, ysize_size_top,
+              dst_x + xsize_size_left, dst_y);
+    BlitBitmap(bitmap_db_cross, backbuffer,
+              src_x, src_y + max_ysize_pos,
+              xsize_size_left, tile_size,
+              dst_x, dst_y + ysize_size_top);
+    BlitBitmap(bitmap_db_cross, backbuffer,
+              src_x + max_xsize_pos, src_y + max_ysize_pos,
+              tile_size, tile_size,
+              dst_x + xsize_size_left, dst_y + ysize_size_top);
+#endif
+
+#if 0
+    redraw_mask = REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    // redraw_mask |= REDRAW_ALL | REDRAW_FROM_BACKBUFFER;
+#else
+  /* CHECK AGAIN (previous code reactivated) */
+    redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+#endif
+
+#if 1
+    DoAnimation();
+    BackToFront();
+#else
+    BackToFront();
+#endif
+
+    WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
+  }
+}
+
+#else
+
+void AnimateEnvelopeRequest(char *text, int anim_mode, int action)
+{
+#if 0
+  int envelope_nr = 0;
+#endif
+#if 1
+  int graphic = IMG_BACKGROUND_REQUEST;
+#else
+  int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
+#endif
+  Bitmap *src_bitmap = graphic_info[graphic].bitmap;
+  int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
+  boolean ffwd_delay = (tape.playing && tape.fast_forward);
+  boolean no_delay = (tape.warp_forward);
+  unsigned int anim_delay = 0;
+  int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+  int anim_delay_value = (no_delay ? 0 : frame_delay_value + 500 * 0);
+#if 1
+  int max_word_len = maxWordLengthInString(text);
+  int font_nr = (max_word_len > 7 ? FONT_TEXT_1 : FONT_TEXT_2);
+#else
+  int font_nr = FONT_ENVELOPE_1 + envelope_nr;
+#endif
+  int font_width = getFontWidth(font_nr);
+  int font_height = getFontHeight(font_nr);
+  int line_spacing = 2 * 1;
+#if 1
+
+#if 1
+  int max_xsize = DXSIZE / font_width;
+  // int max_ysize = DYSIZE / font_height;
+  int max_ysize = DYSIZE / (font_height + line_spacing);
+#else
+  int max_xsize = 7;   /* tools.c: MAX_REQUEST_LINE_FONT1_LEN == 7 */
+  int max_ysize = 13;  /* tools.c: MAX_REQUEST_LINES == 13 */
+#endif
+
+#else
+  int max_xsize = level.envelope[envelope_nr].xsize;
+  int max_ysize = level.envelope[envelope_nr].ysize;
+#endif
+  int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
+  int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
+  int xend = max_xsize;
+  int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
+  int xstep = (xstart < xend ? 1 : 0);
+  int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
+  int x, y;
+
+#if 1
+  char *text_ptr;
+  char *text_copy = getStringCopy(text);
+#else
+#if 1
+  font_nr = FONT_TEXT_2;
+
+  if (maxWordLengthInString(text) > 7) /* MAX_REQUEST_LINE_FONT1_LEN == 7 */
+  {
+    max_xsize = 10;    /* tools.c: MAX_REQUEST_LINE_FONT2_LEN == 10 */
+    font_nr = FONT_TEXT_1;
+  }
+#else
+  int max_word_len = 0;
+  char *text_ptr;
+  char *text_copy = getStringCopy(text);
+
+  font_nr = FONT_TEXT_2;
 
   for (text_ptr = text; *text_ptr; text_ptr++)
   {
@@ -2666,8 +3275,9 @@ void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
   {
     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
     int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
-    int sx = (SXSIZE - xsize * font_width)  / 2;
-    int sy = (SYSIZE - ysize * font_height) / 2;
+    int sx = SX + (SXSIZE - xsize * font_width)  / 2;
+    // int sy = SX + (SYSIZE - ysize * font_height) / 2;
+    int sy = SY + (SYSIZE - ysize * (font_height + line_spacing)) / 2;
     int xx, yy;
 
 #if 1
@@ -2684,18 +3294,21 @@ void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
     SetDrawtoField(DRAW_BACKBUFFER);
 #endif
 
-    for (yy = 0; yy < ysize; yy++) for (xx = 0; xx < xsize; xx++)
-      DrawEnvelopeBackground(envelope_nr, sx,sy, xx,yy, xsize, ysize, font_nr);
+    for (yy = 0; yy < ysize; yy++)
+      for (xx = 0; xx < xsize; xx++)
+       DrawEnvelopeBackgroundTiles(graphic, sx, sy, xx, yy, xsize, ysize,
+                                   getFontWidth(font_nr),
+                                   getFontHeight(font_nr) + line_spacing);
 
 #if 1
 
 #if 1
-    DrawTextBuffer(SX + sx + font_width, SY + sy + font_height + 8,
+    DrawTextBuffer(sx + font_width, sy + font_height + 8,
                   text_copy, font_nr, max_xsize,
-                  xsize - 2, ysize - 2, 2, mask_mode,
+                  xsize - 2, ysize - 2, line_spacing, mask_mode,
                   FALSE, TRUE, FALSE);
 #else
-    DrawTextBuffer(SX + sx + font_width, SY + sy + font_height,
+    DrawTextBuffer(sx + font_width, sy + font_height,
                   level.envelope[envelope_nr].text, font_nr, max_xsize,
                   xsize - 2, ysize - 2, 0, mask_mode,
                   level.envelope[envelope_nr].autowrap,
@@ -2703,21 +3316,31 @@ void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
 #endif
 
 #else
-    DrawTextToTextArea(SX + sx + font_width, SY + sy + font_height,
+    DrawTextToTextArea(sx + font_width, sy + font_height,
                       level.envelope[envelope_nr].text, font_nr, max_xsize,
                       xsize - 2, ysize - 2, mask_mode);
 #endif
 
     /* copy request gadgets to door backbuffer */
 #if 1
+    /*
     if ((ysize - 2) > 13)
       BlitBitmap(bitmap_db_door, drawto,
                 DOOR_GFX_PAGEX1 + (DXSIZE - (xsize - 2) * font_width) / 2,
                 DOOR_GFX_PAGEY1 + 13 * font_height,
                 (xsize - 2) * font_width,
                 (ysize - 2 - 13) * font_height,
-                SX + sx + font_width,
-                SY + sy + font_height * (1 + 13));
+                sx + font_width,
+                sy + font_height * (1 + 13));
+    */
+    if ((ysize - 2) > 13)
+      BlitBitmap(bitmap_db_door, drawto,
+                DOOR_GFX_PAGEX1 + (DXSIZE - (xsize - 2) * font_width) / 2,
+                DOOR_GFX_PAGEY1 + 11 * (font_height + line_spacing * 0),
+                (xsize - 2) * font_width,
+                (ysize - 2 - 13) * (font_height + line_spacing),
+                sx + font_width,
+                sy + (font_height + line_spacing) * (1 + 13));
 #else
     if ((ysize - 2) > 13)
       BlitBitmap(bitmap_db_door, drawto,
@@ -2725,8 +3348,8 @@ void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
                 DOOR_GFX_PAGEY1 + 13 * font_height,
                 (xsize - 2) * font_width,
                 (ysize - 2 - 13) * font_height,
-                SX + sx + font_width,
-                SY + sy + font_height * (1 + 13));
+                sx + font_width,
+                sy + font_height * (1 + 13));
 #endif
 
 #if 1
@@ -2751,61 +3374,25 @@ void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
 #endif
 }
 
-void ShowEnvelope(int envelope_nr)
-{
-  int element = EL_ENVELOPE_1 + envelope_nr;
-  int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
-  int sound_opening = element_info[element].sound[ACTION_OPENING];
-  int sound_closing = element_info[element].sound[ACTION_CLOSING];
-  boolean ffwd_delay = (tape.playing && tape.fast_forward);
-  boolean no_delay = (tape.warp_forward);
-  int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
-  int wait_delay_value = (no_delay ? 0 : normal_delay_value);
-  int anim_mode = graphic_info[graphic].anim_mode;
-  int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
-                       anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
-
-  game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
-
-  PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
-
-  if (anim_mode == ANIM_DEFAULT)
-    AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
-
-  AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
-
-  if (tape.playing)
-    Delay(wait_delay_value);
-  else
-    WaitForEventToContinue();
-
-  PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
-
-  if (anim_mode != ANIM_NONE)
-    AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
-
-  if (anim_mode == ANIM_DEFAULT)
-    AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
-
-  game.envelope_active = FALSE;
-
-  SetDrawtoField(DRAW_BUFFERED);
-
-  redraw_mask |= REDRAW_FIELD;
-  BackToFront();
-}
+#endif
 
-void ShowEnvelopeDoor(char *text, int action)
+void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
 {
 #if 1
   int last_game_status = game_status;  /* save current game status */
   // int last_draw_background_mask = gfx.draw_background_mask;
-  int envelope_nr = 0;
 #endif
+#if 1
+  int graphic = IMG_BACKGROUND_REQUEST;
+  int sound_opening = SND_REQUEST_OPENING;
+  int sound_closing = SND_REQUEST_CLOSING;
+#else
+  int envelope_nr = 0;
   int element = EL_ENVELOPE_1 + envelope_nr;
   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
   int sound_opening = element_info[element].sound[ACTION_OPENING];
   int sound_closing = element_info[element].sound[ACTION_CLOSING];
+#endif
 #if 0
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
   boolean no_delay = (tape.warp_forward);
@@ -2815,10 +3402,30 @@ void ShowEnvelopeDoor(char *text, int action)
   int anim_mode = graphic_info[graphic].anim_mode;
   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
                        anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
+#if 0
+  char *text_copy = getStringCopy(text);
+  char *text_ptr;
+
+  for (text_ptr = text_copy; *text_ptr; text_ptr++)
+    if (*text_ptr == ' ')
+      *text_ptr = '\n';
+#endif
 
 #if 1
   if (game_status == GAME_MODE_PLAYING)
   {
+#if 1
+#if 1
+    BlitScreenToBitmap(backbuffer);
+#else
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      BlitScreenToBitmap_EM(backbuffer);
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      BlitScreenToBitmap_SP(backbuffer);
+    else
+      BlitScreenToBitmap_RND(backbuffer);
+#endif
+#else
     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
       BlitScreenToBitmap_EM(backbuffer);
     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
@@ -2827,6 +3434,7 @@ void ShowEnvelopeDoor(char *text, int action)
     {
       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
     }
+#endif
   }
 
   SetDrawtoField(DRAW_BACKBUFFER);
@@ -2837,14 +3445,39 @@ void ShowEnvelopeDoor(char *text, int action)
   {
     BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
-    if (game_status != GAME_MODE_MAIN)
-      InitAnimation();
+#if 1
+  if (req_state & REQ_ASK)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
   }
-
-  /* force DOOR font inside door area */
-  game_status = GAME_MODE_PSEUDO_DOOR;
-#endif
-
+  else if (req_state & REQ_CONFIRM)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
+  }
+  else if (req_state & REQ_PLAYER)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
+  }
+#endif
+
+#if 1
+    DrawEnvelopeRequest(text);
+#else
+    DrawEnvelopeRequest(text_copy);
+#endif
+
+    if (game_status != GAME_MODE_MAIN)
+      InitAnimation();
+  }
+
+  /* force DOOR font inside door area */
+  game_status = GAME_MODE_PSEUDO_DOOR;
+#endif
+
   game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
 
   if (action == ACTION_OPENING)
@@ -2852,9 +3485,9 @@ void ShowEnvelopeDoor(char *text, int action)
     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
 
     if (anim_mode == ANIM_DEFAULT)
-      AnimateEnvelopeDoor(text, ANIM_DEFAULT, ACTION_OPENING);
+      AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
 
-    AnimateEnvelopeDoor(text, main_anim_mode, ACTION_OPENING);
+    AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
 
 #if 0
     if (tape.playing)
@@ -2868,10 +3501,10 @@ void ShowEnvelopeDoor(char *text, int action)
     PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
 
     if (anim_mode != ANIM_NONE)
-      AnimateEnvelopeDoor(text, main_anim_mode, ACTION_CLOSING);
+      AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
 
     if (anim_mode == ANIM_DEFAULT)
-      AnimateEnvelopeDoor(text, ANIM_DEFAULT, ACTION_CLOSING);
+      AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
   }
 
   game.envelope_active = FALSE;
@@ -2879,6 +3512,11 @@ void ShowEnvelopeDoor(char *text, int action)
 #if 1
   // game_status = last_game_status;   /* restore current game status */
 
+#if 1
+  /* !!! CHECK AGAIN (SEE BELOW) !!! */
+  game_status = last_game_status;      /* restore current game status */
+#endif
+
   if (action == ACTION_CLOSING)
   {
     if (game_status != GAME_MODE_MAIN)
@@ -2892,10 +3530,11 @@ void ShowEnvelopeDoor(char *text, int action)
 
   // SetDrawBackgroundMask(last_draw_background_mask);
 
-#if 1
+#if 0
   redraw_mask = REDRAW_FIELD;
   // redraw_mask |= REDRAW_ALL;
 #else
+  /* CHECK AGAIN (previous code reactivated) */
   redraw_mask |= REDRAW_FIELD;
 #endif
 
@@ -2905,15 +3544,29 @@ void ShowEnvelopeDoor(char *text, int action)
 
   BackToFront();
 
+#if 0
   /* (important: after "BackToFront()", but before "SetDrawtoField()") */
   game_status = last_game_status;      /* restore current game status */
+#endif
 
+#if 1
+  if (action == ACTION_CLOSING &&
+      game_status == GAME_MODE_PLAYING &&
+      level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    SetDrawtoField(DRAW_BUFFERED);
+#else
   if (game_status == GAME_MODE_PLAYING &&
       level.game_engine_type == GAME_ENGINE_TYPE_RND)
     SetDrawtoField(DRAW_BUFFERED);
+#endif
+
 #else
   BackToFront();
 #endif
+
+#if 0
+  free(text_copy);
+#endif
 }
 
 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
@@ -2926,15 +3579,20 @@ void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
   BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
 }
 
-void DrawLevel()
+void DrawLevel(int draw_background_mask)
 {
   int x,y;
 
+#if 1
+  SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
+  SetDrawBackgroundMask(draw_background_mask);
+#else
 #if 1
   SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
   SetDrawBackgroundMask(REDRAW_FIELD);
 #else
   SetDrawBackgroundMask(REDRAW_NONE);
+#endif
 #endif
 
   ClearField();
@@ -2957,7 +3615,7 @@ void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
   redraw_mask |= REDRAW_FIELD;
 }
 
-static void DrawPreviewLevelExt(int from_x, int from_y)
+static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
 {
   boolean show_level_border = (BorderElement != EL_EMPTY);
   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
@@ -2967,14 +3625,28 @@ static void DrawPreviewLevelExt(int from_x, int from_y)
   int preview_height = preview.ysize * tile_size;
   int real_preview_xsize = MIN(level_xsize, preview.xsize);
   int real_preview_ysize = MIN(level_ysize, preview.ysize);
+  int real_preview_width  = real_preview_xsize * tile_size;
+  int real_preview_height = real_preview_ysize * tile_size;
   int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
   int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
   int x, y;
 
+#if 1
+  if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
+    return;
+#endif
+
+#if 0
+  dst_x += (preview_width  - real_preview_width)  / 2;
+  dst_y += (preview_height - real_preview_height) / 2;
+
+  DrawBackground(dst_x, dst_y, real_preview_width, real_preview_height);
+#else
   DrawBackground(dst_x, dst_y, preview_width, preview_height);
 
-  dst_x += (preview_width  - real_preview_xsize * tile_size) / 2;
-  dst_y += (preview_height - real_preview_ysize * tile_size) / 2;
+  dst_x += (preview_width  - real_preview_width)  / 2;
+  dst_y += (preview_height - real_preview_height) / 2;
+#endif
 
   for (x = 0; x < real_preview_xsize; x++)
   {
@@ -3026,6 +3698,9 @@ static void DrawPreviewLevelLabelExt(int mode)
   int font_nr = pos->font;
   int i;
 
+  if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
+    return;
+
   if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
       mode == MICROLABEL_IMPORTED_BY_HEAD)
@@ -3093,7 +3768,7 @@ static void DrawPreviewLevelLabelExt(int mode)
   redraw_mask |= REDRAW_MICROLEVEL;
 }
 
-void DrawPreviewLevel(boolean restart)
+static void DrawPreviewLevelExt(boolean restart)
 {
   static unsigned int scroll_delay = 0;
   static unsigned int label_delay = 0;
@@ -3130,7 +3805,7 @@ void DrawPreviewLevel(boolean restart)
     label_state = 1;
     label_counter = 0;
 
-    DrawPreviewLevelExt(from_x, from_y);
+    DrawPreviewLevelPlayfieldExt(from_x, from_y);
     DrawPreviewLevelLabelExt(label_state);
 
     /* initialize delay counters */
@@ -3165,7 +3840,8 @@ void DrawPreviewLevel(boolean restart)
       label_text[max_len_label_text] = '\0';
 
 #if 1
-      DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
+      if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
+       DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
 #else
       lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
       lypos = SY + MICROLABEL1_YPOS;
@@ -3232,7 +3908,7 @@ void DrawPreviewLevel(boolean restart)
        break;
     }
 
-    DrawPreviewLevelExt(from_x, from_y);
+    DrawPreviewLevelPlayfieldExt(from_x, from_y);
   }
 
   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
@@ -3279,6 +3955,16 @@ void DrawPreviewLevel(boolean restart)
   game_status = last_game_status;      /* restore current game status */
 }
 
+void DrawPreviewLevelInitial()
+{
+  DrawPreviewLevelExt(TRUE);
+}
+
+void DrawPreviewLevelAnimation()
+{
+  DrawPreviewLevelExt(FALSE);
+}
+
 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
                                    int graphic, int sync_frame, int mask_mode)
 {
@@ -3863,117 +4549,264 @@ void WaitForEventToContinue()
 #define MAX_REQUEST_LINE_FONT1_LEN     7
 #define MAX_REQUEST_LINE_FONT2_LEN     10
 
-boolean Request(char *text, unsigned int req_state)
+#if 1
+
+static int RequestHandleEvents(unsigned int req_state)
 {
-  int mx, my, ty, result = -1;
-  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;
-#if 0
-  int max_word_len = 0;
-#endif
-  char *text_ptr;
-  int i;
+  int result;
+  int mx, my;
 
-#if 1
-  global.use_envelope_request = 0;
-#endif
+  button_status = MB_RELEASED;
 
-#if 1
-  if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
-  {
-    max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
-    font_nr = FONT_TEXT_1;
-  }
-#else
-  for (text_ptr = text; *text_ptr; text_ptr++)
-  {
-    max_word_len = (*text_ptr != ' ' ? max_word_len + 1 : 0);
+  request_gadget_id = -1;
+  result = -1;
 
-    if (max_word_len > MAX_REQUEST_LINE_FONT1_LEN)
+  while (result < 0)
+  {
+    if (PendingEvent())
     {
-      max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
-#if 1
-      font_nr = FONT_TEXT_1;
-#else
-      font_nr = FONT_LEVEL_NUMBER;
-#endif
+      Event event;
 
-      break;
-    }
-  }
-#endif
+      NextEvent(&event);
 
-  if (game_status == GAME_MODE_PLAYING)
-  {
-    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-      BlitScreenToBitmap_EM(backbuffer);
-    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
-      BlitScreenToBitmap_SP(backbuffer);
-  }
+      switch (event.type)
+      {
+       case EVENT_BUTTONPRESS:
+       case EVENT_BUTTONRELEASE:
+       case EVENT_MOTIONNOTIFY:
+       {
+         if (event.type == EVENT_MOTIONNOTIFY)
+         {
+           if (!PointerInWindow(window))
+             continue; /* window and pointer are on different screens */
 
-  /* disable deactivated drawing when quick-loading level tape recording */
-  if (tape.playing && tape.deactivate_display)
-    TapeDeactivateDisplayOff(TRUE);
+           if (!button_status)
+             continue;
 
-  SetMouseCursor(CURSOR_DEFAULT);
+           motion_status = TRUE;
+           mx = ((MotionEvent *) &event)->x;
+           my = ((MotionEvent *) &event)->y;
+         }
+         else
+         {
+           motion_status = FALSE;
+           mx = ((ButtonEvent *) &event)->x;
+           my = ((ButtonEvent *) &event)->y;
+           if (event.type == EVENT_BUTTONPRESS)
+             button_status = ((ButtonEvent *) &event)->button;
+           else
+             button_status = MB_RELEASED;
+         }
 
-#if defined(NETWORK_AVALIABLE)
-  /* pause network game while waiting for request to answer */
-  if (options.network &&
-      game_status == GAME_MODE_PLAYING &&
-      req_state & REQUEST_WAIT_FOR_INPUT)
-    SendToServer_PausePlaying();
-#endif
+         /* this sets 'request_gadget_id' */
+         HandleGadgets(mx, my, button_status);
 
-  old_door_state = GetDoorState();
+         switch (request_gadget_id)
+         {
+           case TOOL_CTRL_ID_YES:
+             result = TRUE;
+             break;
+           case TOOL_CTRL_ID_NO:
+             result = FALSE;
+             break;
+           case TOOL_CTRL_ID_CONFIRM:
+             result = TRUE | FALSE;
+             break;
 
-  /* simulate releasing mouse button over last gadget, if still pressed */
-  if (button_status)
-    HandleGadgets(-1, -1, 0);
+           case TOOL_CTRL_ID_PLAYER_1:
+             result = 1;
+             break;
+           case TOOL_CTRL_ID_PLAYER_2:
+             result = 2;
+             break;
+           case TOOL_CTRL_ID_PLAYER_3:
+             result = 3;
+             break;
+           case TOOL_CTRL_ID_PLAYER_4:
+             result = 4;
+             break;
 
-  UnmapAllGadgets();
+           default:
+             break;
+         }
 
-  /* draw released gadget before proceeding */
-  // BackToFront();
+         break;
+       }
 
-#if 0
-  if (old_door_state & DOOR_OPEN_1 && !global.use_envelope_request)
-#else
-  if (old_door_state & DOOR_OPEN_1)
+       case EVENT_KEYPRESS:
+         switch (GetEventKey((KeyEvent *)&event, TRUE))
+         {
+           case KSYM_space:
+             if (req_state & REQ_CONFIRM)
+               result = 1;
+             break;
+
+           case KSYM_Return:
+#if defined(TARGET_SDL2)
+           case KSYM_Menu:
 #endif
-  {
-#if 1
-    if (!global.use_envelope_request)
-      CloseDoor(DOOR_CLOSE_1);
-#else
-    CloseDoor(DOOR_CLOSE_1);
+             result = 1;
+             break;
+
+           case KSYM_Escape:
+#if defined(TARGET_SDL2)
+           case KSYM_Back:
 #endif
+             result = 0;
+             break;
 
-    /* save old door content */
-    BlitBitmap(bitmap_db_door, bitmap_db_door,
-              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
-              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
-  }
+           default:
+             break;
+         }
 
-#if 1
-  SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
-#endif
+         if (req_state & REQ_PLAYER)
+           result = 0;
+         break;
 
-  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
+       case EVENT_KEYRELEASE:
+         ClearPlayerAction();
+         break;
 
-  /* clear door drawing field */
-  DrawBackground(DX, DY, DXSIZE, DYSIZE);
+       default:
+         HandleOtherEvents(&event);
+         break;
+      }
+    }
+    else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
+    {
+      int joy = AnyJoystick();
 
-  /* force DOOR font inside door area */
-  game_status = GAME_MODE_PSEUDO_DOOR;
+      if (joy & JOY_BUTTON_1)
+       result = 1;
+      else if (joy & JOY_BUTTON_2)
+       result = 0;
+    }
 
-  /* write text for request */
-  for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
-  {
-    char text_line[max_request_line_len + 1];
-    int tx, tl, tc = 0;
+#if 1
+
+    if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
+    {
+      HandleGameActions();
+    }
+    else
+    {
+      DoAnimation();
+
+      if (!PendingEvent())     /* delay only if no pending events */
+       Delay(10);
+    }
+
+#if 1
+    game_status = GAME_MODE_PSEUDO_DOOR;
+#endif
+
+    BackToFront();
+
+#if 1
+    game_status = last_game_status;    /* restore current game status */
+#endif
+
+#else
+
+    DoAnimation();
+
+#if 1
+    if (!PendingEvent())       /* delay only if no pending events */
+      Delay(10);
+#else
+    /* don't eat all CPU time */
+    Delay(10);
+#endif
+
+#endif
+  }
+
+  return result;
+}
+
+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;
+  int result;
+  int ty;
+
+  if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
+  {
+    max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
+    font_nr = FONT_TEXT_1;
+  }
+
+  if (game_status == GAME_MODE_PLAYING)
+  {
+#if 1
+    BlitScreenToBitmap(backbuffer);
+#else
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      BlitScreenToBitmap_EM(backbuffer);
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      BlitScreenToBitmap_SP(backbuffer);
+#endif
+  }
+
+  /* disable deactivated drawing when quick-loading level tape recording */
+  if (tape.playing && tape.deactivate_display)
+    TapeDeactivateDisplayOff(TRUE);
+
+  SetMouseCursor(CURSOR_DEFAULT);
+
+#if defined(NETWORK_AVALIABLE)
+  /* pause network game while waiting for request to answer */
+  if (options.network &&
+      game_status == GAME_MODE_PLAYING &&
+      req_state & REQUEST_WAIT_FOR_INPUT)
+    SendToServer_PausePlaying();
+#endif
+
+  old_door_state = GetDoorState();
+
+  /* simulate releasing mouse button over last gadget, if still pressed */
+  if (button_status)
+    HandleGadgets(-1, -1, 0);
+
+  UnmapAllGadgets();
+
+  /* draw released gadget before proceeding */
+  // BackToFront();
+
+  if (old_door_state & DOOR_OPEN_1)
+  {
+    CloseDoor(DOOR_CLOSE_1);
+
+    /* save old door content */
+#if 1
+    BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
+              0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
+#else
+    BlitBitmap(bitmap_db_door, bitmap_db_door,
+              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
+              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
+#endif
+  }
+
+  SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
+  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
+
+  /* clear door drawing field */
+  DrawBackground(DX, DY, DXSIZE, DYSIZE);
+
+  /* force DOOR font inside door area */
+  game_status = GAME_MODE_PSEUDO_DOOR;
+
+  /* write text for request */
+  for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
+  {
+    char text_line[max_request_line_len + 1];
+    int tx, tl, tc = 0;
 
     if (!*text_ptr)
       break;
@@ -3981,10 +4814,14 @@ boolean Request(char *text, unsigned int req_state)
     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
     {
       tc = *(text_ptr + tx);
-      if (!tc || tc == ' ')
+      // if (!tc || tc == ' ')
+      if (!tc || tc == ' ' || tc == '?' || tc == '!')
        break;
     }
 
+    if ((tc == '?' || tc == '!') && tl == 0)
+      tl = 1;
+
     if (!tl)
     { 
       text_ptr++; 
@@ -4000,19 +4837,11 @@ boolean Request(char *text, unsigned int req_state)
             text_line, font_nr);
 
     text_ptr += tl + (tc == ' ' ? 1 : 0);
+    // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
   }
 
   game_status = last_game_status;      /* restore current game status */
 
-#if 1
-  if (global.use_envelope_request)
-  {
-    /* !!! TMP !!! */
-    FreeToolButtons();
-    CreateToolButtons();
-  }
-#endif
-
   if (req_state & REQ_ASK)
   {
     MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
@@ -4031,40 +4860,15 @@ boolean Request(char *text, unsigned int req_state)
   }
 
   /* copy request gadgets to door backbuffer */
+#if 1
+  BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
+#else
   BlitBitmap(drawto, bitmap_db_door,
             DX, DY, DXSIZE, DYSIZE,
             DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
-
-#if 1
-  if (global.use_envelope_request)
-  {
-    ShowEnvelopeDoor(text, ACTION_OPENING);
-
-    for (i = 0; i < NUM_TOOL_BUTTONS; i++)
-    {
-      if ((req_state & REQ_ASK && (i == TOOL_CTRL_ID_YES ||
-                                  i == TOOL_CTRL_ID_NO)) ||
-         (req_state & REQ_CONFIRM && i == TOOL_CTRL_ID_CONFIRM) ||
-         (req_state & REQ_PLAYER && (i == TOOL_CTRL_ID_PLAYER_1 &&
-                                     i == TOOL_CTRL_ID_PLAYER_2 &&
-                                     i == TOOL_CTRL_ID_PLAYER_3 &&
-                                     i == TOOL_CTRL_ID_PLAYER_4)))
-      {
-       int x = tool_gadget[i]->x + dDX;
-       int y = tool_gadget[i]->y + dDY;
-
-       ModifyGadget(tool_gadget[i], GDI_X, x, GDI_Y, y, GDI_END);
-      }
-    }
-  }
 #endif
 
-#if 1
-  if (!global.use_envelope_request)
-    OpenDoor(DOOR_OPEN_1);
-#else
   OpenDoor(DOOR_OPEN_1);
-#endif
 
   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
   {
@@ -4081,276 +4885,1431 @@ boolean Request(char *text, unsigned int req_state)
     return FALSE;
   }
 
-#if 1
-  if (game_status != GAME_MODE_MAIN && !global.use_envelope_request)
-    InitAnimation();
-#else
   if (game_status != GAME_MODE_MAIN)
     InitAnimation();
-#endif
 
-  button_status = MB_RELEASED;
+  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
 
-  request_gadget_id = -1;
+  // ---------- handle request buttons ----------
+  result = RequestHandleEvents(req_state);
 
-  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
+  if (game_status != GAME_MODE_MAIN)
+    StopAnimation();
 
-  while (result < 0)
-  {
-    if (PendingEvent())
-    {
-      Event event;
+  UnmapToolButtons();
 
-      NextEvent(&event);
+  if (!(req_state & REQ_STAY_OPEN))
+  {
+    CloseDoor(DOOR_CLOSE_1);
 
-      switch (event.type)
-      {
-       case EVENT_BUTTONPRESS:
-       case EVENT_BUTTONRELEASE:
-       case EVENT_MOTIONNOTIFY:
-       {
-         if (event.type == EVENT_MOTIONNOTIFY)
-         {
-           if (!PointerInWindow(window))
-             continue; /* window and pointer are on different screens */
+    if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
+       (req_state & REQ_REOPEN))
+      OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+  }
 
-           if (!button_status)
-             continue;
+  RemapAllGadgets();
 
-           motion_status = TRUE;
-           mx = ((MotionEvent *) &event)->x;
-           my = ((MotionEvent *) &event)->y;
-         }
-         else
-         {
-           motion_status = FALSE;
-           mx = ((ButtonEvent *) &event)->x;
-           my = ((ButtonEvent *) &event)->y;
-           if (event.type == EVENT_BUTTONPRESS)
-             button_status = ((ButtonEvent *) &event)->button;
-           else
-             button_status = MB_RELEASED;
-         }
+  if (game_status == GAME_MODE_PLAYING)
+  {
+    SetPanelBackground();
+    SetDrawBackgroundMask(REDRAW_DOOR_1);
+  }
+  else
+  {
+    SetDrawBackgroundMask(REDRAW_FIELD);
+  }
 
-         /* this sets 'request_gadget_id' */
-         HandleGadgets(mx, my, button_status);
+#if defined(NETWORK_AVALIABLE)
+  /* continue network game after request */
+  if (options.network &&
+      game_status == GAME_MODE_PLAYING &&
+      req_state & REQUEST_WAIT_FOR_INPUT)
+    SendToServer_ContinuePlaying();
+#endif
 
-         switch (request_gadget_id)
-         {
-           case TOOL_CTRL_ID_YES:
-             result = TRUE;
-             break;
-           case TOOL_CTRL_ID_NO:
-             result = FALSE;
-             break;
-           case TOOL_CTRL_ID_CONFIRM:
-             result = TRUE | FALSE;
-             break;
+  /* restore deactivated drawing when quick-loading level tape recording */
+  if (tape.playing && tape.deactivate_display)
+    TapeDeactivateDisplayOn();
 
-           case TOOL_CTRL_ID_PLAYER_1:
-             result = 1;
-             break;
-           case TOOL_CTRL_ID_PLAYER_2:
-             result = 2;
-             break;
-           case TOOL_CTRL_ID_PLAYER_3:
-             result = 3;
-             break;
-           case TOOL_CTRL_ID_PLAYER_4:
-             result = 4;
-             break;
+  return result;
+}
+
+static boolean RequestEnvelope(char *text, unsigned int req_state)
+{
+  int result;
+#if 0
+  int i;
+#endif
+
+  if (game_status == GAME_MODE_PLAYING)
+  {
+#if 1
+#if 1
+    BlitScreenToBitmap(backbuffer);
+#else
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      BlitScreenToBitmap_EM(backbuffer);
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      BlitScreenToBitmap_SP(backbuffer);
+    else
+      BlitScreenToBitmap_RND(backbuffer);
+#endif
+#else
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      BlitScreenToBitmap_EM(backbuffer);
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      BlitScreenToBitmap_SP(backbuffer);
+#endif
+  }
+
+  /* disable deactivated drawing when quick-loading level tape recording */
+  if (tape.playing && tape.deactivate_display)
+    TapeDeactivateDisplayOff(TRUE);
+
+  SetMouseCursor(CURSOR_DEFAULT);
+
+#if defined(NETWORK_AVALIABLE)
+  /* pause network game while waiting for request to answer */
+  if (options.network &&
+      game_status == GAME_MODE_PLAYING &&
+      req_state & REQUEST_WAIT_FOR_INPUT)
+    SendToServer_PausePlaying();
+#endif
+
+  /* simulate releasing mouse button over last gadget, if still pressed */
+  if (button_status)
+    HandleGadgets(-1, -1, 0);
+
+  UnmapAllGadgets();
+
+  // (replace with setting corresponding request background)
+  // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
+  // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
+
+  /* clear door drawing field */
+  // DrawBackground(DX, DY, DXSIZE, DYSIZE);
+
+#if 0
+  if (global.use_envelope_request)
+  {
+    /* !!! TMP !!! */
+    FreeToolButtons();
+    CreateToolButtons();
+  }
+#endif
+
+#if 0
+#if 0
+  if (req_state & REQ_ASK)
+  {
+    MapGadgetExt(tool_gadget[TOOL_CTRL_ID_YES], FALSE);
+    MapGadgetExt(tool_gadget[TOOL_CTRL_ID_NO], FALSE);
+  }
+  else if (req_state & REQ_CONFIRM)
+  {
+    MapGadgetExt(tool_gadget[TOOL_CTRL_ID_CONFIRM], FALSE);
+  }
+  else if (req_state & REQ_PLAYER)
+  {
+    MapGadgetExt(tool_gadget[TOOL_CTRL_ID_PLAYER_1], FALSE);
+    MapGadgetExt(tool_gadget[TOOL_CTRL_ID_PLAYER_2], FALSE);
+    MapGadgetExt(tool_gadget[TOOL_CTRL_ID_PLAYER_3], FALSE);
+    MapGadgetExt(tool_gadget[TOOL_CTRL_ID_PLAYER_4], FALSE);
+  }
+#else
+  if (req_state & REQ_ASK)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
+  }
+  else if (req_state & REQ_CONFIRM)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
+  }
+  else if (req_state & REQ_PLAYER)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
+  }
+#endif
+#endif
+
+  ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
+
+#if 0
+  for (i = 0; i < NUM_TOOL_BUTTONS; i++)
+  {
+    if ((req_state & REQ_ASK && (i == TOOL_CTRL_ID_YES ||
+                                i == TOOL_CTRL_ID_NO)) ||
+       (req_state & REQ_CONFIRM && i == TOOL_CTRL_ID_CONFIRM) ||
+       (req_state & REQ_PLAYER && (i == TOOL_CTRL_ID_PLAYER_1 &&
+                                   i == TOOL_CTRL_ID_PLAYER_2 &&
+                                   i == TOOL_CTRL_ID_PLAYER_3 &&
+                                   i == TOOL_CTRL_ID_PLAYER_4)))
+    {
+      int x = tool_gadget[i]->x + dDX;
+      int y = tool_gadget[i]->y + dDY;
+
+      ModifyGadget(tool_gadget[i], GDI_X, x, GDI_Y, y, GDI_END);
+    }
+  }
+#endif
+
+  if (!(req_state & REQUEST_WAIT_FOR_INPUT))
+  {
+    if (game_status == GAME_MODE_PLAYING)
+    {
+      SetPanelBackground();
+      SetDrawBackgroundMask(REDRAW_DOOR_1);
+    }
+    else
+    {
+      SetDrawBackgroundMask(REDRAW_FIELD);
+    }
+
+    return FALSE;
+  }
+
+#if 0
+  if (game_status != GAME_MODE_MAIN)
+    InitAnimation();
+#endif
+
+  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
+
+  // ---------- handle request buttons ----------
+  result = RequestHandleEvents(req_state);
+
+  if (game_status != GAME_MODE_MAIN)
+    StopAnimation();
+
+  UnmapToolButtons();
+
+  ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
+
+  RemapAllGadgets();
+
+  if (game_status == GAME_MODE_PLAYING)
+  {
+    SetPanelBackground();
+    SetDrawBackgroundMask(REDRAW_DOOR_1);
+  }
+  else
+  {
+    SetDrawBackgroundMask(REDRAW_FIELD);
+  }
+
+#if defined(NETWORK_AVALIABLE)
+  /* continue network game after request */
+  if (options.network &&
+      game_status == GAME_MODE_PLAYING &&
+      req_state & REQUEST_WAIT_FOR_INPUT)
+    SendToServer_ContinuePlaying();
+#endif
+
+  /* restore deactivated drawing when quick-loading level tape recording */
+  if (tape.playing && tape.deactivate_display)
+    TapeDeactivateDisplayOn();
+
+  return result;
+}
+
+boolean Request(char *text, unsigned int req_state)
+{
+  if (global.use_envelope_request)
+    return RequestEnvelope(text, req_state);
+  else
+    return RequestDoor(text, req_state);
+}
+
+#else  // =====================================================================
+
+boolean Request(char *text, unsigned int req_state)
+{
+  int mx, my, ty, result = -1;
+  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;
+#if 0
+  int max_word_len = 0;
+#endif
+  char *text_ptr;
+  int i;
+
+#if 0
+  global.use_envelope_request = 1;
+#endif
+
+#if 1
+  if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
+  {
+    max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
+    font_nr = FONT_TEXT_1;
+  }
+#else
+  for (text_ptr = text; *text_ptr; text_ptr++)
+  {
+    max_word_len = (*text_ptr != ' ' ? max_word_len + 1 : 0);
+
+    if (max_word_len > MAX_REQUEST_LINE_FONT1_LEN)
+    {
+      max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
+#if 1
+      font_nr = FONT_TEXT_1;
+#else
+      font_nr = FONT_LEVEL_NUMBER;
+#endif
+
+      break;
+    }
+  }
+#endif
+
+  if (game_status == GAME_MODE_PLAYING)
+  {
+#if 1
+    BlitScreenToBitmap(backbuffer);
+#else
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      BlitScreenToBitmap_EM(backbuffer);
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      BlitScreenToBitmap_SP(backbuffer);
+#endif
+  }
+
+  /* disable deactivated drawing when quick-loading level tape recording */
+  if (tape.playing && tape.deactivate_display)
+    TapeDeactivateDisplayOff(TRUE);
+
+  SetMouseCursor(CURSOR_DEFAULT);
+
+#if defined(NETWORK_AVALIABLE)
+  /* pause network game while waiting for request to answer */
+  if (options.network &&
+      game_status == GAME_MODE_PLAYING &&
+      req_state & REQUEST_WAIT_FOR_INPUT)
+    SendToServer_PausePlaying();
+#endif
+
+  old_door_state = GetDoorState();
+
+  /* simulate releasing mouse button over last gadget, if still pressed */
+  if (button_status)
+    HandleGadgets(-1, -1, 0);
+
+  UnmapAllGadgets();
+
+  /* draw released gadget before proceeding */
+  // BackToFront();
+
+#if 0
+  if (old_door_state & DOOR_OPEN_1 && !global.use_envelope_request)
+#else
+  if (old_door_state & DOOR_OPEN_1)
+#endif
+  {
+#if 1
+    if (!global.use_envelope_request)
+      CloseDoor(DOOR_CLOSE_1);
+#else
+    CloseDoor(DOOR_CLOSE_1);
+#endif
+
+    /* save old door content */
+    BlitBitmap(bitmap_db_door, bitmap_db_door,
+              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
+              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
+  }
+
+#if 1
+  SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
+#endif
+
+  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
+
+  /* clear door drawing field */
+  DrawBackground(DX, DY, DXSIZE, DYSIZE);
+
+  /* force DOOR font inside door area */
+  game_status = GAME_MODE_PSEUDO_DOOR;
+
+  /* write text for request */
+  for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
+  {
+    char text_line[max_request_line_len + 1];
+    int tx, tl, tc = 0;
+
+    if (!*text_ptr)
+      break;
+
+    for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
+    {
+      tc = *(text_ptr + tx);
+      if (!tc || tc == ' ')
+       break;
+    }
+
+    if (!tl)
+    { 
+      text_ptr++; 
+      ty--; 
+      continue; 
+    }
+
+    strncpy(text_line, text_ptr, tl);
+    text_line[tl] = 0;
+
+    DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
+            DY + 8 + ty * (getFontHeight(font_nr) + 2),
+            text_line, font_nr);
+
+    text_ptr += tl + (tc == ' ' ? 1 : 0);
+  }
+
+  game_status = last_game_status;      /* restore current game status */
+
+#if 1
+  if (global.use_envelope_request)
+  {
+    /* !!! TMP !!! */
+    FreeToolButtons();
+    CreateToolButtons();
+  }
+#endif
+
+  if (req_state & REQ_ASK)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
+  }
+  else if (req_state & REQ_CONFIRM)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
+  }
+  else if (req_state & REQ_PLAYER)
+  {
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
+    MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
+  }
+
+  /* copy request gadgets to door backbuffer */
+  BlitBitmap(drawto, bitmap_db_door,
+            DX, DY, DXSIZE, DYSIZE,
+            DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+
+#if 1
+  if (global.use_envelope_request)
+  {
+    ShowEnvelopeRequest(text, ACTION_OPENING);
+
+    for (i = 0; i < NUM_TOOL_BUTTONS; i++)
+    {
+      if ((req_state & REQ_ASK && (i == TOOL_CTRL_ID_YES ||
+                                  i == TOOL_CTRL_ID_NO)) ||
+         (req_state & REQ_CONFIRM && i == TOOL_CTRL_ID_CONFIRM) ||
+         (req_state & REQ_PLAYER && (i == TOOL_CTRL_ID_PLAYER_1 &&
+                                     i == TOOL_CTRL_ID_PLAYER_2 &&
+                                     i == TOOL_CTRL_ID_PLAYER_3 &&
+                                     i == TOOL_CTRL_ID_PLAYER_4)))
+      {
+       int x = tool_gadget[i]->x + dDX;
+       int y = tool_gadget[i]->y + dDY;
+
+       ModifyGadget(tool_gadget[i], GDI_X, x, GDI_Y, y, GDI_END);
+      }
+    }
+  }
+#endif
+
+#if 1
+  if (!global.use_envelope_request)
+    OpenDoor(DOOR_OPEN_1);
+#else
+  OpenDoor(DOOR_OPEN_1);
+#endif
+
+  if (!(req_state & REQUEST_WAIT_FOR_INPUT))
+  {
+    if (game_status == GAME_MODE_PLAYING)
+    {
+      SetPanelBackground();
+      SetDrawBackgroundMask(REDRAW_DOOR_1);
+    }
+    else
+    {
+      SetDrawBackgroundMask(REDRAW_FIELD);
+    }
+
+    return FALSE;
+  }
+
+#if 1
+  if (game_status != GAME_MODE_MAIN && !global.use_envelope_request)
+    InitAnimation();
+#else
+  if (game_status != GAME_MODE_MAIN)
+    InitAnimation();
+#endif
+
+  button_status = MB_RELEASED;
+
+  request_gadget_id = -1;
+
+  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
+
+  while (result < 0)
+  {
+    if (PendingEvent())
+    {
+      Event event;
+
+      NextEvent(&event);
+
+      switch (event.type)
+      {
+       case EVENT_BUTTONPRESS:
+       case EVENT_BUTTONRELEASE:
+       case EVENT_MOTIONNOTIFY:
+       {
+         if (event.type == EVENT_MOTIONNOTIFY)
+         {
+           if (!PointerInWindow(window))
+             continue; /* window and pointer are on different screens */
+
+           if (!button_status)
+             continue;
+
+           motion_status = TRUE;
+           mx = ((MotionEvent *) &event)->x;
+           my = ((MotionEvent *) &event)->y;
+         }
+         else
+         {
+           motion_status = FALSE;
+           mx = ((ButtonEvent *) &event)->x;
+           my = ((ButtonEvent *) &event)->y;
+           if (event.type == EVENT_BUTTONPRESS)
+             button_status = ((ButtonEvent *) &event)->button;
+           else
+             button_status = MB_RELEASED;
+         }
+
+         /* this sets 'request_gadget_id' */
+         HandleGadgets(mx, my, button_status);
+
+         switch (request_gadget_id)
+         {
+           case TOOL_CTRL_ID_YES:
+             result = TRUE;
+             break;
+           case TOOL_CTRL_ID_NO:
+             result = FALSE;
+             break;
+           case TOOL_CTRL_ID_CONFIRM:
+             result = TRUE | FALSE;
+             break;
+
+           case TOOL_CTRL_ID_PLAYER_1:
+             result = 1;
+             break;
+           case TOOL_CTRL_ID_PLAYER_2:
+             result = 2;
+             break;
+           case TOOL_CTRL_ID_PLAYER_3:
+             result = 3;
+             break;
+           case TOOL_CTRL_ID_PLAYER_4:
+             result = 4;
+             break;
+
+           default:
+             break;
+         }
+
+         break;
+       }
+
+       case EVENT_KEYPRESS:
+         switch (GetEventKey((KeyEvent *)&event, TRUE))
+         {
+           case KSYM_space:
+             if (req_state & REQ_CONFIRM)
+               result = 1;
+             break;
+
+           case KSYM_Return:
+             result = 1;
+             break;
+
+           case KSYM_Escape:
+#if defined(TARGET_SDL2)
+           case KSYM_Back:
+#endif
+             result = 0;
+             break;
 
            default:
              break;
          }
 
-         break;
-       }
+         if (req_state & REQ_PLAYER)
+           result = 0;
+         break;
+
+       case EVENT_KEYRELEASE:
+         ClearPlayerAction();
+         break;
+
+       default:
+         HandleOtherEvents(&event);
+         break;
+      }
+    }
+    else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
+    {
+      int joy = AnyJoystick();
+
+      if (joy & JOY_BUTTON_1)
+       result = 1;
+      else if (joy & JOY_BUTTON_2)
+       result = 0;
+    }
+
+#if 1
+
+    if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
+    {
+      HandleGameActions();
+    }
+    else
+    {
+      DoAnimation();
+
+      if (!PendingEvent())     /* delay only if no pending events */
+       Delay(10);
+    }
+
+#if 1
+    game_status = GAME_MODE_PSEUDO_DOOR;
+#endif
+
+    BackToFront();
+
+#if 1
+    game_status = last_game_status;    /* restore current game status */
+#endif
+
+#else
+
+    DoAnimation();
+
+#if 1
+    if (!PendingEvent())       /* delay only if no pending events */
+      Delay(10);
+#else
+    /* don't eat all CPU time */
+    Delay(10);
+#endif
+
+#endif
+  }
+
+  if (game_status != GAME_MODE_MAIN)
+    StopAnimation();
+
+  UnmapToolButtons();
+
+#if 1
+  if (global.use_envelope_request)
+    ShowEnvelopeRequest(text, ACTION_CLOSING);
+#endif
+
+#if 1
+  if (!(req_state & REQ_STAY_OPEN) && !global.use_envelope_request)
+#else
+  if (!(req_state & REQ_STAY_OPEN))
+#endif
+  {
+    CloseDoor(DOOR_CLOSE_1);
+
+    if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
+       (req_state & REQ_REOPEN))
+      OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+  }
+
+  RemapAllGadgets();
+
+  if (game_status == GAME_MODE_PLAYING)
+  {
+    SetPanelBackground();
+    SetDrawBackgroundMask(REDRAW_DOOR_1);
+  }
+  else
+  {
+    SetDrawBackgroundMask(REDRAW_FIELD);
+  }
+
+#if defined(NETWORK_AVALIABLE)
+  /* continue network game after request */
+  if (options.network &&
+      game_status == GAME_MODE_PLAYING &&
+      req_state & REQUEST_WAIT_FOR_INPUT)
+    SendToServer_ContinuePlaying();
+#endif
+
+  /* restore deactivated drawing when quick-loading level tape recording */
+  if (tape.playing && tape.deactivate_display)
+    TapeDeactivateDisplayOn();
+
+  return result;
+}
+
+#endif
+
+static int compareDoorPartOrderInfo(const void *object1, const void *object2)
+{
+  const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
+  const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
+  int compare_result;
+
+  if (dpo1->sort_priority != dpo2->sort_priority)
+    compare_result = dpo1->sort_priority - dpo2->sort_priority;
+  else
+    compare_result = dpo1->nr - dpo2->nr;
+
+  return compare_result;
+}
+
+void InitGraphicCompatibilityInfo_Doors()
+{
+  struct
+  {
+    int door_token;
+    int part_1, part_8;
+    struct DoorInfo *door;
+  }
+  doors[] =
+  {
+    { DOOR_1,  IMG_DOOR_1_GFX_PART_1,  IMG_DOOR_1_GFX_PART_8,  &door_1 },
+    { DOOR_2,  IMG_DOOR_2_GFX_PART_1,  IMG_DOOR_2_GFX_PART_8,  &door_2 },
+
+    { -1,      -1,                     -1,                     NULL    }
+  };
+  struct Rect door_rect_list[] =
+  {
+    { DX, DY, DXSIZE, DYSIZE },
+    { VX, VY, VXSIZE, VYSIZE }
+  };
+  int i, j;
+
+  for (i = 0; doors[i].door_token != -1; i++)
+  {
+    int door_token = doors[i].door_token;
+    int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
+    int part_1 = doors[i].part_1;
+    int part_8 = doors[i].part_8;
+    int part_2 = part_1 + 1;
+    int part_3 = part_1 + 2;
+    struct DoorInfo *door = doors[i].door;
+    struct Rect *door_rect = &door_rect_list[door_index];
+    boolean door_gfx_redefined = FALSE;
+
+    /* check if any door part graphic definitions have been redefined */
+
+    for (j = 0; door_part_controls[j].door_token != -1; j++)
+    {
+      struct DoorPartControlInfo *dpc = &door_part_controls[j];
+      struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
+
+      if (dpc->door_token == door_token && fi->redefined)
+       door_gfx_redefined = TRUE;
+    }
+
+    /* check for old-style door graphic/animation modifications */
+
+    if (!door_gfx_redefined)
+    {
+      if (door->anim_mode & ANIM_STATIC_PANEL)
+      {
+       door->panel.step_xoffset = 0;
+       door->panel.step_yoffset = 0;
+      }
+
+      if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
+      {
+       struct GraphicInfo *g_part_1 = &graphic_info[part_1];
+       struct GraphicInfo *g_part_2 = &graphic_info[part_2];
+       int num_door_steps, num_panel_steps;
+
+       /* remove door part graphics other than the two default wings */
+
+       for (j = 0; door_part_controls[j].door_token != -1; j++)
+       {
+         struct DoorPartControlInfo *dpc = &door_part_controls[j];
+         struct GraphicInfo *g = &graphic_info[dpc->graphic];
+
+         if (dpc->graphic >= part_3 &&
+             dpc->graphic <= part_8)
+           g->bitmap = NULL;
+       }
+
+       /* set graphics and screen positions of the default wings */
+
+       g_part_1->width  = door_rect->width;
+       g_part_1->height = door_rect->height;
+       g_part_2->width  = door_rect->width;
+       g_part_2->height = door_rect->height;
+       g_part_2->src_x = door_rect->width;
+       g_part_2->src_y = g_part_1->src_y;
+
+       door->part_2.x = door->part_1.x;
+       door->part_2.y = door->part_1.y;
+
+       if (door->width != -1)
+       {
+         g_part_1->width = door->width;
+         g_part_2->width = door->width;
+
+         // special treatment for graphics and screen position of right wing
+         g_part_2->src_x += door_rect->width - door->width;
+         door->part_2.x  += door_rect->width - door->width;
+       }
+
+       if (door->height != -1)
+       {
+         g_part_1->height = door->height;
+         g_part_2->height = door->height;
+
+         // special treatment for graphics and screen position of bottom wing
+         g_part_2->src_y += door_rect->height - door->height;
+         door->part_2.y  += door_rect->height - door->height;
+       }
+
+       /* set animation delays for the default wings and panels */
+
+       door->part_1.step_delay = door->step_delay;
+       door->part_2.step_delay = door->step_delay;
+       door->panel.step_delay  = door->step_delay;
+
+       /* set animation draw order for the default wings */
+
+       door->part_1.sort_priority = 2; /* draw left wing over ... */
+       door->part_2.sort_priority = 1; /*          ... right wing */
+
+       /* set animation draw offset for the default wings */
+
+       if (door->anim_mode & ANIM_HORIZONTAL)
+       {
+         door->part_1.step_xoffset = door->step_offset;
+         door->part_1.step_yoffset = 0;
+         door->part_2.step_xoffset = door->step_offset * -1;
+         door->part_2.step_yoffset = 0;
+
+         num_door_steps = g_part_1->width / door->step_offset;
+       }
+       else    // ANIM_VERTICAL
+       {
+         door->part_1.step_xoffset = 0;
+         door->part_1.step_yoffset = door->step_offset;
+         door->part_2.step_xoffset = 0;
+         door->part_2.step_yoffset = door->step_offset * -1;
+
+         num_door_steps = g_part_1->height / door->step_offset;
+       }
+
+       /* set animation draw offset for the default panels */
+
+       if (door->step_offset > 1)
+       {
+         num_panel_steps = 2 * door_rect->height / door->step_offset;
+         door->panel.start_step = num_panel_steps - num_door_steps;
+       }
+       else
+       {
+         num_panel_steps = door_rect->height / door->step_offset;
+         door->panel.start_step = num_panel_steps - num_door_steps / 2;
+         door->panel.step_delay *= 2;
+       }
+      }
+    }
+  }
+}
+
+void InitDoors()
+{
+  int i;
+
+  for (i = 0; door_part_controls[i].door_token != -1; i++)
+  {
+    struct DoorPartControlInfo *dpc = &door_part_controls[i];
+    struct DoorPartOrderInfo *dpo = &door_part_order[i];
+
+    /* initialize "start_step_opening" and "start_step_closing", if needed */
+    if (dpc->pos->start_step_opening == 0 &&
+       dpc->pos->start_step_closing == 0)
+    {
+      // dpc->pos->start_step_opening = dpc->pos->start_step;
+      dpc->pos->start_step_closing = dpc->pos->start_step;
+    }
+
+    /* fill structure for door part draw order (sorted below) */
+    dpo->nr = i;
+    dpo->sort_priority = dpc->pos->sort_priority;
+
+#if 0
+    struct DoorPartPosInfo *pos = dpc->pos;
+
+    printf(":0: step_xoffset == %d, step_yoffset == %d\n",
+          pos->step_xoffset, pos->step_yoffset);
+#endif
+  }
+
+  /* sort door part controls according to sort_priority and graphic number */
+  qsort(door_part_order, MAX_DOOR_PARTS,
+        sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
+}
+
+unsigned int OpenDoor(unsigned int door_state)
+{
+  if (door_state & DOOR_COPY_BACK)
+  {
+#if 1
+    if (door_state & DOOR_OPEN_1)
+      BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
+                1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
+
+    if (door_state & DOOR_OPEN_2)
+      BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
+                1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
+#else
+    if (door_state & DOOR_OPEN_1)
+      BlitBitmap(bitmap_db_door, bitmap_db_door,
+                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
+                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+
+    if (door_state & DOOR_OPEN_2)
+      BlitBitmap(bitmap_db_door, bitmap_db_door,
+                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY2, VXSIZE, VYSIZE,
+                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
+#endif
+
+    door_state &= ~DOOR_COPY_BACK;
+  }
+
+  return MoveDoor(door_state);
+}
+
+unsigned int CloseDoor(unsigned int door_state)
+{
+  unsigned int old_door_state = GetDoorState();
+
+  if (!(door_state & DOOR_NO_COPY_BACK))
+  {
+#if 1
+    if (old_door_state & DOOR_OPEN_1)
+      BlitBitmap(backbuffer, bitmap_db_door_1,
+                DX, DY, DXSIZE, DYSIZE, 0, 0);
 
-       case EVENT_KEYPRESS:
-         switch (GetEventKey((KeyEvent *)&event, TRUE))
-         {
-           case KSYM_space:
-             if (req_state & REQ_CONFIRM)
-               result = 1;
-             break;
+    if (old_door_state & DOOR_OPEN_2)
+      BlitBitmap(backbuffer, bitmap_db_door_2,
+                VX, VY, VXSIZE, VYSIZE, 0, 0);
+#else
+    if (old_door_state & DOOR_OPEN_1)
+      BlitBitmap(backbuffer, bitmap_db_door,
+                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
 
-           case KSYM_Return:
-             result = 1;
-             break;
+    if (old_door_state & DOOR_OPEN_2)
+      BlitBitmap(backbuffer, bitmap_db_door,
+                VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
+#endif
 
-           case KSYM_Escape:
-#if defined(TARGET_SDL2)
-           case KSYM_Back:
+    door_state &= ~DOOR_NO_COPY_BACK;
+  }
+
+  return MoveDoor(door_state);
+}
+
+unsigned int GetDoorState()
+{
+  return MoveDoor(DOOR_GET_STATE);
+}
+
+unsigned int SetDoorState(unsigned int door_state)
+{
+  return MoveDoor(door_state | DOOR_SET_STATE);
+}
+
+#if 1
+
+// ========== TEST 1 ===========================================================
+
+int euclid(int a, int b)
+{
+  return (b ? euclid(b, a % b) : a);
+}
+
+unsigned int MoveDoor(unsigned int door_state)
+{
+#if 0
+  struct XY panel_pos_list[] =
+  {
+    { DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 },
+    { DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2 },
+  };
 #endif
-             result = 0;
-             break;
+  struct Rect door_rect_list[] =
+  {
+    { DX, DY, DXSIZE, DYSIZE },
+    { VX, VY, VXSIZE, VYSIZE }
+  };
+  static int door1 = DOOR_OPEN_1;
+  static int door2 = DOOR_CLOSE_2;
+  unsigned int door_delay = 0;
+  unsigned int door_delay_value;
+  int i;
 
-           default:
-             break;
-         }
+#if 1
+  if (door_1.width < 0 || door_1.width > DXSIZE)
+    door_1.width = DXSIZE;
+  if (door_1.height < 0 || door_1.height > DYSIZE)
+    door_1.height = DYSIZE;
+  if (door_2.width < 0 || door_2.width > VXSIZE)
+    door_2.width = VXSIZE;
+  if (door_2.height < 0 || door_2.height > VYSIZE)
+    door_2.height = VYSIZE;
+#endif
 
-         if (req_state & REQ_PLAYER)
-           result = 0;
-         break;
+  if (door_state == DOOR_GET_STATE)
+    return (door1 | door2);
 
-       case EVENT_KEYRELEASE:
-         ClearPlayerAction();
-         break;
+  if (door_state & DOOR_SET_STATE)
+  {
+    if (door_state & DOOR_ACTION_1)
+      door1 = door_state & DOOR_ACTION_1;
+    if (door_state & DOOR_ACTION_2)
+      door2 = door_state & DOOR_ACTION_2;
 
-       default:
-         HandleOtherEvents(&event);
-         break;
-      }
-    }
-    else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
-    {
-      int joy = AnyJoystick();
+    return (door1 | door2);
+  }
 
-      if (joy & JOY_BUTTON_1)
-       result = 1;
-      else if (joy & JOY_BUTTON_2)
-       result = 0;
-    }
+  if (!(door_state & DOOR_FORCE_REDRAW))
+  {
+    if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
+      door_state &= ~DOOR_OPEN_1;
+    else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
+      door_state &= ~DOOR_CLOSE_1;
+    if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
+      door_state &= ~DOOR_OPEN_2;
+    else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
+      door_state &= ~DOOR_CLOSE_2;
+  }
+
+#if 0
+  door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
+                     door_2.step_delay);
+
+  if (setup.quick_doors)
+  {
+    stepsize = 20;             /* must be chosen to always draw last frame */
+    door_delay_value = 0;
+  }
+#endif
+
+  if (global.autoplay_leveldir)
+  {
+    door_state |= DOOR_NO_DELAY;
+    door_state &= ~DOOR_CLOSE_ALL;
+  }
 
 #if 1
+  if (game_status == GAME_MODE_EDITOR)
+    door_state |= DOOR_NO_DELAY;
+#endif
 
-    if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
+  if (door_state & DOOR_ACTION)
+  {
+    boolean door_panel_drawn[NUM_DOORS];
+    boolean panel_has_doors[NUM_DOORS];
+    boolean door_part_skip[MAX_DOOR_PARTS];
+    boolean door_part_done[MAX_DOOR_PARTS];
+    boolean door_part_done_all;
+    int num_steps[MAX_DOOR_PARTS];
+    int max_move_delay = 0;    // delay for complete animations of all doors
+    int max_step_delay = 0;    // delay (ms) between two animation frames
+    int num_move_steps = 0;    // number of animation steps for all doors
+    int current_move_delay = 0;
+    int k;
+
+    for (i = 0; i < NUM_DOORS; i++)
+      panel_has_doors[i] = FALSE;
+
+    for (i = 0; i < MAX_DOOR_PARTS; i++)
     {
-      HandleGameActions();
+      struct DoorPartControlInfo *dpc = &door_part_controls[i];
+      struct GraphicInfo *g = &graphic_info[dpc->graphic];
+      int door_token = dpc->door_token;
+
+      door_part_done[i] = FALSE;
+      door_part_skip[i] = (!(door_state & door_token) ||
+                          !g->bitmap);
     }
-    else
+
+#if 0
+    for (i = 0; i < MAX_DOOR_PARTS; i++)
     {
-      DoAnimation();
+      struct DoorPartControlInfo *dpc = &door_part_controls[i];
+      struct DoorPartPosInfo *pos = dpc->pos;
+      int start_step = pos->start_step;
 
-      if (!PendingEvent())     /* delay only if no pending events */
-       Delay(10);
+      printf("::: ---> %d: start_step == %d [%d]\n",
+            i, start_step, door_part_done[i]);
     }
+#endif
 
-#if 1
-    game_status = GAME_MODE_PSEUDO_DOOR;
+    for (i = 0; i < MAX_DOOR_PARTS; i++)
+    {
+      int nr = door_part_order[i].nr;
+      struct DoorPartControlInfo *dpc = &door_part_controls[nr];
+      struct DoorPartPosInfo *pos = dpc->pos;
+      struct GraphicInfo *g = &graphic_info[dpc->graphic];
+      int door_token = dpc->door_token;
+      int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
+      boolean is_panel = DOOR_PART_IS_PANEL(nr);
+      int step_xoffset = ABS(pos->step_xoffset);
+      int step_yoffset = ABS(pos->step_yoffset);
+      int step_delay = pos->step_delay;
+      int current_door_state = door_state & door_token;
+      boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
+      boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
+      boolean part_opening = (is_panel ? door_closing : door_opening);
+      int start_step = (part_opening ? pos->start_step_opening :
+                       pos->start_step_closing);
+      float move_xsize = (step_xoffset ? g->width  : 0);
+      float move_ysize = (step_yoffset ? g->height : 0);
+      int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
+      int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
+      int move_steps = (move_xsteps && move_ysteps ?
+                       MIN(move_xsteps, move_ysteps) :
+                       move_xsteps ? move_xsteps : move_ysteps) - start_step;
+      int move_delay = move_steps * step_delay;
+
+      if (door_part_skip[nr])
+       continue;
+
+      if (!is_panel)
+       panel_has_doors[door_index] = TRUE;
+
+      max_move_delay = MAX(max_move_delay, move_delay);
+      max_step_delay = (max_step_delay == 0 ? step_delay :
+                       euclid(max_step_delay, step_delay));
+      num_steps[nr] = move_steps;
+
+#if 0
+#if 0
+      printf("::: %d: move_delay == %d, start_step == %d [%d]\n",
+            i, move_delay, start_step, door_part_order[i].nr);
+#else
+      if (DOOR_PART_IS_PANEL(i))
+       printf("::: %d: move_delay == %d, start_step == %d\n",
+              i, move_delay, start_step);
 #endif
+#endif
+    }
 
-    BackToFront();
+    num_move_steps = max_move_delay / max_step_delay;
+
+    door_delay_value = max_step_delay;
+
+#if 0
+    door_delay_value *= 10;
+#endif
+
+#if 0
+    printf("::: num_move_steps == %d, max_move_delay == %d, max_step_delay == %d\n", num_move_steps, max_move_delay, max_step_delay);
+#endif
+
+    for (k = 0; k < num_move_steps; k++)
+    {
+      door_part_done_all = TRUE;
+
+      for (i = 0; i < NUM_DOORS; i++)
+       door_panel_drawn[i] = FALSE;
+
+      for (i = 0; i < MAX_DOOR_PARTS; i++)
+      {
+       int nr = door_part_order[i].nr;
+       struct DoorPartControlInfo *dpc = &door_part_controls[nr];
+       struct DoorPartPosInfo *pos = dpc->pos;
+       struct GraphicInfo *g = &graphic_info[dpc->graphic];
+       int door_token = dpc->door_token;
+       int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
+       boolean is_panel = DOOR_PART_IS_PANEL(nr);
+#if 0
+       struct XY *panel_pos = &panel_pos_list[door_index];
+#endif
+       struct Rect *door_rect = &door_rect_list[door_index];
+       Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
+                                 bitmap_db_door_2);
+       Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
+       int current_door_state = door_state & door_token;
+       boolean door_opening = ((current_door_state & DOOR_OPEN)  != 0);
+       boolean door_closing = !door_opening;
+       boolean part_opening = (is_panel ? door_closing : door_opening);
+       boolean part_closing = !part_opening;
+       int start_step = (part_opening ? pos->start_step_opening :
+                         pos->start_step_closing);
+       int step_delay = pos->step_delay;
+       int step_factor = step_delay / max_step_delay;
+       int k1 = (step_factor ? k / step_factor + 1 : k);
+       int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
+       int kk = (k2 < 0 ? 0 : k2);
+       int src_x, src_y, src_xx, src_yy;
+       int dst_x, dst_y, dst_xx, dst_yy;
+       int width, height;
+
+#if 0
+       if (k == 0 && is_panel && door_token == DOOR_2)
+         printf("::: %d, %d\n", g->width, g->height);
+#endif
+
+#if 0
+       if (DOOR_PART_IS_PANEL(nr))
+       {
+         int start_step = pos->start_step;
+
+         k2 = (door_closing ? k1 : num_steps[nr] - k1);// - start_step;
+         kk = (k2 < 0 ? 0 : k2);
+       }
+#endif
+
+#if 0
+       // !!! TEST !!! 
+       if (nr != 16 && nr != 0)
+         continue;
+#endif
+
+#if 0
+       // !!! TEST !!! 
+       if (!is_panel)
+         continue;
+#endif
 
 #if 1
-    game_status = last_game_status;    /* restore current game status */
+       if (door_part_skip[nr])
+         continue;
+#endif
+
+       if (!(door_state & door_token))
+         continue;
+
+       if (!g->bitmap)
+         continue;
+
+#if 0
+       if (current_move_delay % step_delay)
+         continue;
 #endif
 
+       // draw door panel
+
+       if (!door_panel_drawn[door_index])
+       {
+#if 1
+         ClearRectangle(drawto, door_rect->x, door_rect->y,
+                        door_rect->width, door_rect->height);
 #else
+         BlitBitmap(bitmap_db_door, drawto, panel_pos->x, panel_pos->y,
+                    door_rect->width, door_rect->height,
+                    door_rect->x, door_rect->y);
+#endif
 
-    DoAnimation();
+         door_panel_drawn[door_index] = TRUE;
+       }
+
+       // draw opening or closing door parts
+
+       if (pos->step_xoffset < 0)      // door part on right side
+       {
+         src_xx = 0;
+         dst_xx = pos->x + ABS(kk * pos->step_xoffset);
+         width = g->width;
+
+         if (dst_xx + width > door_rect->width)
+           width = door_rect->width - dst_xx;
+       }
+       else                            // door part on left side
+       {
+         src_xx = 0;
+         dst_xx = pos->x - kk * pos->step_xoffset;
+
+         if (dst_xx < 0)
+         {
+           src_xx = ABS(dst_xx);
+           dst_xx = 0;
+         }
+
+         width = g->width - src_xx;
+
+         // printf("::: k == %d [%d] \n", k, start_step);
+       }
+
+       if (pos->step_yoffset < 0)      // door part on bottom side
+       {
+         src_yy = 0;
+         dst_yy = pos->y + ABS(kk * pos->step_yoffset);
+         height = g->height;
+
+         if (dst_yy + height > door_rect->height)
+           height = door_rect->height - dst_yy;
+       }
+       else                            // door part on top side
+       {
+         src_yy = 0;
+         dst_yy = pos->y - kk * pos->step_yoffset;
+
+         if (dst_yy < 0)
+         {
+           src_yy = ABS(dst_yy);
+           dst_yy = 0;
+         }
+
+         height = g->height - src_yy;
+       }
 
+       if (is_panel)
+       {
 #if 1
-    if (!PendingEvent())       /* delay only if no pending events */
-      Delay(10);
+         src_x = src_xx;
+         src_y = src_yy;
 #else
-    /* don't eat all CPU time */
-    Delay(10);
+         src_x = panel_pos->x + src_xx;
+         src_y = panel_pos->y + src_yy;
 #endif
+       }
+       else
+       {
+         src_x = g->src_x + src_xx;
+         src_y = g->src_y + src_yy;
+       }
 
+       dst_x = door_rect->x + dst_xx;
+       dst_y = door_rect->y + dst_yy;
+
+#if 0
+       if (DOOR_PART_IS_PANEL(nr))
+       {
+         printf("::: width == %d, height == %d [%d, %d] [%d, %d]\n",
+                width, height, g->width, g->height, src_x, src_y);
+       }
 #endif
-  }
 
-  if (game_status != GAME_MODE_MAIN)
-    StopAnimation();
+       if (width  >= 0 && width  <= g->width &&
+           height >= 0 && height <= g->height)
+       {
+         if (is_panel || !pos->draw_masked)
+           BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
+                      dst_x, dst_y);
+         else
+           BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
+                            dst_x, dst_y);
+       }
+
+#if 0
+       if (DOOR_PART_IS_PANEL(nr))
+       {
+         bitmap = bitmap_db_door;
+         src_x = panel_pos->x + src_xx;
+         src_y = panel_pos->y + src_yy;
+
+         printf("::: width == %d, height == %d [%d, %d] [%d, %d]\n",
+                width, height, g->width, g->height, src_x, src_y);
+
+         if (width  >= 0 && width  <= g->width &&
+             height >= 0 && height <= g->height)
+           BlitBitmap(bitmap, drawto, src_x, src_y,
+                            width, height,
+                            dst_x, dst_y);
+       }
+#endif
 
-  UnmapToolButtons();
+       redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
 
 #if 1
-  if (global.use_envelope_request)
-    ShowEnvelopeDoor(text, ACTION_CLOSING);
+       if ((part_opening && (width < 0         || height < 0)) ||
+           (part_closing && (width >= g->width && height >= g->height)))
+         door_part_done[nr] = TRUE;
+#else
+       if ((door_opening && (width < 0         || height < 0)) ||
+           (door_closing && (width >= g->width && height >= g->height)))
+         door_part_done[nr] = TRUE;
 #endif
 
 #if 1
-  if (!(req_state & REQ_STAY_OPEN) && !global.use_envelope_request)
+       // continue door part animations, but not panel after door has closed
+       if (!door_part_done[nr] &&
+           !(is_panel && door_closing && panel_has_doors[door_index]))
+         door_part_done_all = FALSE;
 #else
-  if (!(req_state & REQ_STAY_OPEN))
+       // continue door part animations, but not panel after door has closed
+       if (!door_part_done[nr] && !(is_panel && door_closing))
+         door_part_done_all = FALSE;
 #endif
-  {
-    CloseDoor(DOOR_CLOSE_1);
-
-    if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
-       (req_state & REQ_REOPEN))
-      OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
-  }
-
-  RemapAllGadgets();
 
-  if (game_status == GAME_MODE_PLAYING)
-  {
-    SetPanelBackground();
-    SetDrawBackgroundMask(REDRAW_DOOR_1);
-  }
-  else
-  {
-    SetDrawBackgroundMask(REDRAW_FIELD);
-  }
-
-#if defined(NETWORK_AVALIABLE)
-  /* continue network game after request */
-  if (options.network &&
-      game_status == GAME_MODE_PLAYING &&
-      req_state & REQUEST_WAIT_FOR_INPUT)
-    SendToServer_ContinuePlaying();
+#if 0
+       if (!door_part_done[nr])
+         printf("::: k == %d, nr == %d\n", k, nr);
 #endif
+      }
 
-  /* restore deactivated drawing when quick-loading level tape recording */
-  if (tape.playing && tape.deactivate_display)
-    TapeDeactivateDisplayOn();
-
-  return result;
-}
-
-unsigned int OpenDoor(unsigned int door_state)
-{
-  if (door_state & DOOR_COPY_BACK)
-  {
-    if (door_state & DOOR_OPEN_1)
-      BlitBitmap(bitmap_db_door, bitmap_db_door,
-                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
-                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+      if (!(door_state & DOOR_NO_DELAY))
+      {
+       BackToFront();
 
-    if (door_state & DOOR_OPEN_2)
-      BlitBitmap(bitmap_db_door, bitmap_db_door,
-                DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY2, VXSIZE, VYSIZE,
-                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
+       if (game_status == GAME_MODE_MAIN)
+         DoAnimation();
 
-    door_state &= ~DOOR_COPY_BACK;
-  }
+       WaitUntilDelayReached(&door_delay, door_delay_value);
 
-  return MoveDoor(door_state);
-}
+       current_move_delay += max_step_delay;
+      }
 
-unsigned int CloseDoor(unsigned int door_state)
-{
-  unsigned int old_door_state = GetDoorState();
+#if 0
+      door_part_done_all = TRUE;
 
-  if (!(door_state & DOOR_NO_COPY_BACK))
-  {
-    if (old_door_state & DOOR_OPEN_1)
-      BlitBitmap(backbuffer, bitmap_db_door,
-                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+      for (i = 0; i < MAX_DOOR_PARTS; i++)
+       if (!door_part_done[i] &&
+           !(DOOR_PART_IS_PANEL(i) && door_closing))
+         door_part_done_all = FALSE;
+#endif
+#if 1
+      if (door_part_done_all)
+       break;
+#endif
+    }
+  }
 
-    if (old_door_state & DOOR_OPEN_2)
-      BlitBitmap(backbuffer, bitmap_db_door,
-                VX, VY, VXSIZE, VYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY2);
+  if (door_state & DOOR_ACTION_1)
+    door1 = door_state & DOOR_ACTION_1;
+  if (door_state & DOOR_ACTION_2)
+    door2 = door_state & DOOR_ACTION_2;
 
-    door_state &= ~DOOR_NO_COPY_BACK;
-  }
+#if 0
+  printf("::: DOORS DONE %08x\n", door_state);
+  Delay(3000);
+  printf("::: GO!\n");
+#endif
 
-  return MoveDoor(door_state);
+  return (door1 | door2);
 }
 
-unsigned int GetDoorState()
-{
-  return MoveDoor(DOOR_GET_STATE);
-}
+#else
 
-unsigned int SetDoorState(unsigned int door_state)
-{
-  return MoveDoor(door_state | DOOR_SET_STATE);
-}
+// ========== OLD ==============================================================
 
 unsigned int MoveDoor(unsigned int door_state)
 {
@@ -4360,6 +6319,7 @@ unsigned int MoveDoor(unsigned int door_state)
   unsigned int door_delay_value;
   int stepsize = 1;
 
+#if 1
   if (door_1.width < 0 || door_1.width > DXSIZE)
     door_1.width = DXSIZE;
   if (door_1.height < 0 || door_1.height > DYSIZE)
@@ -4368,6 +6328,7 @@ unsigned int MoveDoor(unsigned int door_state)
     door_2.width = VXSIZE;
   if (door_2.height < 0 || door_2.height > VYSIZE)
     door_2.height = VYSIZE;
+#endif
 
   if (door_state == DOOR_GET_STATE)
     return (door1 | door2);
@@ -4397,6 +6358,8 @@ unsigned int MoveDoor(unsigned int door_state)
   door_delay_value = (door_state & DOOR_ACTION_1 ? door_1.step_delay :
                      door_2.step_delay);
 
+  // door_delay_value *= 4;    // !!! TEST ONLY !!!
+
   if (setup.quick_doors)
   {
     stepsize = 20;             /* must be chosen to always draw last frame */
@@ -4416,17 +6379,46 @@ unsigned int MoveDoor(unsigned int door_state)
 
   if (door_state & DOOR_ACTION)
   {
+#if 1
+    struct GraphicInfo *g1_left  = &graphic_info[IMG_DOOR_1_WING_LEFT];
+    struct GraphicInfo *g1_right = &graphic_info[IMG_DOOR_1_WING_RIGHT];
+    struct GraphicInfo *g2_left  = &graphic_info[IMG_DOOR_2_WING_LEFT];
+    struct GraphicInfo *g2_right = &graphic_info[IMG_DOOR_2_WING_RIGHT];
+    int door_1_left_width   = g1_left->width;
+    int door_1_left_height  = g1_left->height;
+    int door_1_right_width  = g1_right->width;
+    int door_1_right_height = g1_right->height;
+    int door_2_left_width   = g2_left->width;
+    int door_2_left_height  = g2_left->height;
+    int door_2_right_width  = g2_right->width;
+    int door_2_right_height = g2_right->height;
+    int door_1_width  = MAX(door_1_left_width,  door_1_right_width);
+    int door_1_height = MAX(door_1_left_height, door_1_right_height);
+    int door_2_width  = MAX(door_2_left_width,  door_2_right_width);
+    int door_2_height = MAX(door_2_left_height, door_2_right_height);
+#endif
     boolean handle_door_1 = (door_state & DOOR_ACTION_1);
     boolean handle_door_2 = (door_state & DOOR_ACTION_2);
     boolean door_1_done = (!handle_door_1);
     boolean door_2_done = (!handle_door_2);
     boolean door_1_vertical = (door_1.anim_mode & ANIM_VERTICAL);
     boolean door_2_vertical = (door_2.anim_mode & ANIM_VERTICAL);
+#if 1
+#if 1
+    int door_size_1 = (door_1_vertical ? door_1_height : door_1_width);
+    int door_size_2 = (door_2_vertical ? door_2_height : door_2_width);
+#else
+    int door_size_1 = (door_1_vertical ? DYSIZE : DXSIZE);
+    int door_size_2 = (door_2_vertical ? VYSIZE : VXSIZE);
+#endif
+#else
     int door_size_1 = (door_1_vertical ? door_1.height : door_1.width);
     int door_size_2 = (door_2_vertical ? door_2.height : door_2.width);
+#endif
     int max_door_size_1 = (door_1_vertical ? DYSIZE : DXSIZE);
     int max_door_size_2 = (door_2_vertical ? VYSIZE : VXSIZE);
-    int door_size     = (handle_door_1 ? door_size_1     : door_size_2);
+    // int door_size     = (handle_door_1 ? door_size_1     : door_size_2);
+    int door_size     = (handle_door_2 ? door_size_2     : door_size_1);
     int max_door_size = (handle_door_1 ? max_door_size_1 : max_door_size_2);
     int door_skip = max_door_size - door_size;
     int end = door_size;
@@ -4445,14 +6437,35 @@ unsigned int MoveDoor(unsigned int door_state)
     for (k = start; k <= end && !(door_1_done && door_2_done); k += stepsize)
     {
       int x = k;
+#if 0
       Bitmap *bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
       GC gc = bitmap->stored_clip_gc;
+#endif
 
-      if (door_state & DOOR_ACTION_1)
+      if (door_state & DOOR_ACTION_1 &&
+         x * door_1.step_offset <= door_size_1)
       {
        int a = MIN(x * door_1.step_offset, end);
        int p = (door_state & DOOR_OPEN_1 ? end - a : a);
+#if 1
+       int i = p;
+#else
        int i = p + door_skip;
+#endif
+
+#if 1
+       struct GraphicInfo *g_left  = &graphic_info[IMG_DOOR_1_WING_LEFT];
+       struct GraphicInfo *g_right = &graphic_info[IMG_DOOR_1_WING_RIGHT];
+       Bitmap *bm_left  = g_left->bitmap;
+       Bitmap *bm_right = g_right->bitmap;
+       GC gc_left  = bm_left->stored_clip_gc;
+       GC gc_right = bm_right->stored_clip_gc;
+#endif
+
+       int classic_dxsize = 100;
+       int classic_dysize = 280;
+       boolean classic_door_1_size = (DXSIZE == classic_dxsize &&
+                                      DYSIZE == classic_dysize);
 
        if (door_1.anim_mode & ANIM_STATIC_PANEL)
        {
@@ -4466,14 +6479,37 @@ unsigned int MoveDoor(unsigned int door_state)
                     DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1 + p / 2,
                     DXSIZE, DYSIZE - p / 2, DX, DY);
 
+#if 1
+         // printf("::: p == %d\n", p);
          ClearRectangle(drawto, DX, DY + DYSIZE - p / 2, DXSIZE, p / 2);
+#endif
        }
 
        if (door_1.anim_mode & ANIM_HORIZONTAL && x <= DXSIZE)
        {
+#if 1
+         int src1_x = g_right->src_x;
+         int src1_y = g_right->src_y;
+         int src2_x = g_left->src_x + g_left->width - i;
+         int src2_y = g_left->src_y;
+         int dst1_x = DX + DXSIZE - i;
+         int dst1_y = DY;
+         int dst2_x = DX;
+         int dst2_y = DY;
+         int width = i;
+         int height = DYSIZE;
+
+         SetClipOrigin(bm_right, gc_right, dst1_x - src1_x, dst1_y - src1_y);
+         BlitBitmapMasked(bm_right, drawto, src1_x, src1_y, width, height,
+                          dst1_x, dst1_y);
+
+         SetClipOrigin(bm_left, gc_left, dst2_x - src2_x, dst2_y - src2_y);
+         BlitBitmapMasked(bm_left, drawto, src2_x, src2_y, width, height,
+                          dst2_x, dst2_y);
+#else
          int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
-         int dst1_x = DX + DXSIZE - i, dst1_y = DY;
          int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
+         int dst1_x = DX + DXSIZE - i, dst1_y = DY;
          int dst2_x = DX,              dst2_y = DY;
          int width = i, height = DYSIZE;
 
@@ -4484,12 +6520,33 @@ unsigned int MoveDoor(unsigned int door_state)
          SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
          BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
                           dst2_x, dst2_y);
+#endif
        }
        else if (door_1.anim_mode & ANIM_VERTICAL && x <= DYSIZE)
        {
+#if 1
+         int src1_x = g_right->src_x;
+         int src1_y = g_right->src_y;
+         int src2_x = g_left->src_x;
+         int src2_y = g_left->src_y + g_left->height - i;
+         int dst1_x = DX;
+         int dst1_y = DY + DYSIZE - i;
+         int dst2_x = DX;
+         int dst2_y = DY;
+         int width = DXSIZE;
+         int height = i;
+
+         SetClipOrigin(bm_right, gc_right, dst1_x - src1_x, dst1_y - src1_y);
+         BlitBitmapMasked(bm_right, drawto, src1_x, src1_y, width, height,
+                          dst1_x, dst1_y);
+
+         SetClipOrigin(bm_left, gc_left, dst2_x - src2_x, dst2_y - src2_y);
+         BlitBitmapMasked(bm_left, drawto, src2_x, src2_y, width, height,
+                          dst2_x, dst2_y);
+#else
          int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
-         int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
          int src2_x = 0,               src2_y = DOOR_GFX_PAGEY1 + DYSIZE - i;
+         int dst1_x = DX,              dst1_y = DY + DYSIZE - i;
          int dst2_x = DX,              dst2_y = DY;
          int width = DXSIZE, height = i;
 
@@ -4500,11 +6557,99 @@ unsigned int MoveDoor(unsigned int door_state)
          SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
          BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
                           dst2_x, dst2_y);
+#endif
        }
-       else if (x <= DXSIZE)   /* ANIM_DEFAULT */
+       else if (classic_door_1_size && x <= DXSIZE)    /* ANIM_DEFAULT */
        {
          int j = (door_1.anim_mode == ANIM_DEFAULT ? (DXSIZE - i) / 3 : 0);
 
+#if 1
+         int src1_x = g_right->src_x;
+         int src1_y = g_right->src_y;
+         int src2_x = g_left->src_x + g_left->width - i;
+         int src2_y = g_left->src_y;
+         int dst1_x = DX + DXSIZE - i;
+         int dst1_y = DY;
+         int dst2_x = DX;
+         int dst2_y = DY;
+         int width = i;
+         int height1 = 63, height2 = DYSIZE / 2 - height1;
+         int ypos1 = 0, ypos2 = height2;
+         int ypos3 = DYSIZE / 2, ypos4 = DYSIZE - height2;
+
+         SetClipOrigin(bm_right, gc_right,
+                       dst1_x - src1_x, dst1_y - src1_y + j);
+         BlitBitmapMasked(bm_right, drawto,
+                          src1_x, src1_y + ypos1, width, height2,
+                          dst1_x, dst1_y + ypos1 + j);
+         BlitBitmapMasked(bm_right, drawto,
+                          src1_x, src1_y + ypos3, width, height1,
+                          dst1_x, dst1_y + ypos3 + j);
+         SetClipOrigin(bm_left, gc_left,
+                       dst2_x - src2_x, dst2_y - src2_y - j);
+         BlitBitmapMasked(bm_left, drawto,
+                          src2_x, src2_y + ypos1 + j, width, height2 - j,
+                          dst2_x, dst2_y + ypos1);
+         BlitBitmapMasked(bm_left, drawto,
+                          src2_x, src2_y + ypos3, width, height1,
+                          dst2_x, dst2_y + ypos3 - j);
+
+         SetClipOrigin(bm_left, gc_left,
+                       dst2_x - src2_x, dst2_y - src2_y - j);
+         BlitBitmapMasked(bm_left, drawto,
+                          src2_x, src2_y + ypos2, width, height1,
+                          dst2_x, dst2_y + ypos2 - j);
+         BlitBitmapMasked(bm_left, drawto,
+                          src2_x, src2_y + ypos4, width, height2,
+                          dst2_x, dst2_y + ypos4 - j);
+         SetClipOrigin(bm_right, gc_right,
+                       dst1_x - src1_x, dst1_y - src1_y + j);
+         BlitBitmapMasked(bm_right, drawto,
+                          src1_x, src1_y + ypos2, width, height1,
+                          dst1_x, dst1_y + ypos2 + j);
+         BlitBitmapMasked(bm_right, drawto,
+                          src1_x, src1_y + ypos4, width, height2 - j,
+                          dst1_x, dst1_y + ypos4 + j);
+
+#else
+         int src1_x = DXSIZE,          src1_y = DOOR_GFX_PAGEY1;
+         int src2_x = DXSIZE - i,      src2_y = DOOR_GFX_PAGEY1;
+         int dst1_x = DX + DXSIZE - i, dst1_y = DY;
+         int dst2_x = DX,              dst2_y = DY;
+         int width = i, height = DYSIZE;
+         int ypos1 = 63, ypos2 = 77, ypos3 = 140, ypos4 = 203;
+
+         SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y + j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src1_x, src1_y, width, ypos2,
+                          dst1_x, dst1_y + j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src1_x, src1_y + ypos3, width, ypos1,
+                          dst1_x, dst1_y + ypos3 + j);
+         SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y - j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src2_x, src2_y + j, width, ypos2 - j,
+                          dst2_x, dst2_y);
+         BlitBitmapMasked(bitmap, drawto,
+                          src2_x, src2_y + ypos3, width, ypos1,
+                          dst2_x, dst2_y + ypos3 - j);
+
+         SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y - j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src2_x, src2_y + ypos2, width, ypos1,
+                          dst2_x, dst2_y + ypos2 - j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src2_x, src2_y + ypos4, width, ypos2,
+                          dst2_x, dst2_y + ypos4 - j);
+         SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y + j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src1_x, src1_y + ypos2, width, ypos1,
+                          dst1_x, dst1_y + ypos2 + j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src1_x, src1_y + ypos4, width, ypos2 - j,
+                          dst1_x, dst1_y + ypos4 + j);
+
+         /*
          SetClipOrigin(bitmap, gc, DX - i, (DY + j) - DOOR_GFX_PAGEY1);
          BlitBitmapMasked(bitmap, drawto,
                           DXSIZE, DOOR_GFX_PAGEY1, i, 77,
@@ -4534,18 +6679,35 @@ unsigned int MoveDoor(unsigned int door_state)
          BlitBitmapMasked(bitmap, drawto,
                           DXSIZE, DOOR_GFX_PAGEY1 + 203, i, 77 - j,
                           DX + DXSIZE - i, DY + 203 + j);
+         */
+#endif
        }
 
        redraw_mask |= REDRAW_DOOR_1;
        door_1_done = (a == end);
       }
 
-      if (door_state & DOOR_ACTION_2)
+      if (door_state & DOOR_ACTION_2 &&
+         x * door_2.step_offset <= door_size_2)
       {
        int a = MIN(x * door_2.step_offset, door_size);
        int p = (door_state & DOOR_OPEN_2 ? door_size - a : a);
        int i = p + door_skip;
 
+#if 1
+       struct GraphicInfo *g_left  = &graphic_info[IMG_DOOR_2_WING_LEFT];
+       struct GraphicInfo *g_right = &graphic_info[IMG_DOOR_2_WING_RIGHT];
+       Bitmap *bm_left  = g_left->bitmap;
+       Bitmap *bm_right = g_right->bitmap;
+       GC gc_left  = bm_left->stored_clip_gc;
+       GC gc_right = bm_right->stored_clip_gc;
+#endif
+
+       int classic_vxsize = 100;
+       int classic_vysize = 100;
+       boolean classic_door_2_size = (VXSIZE == classic_vxsize &&
+                                      VYSIZE == classic_vysize);
+
        if (door_2.anim_mode & ANIM_STATIC_PANEL)
        {
          BlitBitmap(bitmap_db_door, drawto,
@@ -4563,9 +6725,29 @@ unsigned int MoveDoor(unsigned int door_state)
 
        if (door_2.anim_mode & ANIM_HORIZONTAL && x <= VXSIZE)
        {
+#if 1
+         int src1_x = g_right->src_x;
+         int src1_y = g_right->src_y;
+         int src2_x = g_left->src_x + g_left->width - i;
+         int src2_y = g_left->src_y;
+         int dst1_x = VX + VXSIZE - i;
+         int dst1_y = VY;
+         int dst2_x = VX;
+         int dst2_y = VY;
+         int width = i;
+         int height = VYSIZE;
+
+         SetClipOrigin(bm_right, gc_right, dst1_x - src1_x, dst1_y - src1_y);
+         BlitBitmapMasked(bm_right, drawto, src1_x, src1_y, width, height,
+                          dst1_x, dst1_y);
+
+         SetClipOrigin(bm_left, gc_left, dst2_x - src2_x, dst2_y - src2_y);
+         BlitBitmapMasked(bm_left, drawto, src2_x, src2_y, width, height,
+                          dst2_x, dst2_y);
+#else
          int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
-         int dst1_x = VX + VXSIZE - i, dst1_y = VY;
          int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
+         int dst1_x = VX + VXSIZE - i, dst1_y = VY;
          int dst2_x = VX,              dst2_y = VY;
          int width = i, height = VYSIZE;
 
@@ -4576,12 +6758,33 @@ unsigned int MoveDoor(unsigned int door_state)
          SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
          BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
                           dst2_x, dst2_y);
+#endif
        }
        else if (door_2.anim_mode & ANIM_VERTICAL && x <= VYSIZE)
        {
+#if 1
+         int src1_x = g_right->src_x;
+         int src1_y = g_right->src_y;
+         int src2_x = g_left->src_x;
+         int src2_y = g_left->src_y + g_left->height - i;
+         int dst1_x = VX;
+         int dst1_y = VY + VYSIZE - i;
+         int dst2_x = VX;
+         int dst2_y = VY;
+         int width = VXSIZE;
+         int height = i;
+
+         SetClipOrigin(bm_right, gc_right, dst1_x - src1_x, dst1_y - src1_y);
+         BlitBitmapMasked(bm_right, drawto, src1_x, src1_y, width, height,
+                          dst1_x, dst1_y);
+
+         SetClipOrigin(bm_left, gc_left, dst2_x - src2_x, dst2_y - src2_y);
+         BlitBitmapMasked(bm_left, drawto, src2_x, src2_y, width, height,
+                          dst2_x, dst2_y);
+#else
          int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
-         int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
          int src2_x = 0,               src2_y = DOOR_GFX_PAGEY2 + VYSIZE - i;
+         int dst1_x = VX,              dst1_y = VY + VYSIZE - i;
          int dst2_x = VX,              dst2_y = VY;
          int width = VXSIZE, height = i;
 
@@ -4592,11 +6795,73 @@ unsigned int MoveDoor(unsigned int door_state)
          SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src2_y);
          BlitBitmapMasked(bitmap, drawto, src2_x, src2_y, width, height,
                           dst2_x, dst2_y);
+#endif
        }
-       else if (x <= VXSIZE)   /* ANIM_DEFAULT */
+       else if (classic_door_2_size && x <= VXSIZE)    /* ANIM_DEFAULT */
        {
          int j = (door_2.anim_mode == ANIM_DEFAULT ? (VXSIZE - i) / 3 : 0);
 
+#if 1
+         int src1_x = g_right->src_x;
+         int src1_y = g_right->src_y;
+         int src2_x = g_left->src_x + g_left->width - i;
+         int src2_y = g_left->src_y;
+         int dst1_x = VX + VXSIZE - i;
+         int dst1_y = VY;
+         int dst2_x = VX;
+         int dst2_y = VY;
+         int width = i;
+         int height = VYSIZE / 2;
+         int ypos1 = 0, ypos2 = VYSIZE / 2;
+
+         SetClipOrigin(bm_right, gc_right,
+                       dst1_x - src1_x, dst1_y - src1_y + j);
+         BlitBitmapMasked(bm_right, drawto,
+                          src1_x, src1_y + ypos1, width, height,
+                          dst1_x, dst1_y + ypos1 + j);
+         SetClipOrigin(bm_left, gc_left,
+                       dst2_x - src2_x, dst2_y - src2_y - j);
+         BlitBitmapMasked(bm_left, drawto,
+                          src2_x, src2_y + ypos1 + j, width, height - j,
+                          dst2_x, dst2_y + ypos1);
+
+         SetClipOrigin(bm_left, gc_left,
+                       dst2_x - src2_x, dst2_y - src2_y - j);
+         BlitBitmapMasked(bm_left, drawto,
+                          src2_x, src2_y + ypos2, width, height,
+                          dst2_x, dst2_y + ypos2 - j);
+         SetClipOrigin(bm_right, gc_right,
+                       dst1_x - src1_x, dst1_y - src1_y + j);
+         BlitBitmapMasked(bm_right, drawto,
+                          src1_x, src1_y + ypos2, width, height - j,
+                          dst1_x, dst1_y + ypos2 + j);
+#else
+         int src1_x = VXSIZE,          src1_y = DOOR_GFX_PAGEY2;
+         int src2_x = VXSIZE - i,      src2_y = DOOR_GFX_PAGEY2;
+         int dst1_x = VX + VXSIZE - i, dst1_y = VY;
+         int dst2_x = VX,              dst2_y = VY;
+         int width = i, height = VYSIZE;
+         int ypos = VYSIZE / 2;
+
+         SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y + j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src1_x, src1_y, width, ypos,
+                          dst1_x, dst1_y + j);
+         SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src1_y - j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src2_x, src2_y + j, width, ypos - j,
+                          dst2_x, dst2_y);
+
+         SetClipOrigin(bitmap, gc, dst2_x - src2_x, dst2_y - src1_y - j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src2_x, src2_y + ypos, width, ypos,
+                          dst2_x, dst2_y + ypos - j);
+         SetClipOrigin(bitmap, gc, dst1_x - src1_x, dst1_y - src1_y + j);
+         BlitBitmapMasked(bitmap, drawto,
+                          src1_x, src1_y + ypos, width, ypos - j,
+                          dst1_x, dst1_y + ypos + j);
+
+         /*
          SetClipOrigin(bitmap, gc, VX - i, (VY + j) - DOOR_GFX_PAGEY2);
          BlitBitmapMasked(bitmap, drawto,
                           VXSIZE, DOOR_GFX_PAGEY2, i, VYSIZE / 2,
@@ -4615,6 +6880,8 @@ unsigned int MoveDoor(unsigned int door_state)
                           VXSIZE, DOOR_GFX_PAGEY2 + VYSIZE / 2,
                           i, VYSIZE / 2 - j,
                           VX + VXSIZE - i, VY + VYSIZE / 2 + j);
+         */
+#endif
        }
 
        redraw_mask |= REDRAW_DOOR_2;
@@ -4641,25 +6908,74 @@ unsigned int MoveDoor(unsigned int door_state)
   return (door1 | door2);
 }
 
+#endif
+
 void DrawSpecialEditorDoor()
 {
-  /* draw bigger toolbox window */
+#if 1
+  struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
+  int top_border_width = gfx1->width;
+  int top_border_height = gfx1->height;
+  int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
+  int ex = EX - outer_border;
+  int ey = EY - outer_border;
+  int vy = VY - outer_border;
+  int exsize = EXSIZE + 2 * outer_border;
+
+  CloseDoor(DOOR_CLOSE_2);
+
+  /* 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);
+  BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
+            exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
+#else
+  /* draw bigger level editor toolbox window */
   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-            DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
-            EX - 4, EY - 12);
+             DOOR_GFX_PAGEX7, 0, EXSIZE + 8, 8,
+             EX - 4, EY - 12);
   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
-            EX - 6, VY - 4, EXSIZE + 12, EYSIZE - VYSIZE + 4,
-            EX - 6, EY - 4);
+             EX - 6, VY - 4, EXSIZE + 12, EYSIZE - VYSIZE + 4,
+             EX - 6, EY - 4);
+#endif
 
   redraw_mask |= REDRAW_ALL;
 }
 
 void UndrawSpecialEditorDoor()
 {
+#if 1
+  struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
+  int top_border_width = gfx1->width;
+  int top_border_height = gfx1->height;
+  int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
+  int ex = EX - outer_border;
+  int ey = EY - outer_border;
+  int ey_top = ey - top_border_height;
+  int exsize = EXSIZE + 2 * outer_border;
+  int eysize = EYSIZE + 2 * outer_border;
+
+  /* draw normal tape recorder window */
+  if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
+  {
+    BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
+              ex, ey_top, top_border_width, top_border_height,
+              ex, ey_top);
+    BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
+              ex, ey, exsize, eysize, ex, ey);
+  }
+  else
+  {
+    // if screen background is set to "[NONE]", clear editor toolbox window
+    ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
+    ClearRectangle(drawto, ex, ey, exsize, eysize);
+  }
+#else
   /* draw normal tape recorder window */
   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
-            EX - 6, EY - 12, EXSIZE + 12, EYSIZE - VYSIZE + 12,
-            EX - 6, EY - 12);
+             EX - 6, EY - 12, EXSIZE + 12, EYSIZE - VYSIZE + 12,
+             EX - 6, EY - 12);
+#endif
 
   redraw_mask |= REDRAW_ALL;
 }
@@ -4719,12 +7035,17 @@ void CreateToolButtons()
     Bitmap *deco_bitmap = None;
     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
     unsigned int event_mask = GD_EVENT_RELEASED;
-    int gd_x   = gfx->src_x;
-    int gd_y   = gfx->src_y;
-    int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
-    int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
+    int dx = DX;
+    int dy = DY;
+    int gd_x = gfx->src_x;
+    int gd_y = gfx->src_y;
+    int gd_xp = gfx->src_x + gfx->pressed_xoffset;
+    int gd_yp = gfx->src_y + gfx->pressed_yoffset;
     int id = i;
 
+    if (global.use_envelope_request)
+      setRequestPosition(&dx, &dy, TRUE);
+
     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
     {
       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
@@ -4737,8 +7058,8 @@ void CreateToolButtons()
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_INFO_TEXT, toolbutton_info[i].infotext,
-                     GDI_X, DX + pos->x,
-                     GDI_Y, DY + pos->y,
+                     GDI_X, dx + GDI_ACTIVE_POS(pos->x),
+                     GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
                      GDI_WIDTH, gfx->width,
                      GDI_HEIGHT, gfx->height,
                      GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
@@ -4897,8 +7218,8 @@ void CreateToolButtons()
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_INFO_TEXT, toolbutton_info[i].infotext,
-                     GDI_X, DX + toolbutton_info[i].x,
-                     GDI_Y, DY + toolbutton_info[i].y,
+                     GDI_X, DX + GDI_ACTIVE_POS(toolbutton_info[i].x),
+                     GDI_Y, DY + GDI_ACTIVE_POS(toolbutton_info[i].y),
                      GDI_WIDTH, toolbutton_info[i].width,
                      GDI_HEIGHT, toolbutton_info[i].height,
                      GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
@@ -7327,11 +9648,33 @@ int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
 }
 
+#if 1
+boolean getTeamMode_EM()
+{
+  return game.team_mode;
+}
+#else
 int getNumActivePlayers_EM()
 {
+#if 1
+  int num_players = 0;
+  int i;
+
+  if (!tape.playing)
+    return (setup.team_mode ? MAX_PLAYERS : 1);
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    if (tape.player_participates[i])
+      num_players++;
+
+  return (num_players > 1 ? MAX_PLAYERS : 1);
+
+#else
+
   int num_players = 0;
   int i;
 
+  /* when recording game, activate all connected players */
   if (!tape.playing)
     return -1;
 
@@ -7340,7 +9683,9 @@ int getNumActivePlayers_EM()
       num_players++;
 
   return num_players;
+#endif
 }
+#endif
 
 int getGameFrameDelay_EM(int native_em_game_frame_delay)
 {
@@ -9089,7 +11434,7 @@ void PlaySoundSelecting()
 #endif
 }
 
-void ToggleFullscreenIfNeeded()
+void ToggleFullscreenOrChangeWindowScalingIfNeeded()
 {
   boolean change_fullscreen = (setup.fullscreen !=
                               video.fullscreen_enabled);
@@ -9167,27 +11512,55 @@ void ToggleFullscreenIfNeeded()
 
 void ChangeViewportPropertiesIfNeeded()
 {
+#if 0
   int *door_1_x = &DX;
   int *door_1_y = &DY;
   int *door_2_x = (game_status == GAME_MODE_EDITOR ? &EX : &VX);
   int *door_2_y = (game_status == GAME_MODE_EDITOR ? &EY : &VY);
+#endif
+#if 1
+  int gfx_game_mode = game_status;
+#else
   int gfx_game_mode = (game_status == GAME_MODE_PLAYING ||
                       game_status == GAME_MODE_EDITOR ? game_status :
                       GAME_MODE_MAIN);
+#endif
+  int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
+                       game_status);
   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_mode];
-  int border_size = vp_playfield->border_size;
-  int new_sx = vp_playfield->x + border_size;
-  int new_sy = vp_playfield->y + border_size;
-  int new_sxsize = vp_playfield->width  - 2 * border_size;
-  int new_sysize = vp_playfield->height - 2 * border_size;
-  int new_real_sx = vp_playfield->x;
-  int new_real_sy = vp_playfield->y;
-  int new_full_sxsize = vp_playfield->width;
-  int new_full_sysize = vp_playfield->height;
+  struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
+  struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
+  int border_size      = vp_playfield->border_size;
+  int new_sx           = vp_playfield->x + border_size;
+  int new_sy           = vp_playfield->y + border_size;
+  int new_sxsize       = vp_playfield->width  - 2 * border_size;
+  int new_sysize       = vp_playfield->height - 2 * border_size;
+  int new_real_sx      = vp_playfield->x;
+  int new_real_sy      = vp_playfield->y;
+  int new_full_sxsize  = vp_playfield->width;
+  int new_full_sysize  = vp_playfield->height;
+  int new_dx           = vp_door_1->x;
+  int new_dy           = vp_door_1->y;
+  int new_dxsize       = vp_door_1->width;
+  int new_dysize       = vp_door_1->height;
+  int new_vx           = vp_door_2->x;
+  int new_vy           = vp_door_2->y;
+  int new_vxsize       = vp_door_2->width;
+  int new_vysize       = vp_door_2->height;
+  int new_ex           = vp_door_3->x;
+  int new_ey           = vp_door_3->y;
+  int new_exsize       = vp_door_3->width;
+  int new_eysize       = vp_door_3->height;
 #if NEW_TILESIZE
+
+#if NEW_GAME_TILESIZE
+  int new_tilesize_var =
+    (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
+#else
   int new_tilesize_var = TILESIZE / (setup.small_game_graphics ? 2 : 1);
+#endif
+
   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
                  gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
   int new_scr_fieldx = new_sxsize / tilesize;
@@ -9201,6 +11574,7 @@ void ChangeViewportPropertiesIfNeeded()
   boolean init_gfx_buffers = FALSE;
   boolean init_video_buffer = FALSE;
   boolean init_gadgets_and_toons = FALSE;
+  boolean init_em_graphics = FALSE;
 
 #if 0
   /* !!! TEST ONLY !!! */
@@ -9259,22 +11633,63 @@ void ChangeViewportPropertiesIfNeeded()
 
   if (new_sx != SX ||
       new_sy != SY ||
+      new_dx != DX ||
+      new_dy != DY ||
+      new_vx != VX ||
+      new_vy != VY ||
+      new_ex != EX ||
+      new_ey != EY ||
       new_sxsize != SXSIZE ||
       new_sysize != SYSIZE ||
+      new_dxsize != DXSIZE ||
+      new_dysize != DYSIZE ||
+      new_vxsize != VXSIZE ||
+      new_vysize != VYSIZE ||
+      new_exsize != EXSIZE ||
+      new_eysize != EYSIZE ||
       new_real_sx != REAL_SX ||
       new_real_sy != REAL_SY ||
       new_full_sxsize != FULL_SXSIZE ||
       new_full_sysize != FULL_SYSIZE ||
-      new_tilesize_var != TILESIZE_VAR ||
+      new_tilesize_var != TILESIZE_VAR
+#if 0
+      ||
       vp_door_1->x != *door_1_x ||
       vp_door_1->y != *door_1_y ||
       vp_door_2->x != *door_2_x ||
-      vp_door_2->y != *door_2_y)
+      vp_door_2->y != *door_2_y
+#endif
+      )
   {
+#if 1
+    if (new_tilesize_var != TILESIZE_VAR)
+    {
+      // printf("::: new_tilesize_var != TILESIZE_VAR\n");
+
+      // changing tile size invalidates scroll values of engine snapshots
+      FreeEngineSnapshot();
+
+      // changing tile size requires update of graphic mapping for EM engine
+      init_em_graphics = TRUE;
+    }
+#endif
+
     SX = new_sx;
     SY = new_sy;
+    DX = new_dx;
+    DY = new_dy;
+    VX = new_vx;
+    VY = new_vy;
+    EX = new_ex;
+    EY = new_ey;
     SXSIZE = new_sxsize;
     SYSIZE = new_sysize;
+    DXSIZE = new_dxsize;
+    DYSIZE = new_dysize;
+    VXSIZE = new_vxsize;
+    VYSIZE = new_vysize;
+    EXSIZE = new_exsize;
+    EYSIZE = new_eysize;
     REAL_SX = new_real_sx;
     REAL_SY = new_real_sy;
     FULL_SXSIZE = new_full_sxsize;
@@ -9287,10 +11702,12 @@ void ChangeViewportPropertiesIfNeeded()
           setup.small_game_graphics);
 #endif
 
+#if 0
     *door_1_x = vp_door_1->x;
     *door_1_y = vp_door_1->y;
     *door_2_x = vp_door_2->x;
     *door_2_y = vp_door_2->y;
+#endif
 
 #if 1
     init_gfx_buffers = TRUE;
@@ -9300,7 +11717,9 @@ void ChangeViewportPropertiesIfNeeded()
     InitGfxBuffers();
 #endif
 
+#if 0
     if (gfx_game_mode == GAME_MODE_MAIN)
+#endif
     {
 #if 1
       init_gadgets_and_toons = TRUE;
@@ -9344,6 +11763,11 @@ void ChangeViewportPropertiesIfNeeded()
     InitToons();
   }
 
+  if (init_em_graphics)
+  {
+      InitGraphicInfo_EM();
+  }
+
 #if 0
   printf("::: %d, %d  /  %d, %d [%d]\n", VX, VY, EX, EY, game_status);
 #endif