rnd-20140205-1-src
[rocksndiamonds.git] / src / tools.c
index ba0ec81ca5ffe036047f565321ccb0f5793cfe47..95f12fc1d5ca157f11fbfea1a3f9ee002e725fc0 100644 (file)
@@ -323,13 +323,6 @@ void BlitScreenToBitmap(Bitmap *target_bitmap)
   fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
 #endif
 
-  /* !!! THIS WORKS !!! */
-
-  printf("::: (%d, %d) [(%d / %d, %d / %d)]\n",
-        scroll_x, scroll_y,
-        SBX_Left, SBX_Right,
-        SBY_Upper, SBY_Lower);
-
   ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
   ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
 
@@ -338,11 +331,11 @@ void BlitScreenToBitmap(Bitmap *target_bitmap)
     if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
     else
-      fx += (dx > 0 ? TILEX_VAR : 0);
+      fx += (dx_var > 0 ? TILEX_VAR : 0);
   }
   else
   {
-    fx += dx;
+    fx += dx_var;
   }
 
   if (EVEN(SCR_FIELDY))
@@ -350,13 +343,21 @@ void BlitScreenToBitmap(Bitmap *target_bitmap)
     if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
     else
-      fy += (dy > 0 ? TILEY_VAR : 0);
+      fy += (dy_var > 0 ? TILEY_VAR : 0);
   }
   else
   {
-    fy += dy;
+    fy += dy_var;
   }
 
+#if 0
+  printf("::: (%d, %d) [(%d / %d, %d / %d)] => %d, %d\n",
+        scroll_x, scroll_y,
+        SBX_Left, SBX_Right,
+        SBY_Upper, SBY_Lower,
+        fx, fy);
+#endif
+
   if (border.draw_masked[GAME_MODE_PLAYING])
   {
     if (buffer != backbuffer)
@@ -394,12 +395,45 @@ void BackToFront()
   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
     redraw_mask |= REDRAW_FIELD;
 
+#if 0
+  // never redraw single tiles, always redraw the whole field
+  // (redrawing single tiles up to a certain threshold was faster on old,
+  // now legacy graphics, but slows things down on modern graphics now)
+  // UPDATE: this is now globally defined by value of REDRAWTILES_THRESHOLD
+  if (redraw_mask & REDRAW_TILES)
+    redraw_mask |= REDRAW_FIELD;
+#endif
+
+#if 0
+  /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+  /* (force full redraw) */
+  if (game_status == GAME_MODE_PLAYING)
+    redraw_mask |= REDRAW_FIELD;
+#endif
+
   if (redraw_mask & REDRAW_FIELD)
     redraw_mask &= ~REDRAW_TILES;
 
   if (redraw_mask == REDRAW_NONE)
     return;
 
+#if 0
+  printf("::: ");
+  if (redraw_mask & REDRAW_ALL)
+    printf("[REDRAW_ALL]");
+  if (redraw_mask & REDRAW_FIELD)
+    printf("[REDRAW_FIELD]");
+  if (redraw_mask & REDRAW_TILES)
+    printf("[REDRAW_TILES]");
+  if (redraw_mask & REDRAW_DOOR_1)
+    printf("[REDRAW_DOOR_1]");
+  if (redraw_mask & REDRAW_DOOR_2)
+    printf("[REDRAW_DOOR_2]");
+  if (redraw_mask & REDRAW_FROM_BACKBUFFER)
+    printf("[REDRAW_FROM_BACKBUFFER]");
+  printf(" [%d]\n", FrameCounter);
+#endif
+
   if (redraw_mask & REDRAW_TILES &&
       game_status == GAME_MODE_PLAYING &&
       border.draw_masked[GAME_MODE_PLAYING])
@@ -596,18 +630,22 @@ void BackToFront()
       int sy = SY; // + (EVEN(SCR_FIELDY) ? TILEY_VAR / 2 : 0);
 
       int dx = 0, dy = 0;
+      int dx_var = dx * TILESIZE_VAR / TILESIZE;
+      int dy_var = dy * TILESIZE_VAR / TILESIZE;
       int ffx, ffy;
       int fx = FX, fy = FY;
 
-      ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx * TILESIZE_VAR / TILESIZE;
-      ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy * TILESIZE_VAR / TILESIZE;
+      int scr_fieldx = SCR_FIELDX + (EVEN(SCR_FIELDX) ? 2 : 0);
+      int scr_fieldy = SCR_FIELDY + (EVEN(SCR_FIELDY) ? 2 : 0);
+
+      ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
+      ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
 
       if (EVEN(SCR_FIELDX))
       {
        if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
        {
-         fx = fx + dx * TILESIZE_VAR / TILESIZE - MIN(ffx, TILEX_VAR / 2) +
-           TILEX_VAR;
+         fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
 
          if (fx % TILEX_VAR)
            sx -= TILEX_VAR / 2;
@@ -616,7 +654,7 @@ void BackToFront()
        }
        else
        {
-         fx = fx - (dx <= 0 ? TILEX_VAR : 0) + TILEX_VAR;
+         fx += (dx_var > 0 ? TILEX_VAR : 0);
        }
       }
 
@@ -624,8 +662,7 @@ void BackToFront()
       {
        if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
        {
-         fy = fy + dy * TILESIZE_VAR / TILESIZE - MIN(ffy, TILEY_VAR / 2) +
-           TILEY_VAR;
+         fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
 
          if (fy % TILEY_VAR)
            sy -= TILEY_VAR / 2;
@@ -634,12 +671,16 @@ void BackToFront()
        }
        else
        {
-         fy = fy - (dy <= 0 ? TILEY_VAR : 0) + TILEY_VAR;
+         fy += (dy_var > 0 ? TILEY_VAR : 0);
        }
       }
 
-      for (x = 0; x < SCR_FIELDX; x++)
-       for (y = 0 ; y < SCR_FIELDY; y++)
+#if 0
+      printf("::: %d, %d, %d, %d\n", sx, sy, SCR_FIELDX, SCR_FIELDY);
+#endif
+
+      for (x = 0; x < scr_fieldx; x++)
+       for (y = 0 ; y < scr_fieldy; y++)
          if (redraw[redraw_x1 + x][redraw_y1 + y])
            BlitBitmap(buffer, window,
                       FX + x * TILEX_VAR, FY + y * TILEY_VAR,
@@ -1046,7 +1087,18 @@ void DrawBackground(int x, int y, int width, int height)
   ClearRectangleOnBackground(backbuffer, x, y, width, height);
 #endif
 
+#if 1
+  /* (this only works for the current arrangement of playfield and panels) */
+  if (x < gfx.dx)
+    redraw_mask |= REDRAW_FIELD;
+  else if (y < gfx.vy)
+    redraw_mask |= REDRAW_DOOR_1;
+  else
+    redraw_mask |= REDRAW_DOOR_2;
+#else
+  /* (this is just wrong (when drawing to one of the two door panel areas)) */
   redraw_mask |= REDRAW_FIELD;
+#endif
 }
 
 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
@@ -2413,49 +2465,57 @@ 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  = MAX(width  - 2 * tile_width,  tile_width);
+  int inner_height = MAX(height - 2 * tile_height, 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;
@@ -2463,7 +2523,7 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
   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 long anim_delay = 0;
+  unsigned int anim_delay = 0;
   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
   int anim_delay_value = (no_delay ? 0 : frame_delay_value);
   int font_nr = FONT_ENVELOPE_1 + envelope_nr;
@@ -2483,27 +2543,32 @@ 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);
 
+#if 1
+    BlitScreenToBitmap(backbuffer);
+#else
     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+#endif
 
     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
@@ -2515,19 +2580,261 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
   }
 }
 
-void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
+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();
+}
+
+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)
+{
+  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;
+  boolean autowrap = FALSE;
+  boolean centered = TRUE;
+  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;
+
+  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, font_nr,
+                line_length, -1, max_lines, line_spacing, mask_mode,
+                autowrap, 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 1
+
+void AnimateEnvelopeRequest(char *text, 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 1
+    redraw_mask = REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    // redraw_mask |= REDRAW_ALL | REDRAW_FROM_BACKBUFFER;
+#else
+    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 long anim_delay = 0;
+  unsigned int anim_delay = 0;
   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
-  int anim_delay_value = (no_delay ? 0 : frame_delay_value);
+  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);
@@ -2536,11 +2843,13 @@ void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
 #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;
+  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 */
@@ -2610,8 +2919,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
@@ -2619,23 +2929,30 @@ void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
 #else
     SetDrawtoField(DRAW_BUFFERED);
 
+#if 1
+    BlitScreenToBitmap(backbuffer);
+#else
     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+#endif
 
     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,
@@ -2643,21 +2960,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,
@@ -2665,8 +2992,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
@@ -2691,61 +3018,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);
@@ -2755,6 +3046,12 @@ 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);
+  char *text_copy = getStringCopy(text);
+  char *text_ptr;
+
+  for (text_ptr = text_copy; *text_ptr; text_ptr++)
+    if (*text_ptr == ' ')
+      *text_ptr = '\n';
 
 #if 1
   if (game_status == GAME_MODE_PLAYING)
@@ -2777,24 +3074,45 @@ 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();
-  }
-
-  /* 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)
+#if 1
+  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
+
+    DrawEnvelopeRequest(text_copy);
+
+    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)
   {
     PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
 
     if (anim_mode == ANIM_DEFAULT)
-      AnimateEnvelopeDoor(text, ANIM_DEFAULT, ACTION_OPENING);
+      AnimateEnvelopeRequest(text, ANIM_DEFAULT, ACTION_OPENING);
 
-    AnimateEnvelopeDoor(text, main_anim_mode, ACTION_OPENING);
+    AnimateEnvelopeRequest(text, main_anim_mode, ACTION_OPENING);
 
 #if 0
     if (tape.playing)
@@ -2808,10 +3126,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(text, main_anim_mode, ACTION_CLOSING);
 
     if (anim_mode == ANIM_DEFAULT)
-      AnimateEnvelopeDoor(text, ANIM_DEFAULT, ACTION_CLOSING);
+      AnimateEnvelopeRequest(text, ANIM_DEFAULT, ACTION_CLOSING);
   }
 
   game.envelope_active = FALSE;
@@ -2848,12 +3166,22 @@ void ShowEnvelopeDoor(char *text, int action)
   /* (important: after "BackToFront()", but before "SetDrawtoField()") */
   game_status = last_game_status;      /* restore current game status */
 
+#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
+
+  free(text_copy);
 }
 
 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
@@ -2897,7 +3225,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);
@@ -3033,13 +3361,13 @@ static void DrawPreviewLevelLabelExt(int mode)
   redraw_mask |= REDRAW_MICROLEVEL;
 }
 
-void DrawPreviewLevel(boolean restart)
+static void DrawPreviewLevelExt(boolean restart)
 {
-  static unsigned long scroll_delay = 0;
-  static unsigned long label_delay = 0;
+  static unsigned int scroll_delay = 0;
+  static unsigned int label_delay = 0;
   static int from_x, from_y, scroll_direction;
   static int label_state, label_counter;
-  unsigned long scroll_delay_value = preview.step_delay;
+  unsigned int scroll_delay_value = preview.step_delay;
   boolean show_level_border = (BorderElement != EL_EMPTY);
   int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
   int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
@@ -3070,7 +3398,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 */
@@ -3172,7 +3500,7 @@ void DrawPreviewLevel(boolean restart)
        break;
     }
 
-    DrawPreviewLevelExt(from_x, from_y);
+    DrawPreviewLevelPlayfieldExt(from_x, from_y);
   }
 
   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
@@ -3219,6 +3547,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)
 {
@@ -3669,139 +4007,682 @@ void DrawPlayer(struct PlayerInfo *player)
 
   if (player->GfxPos)
   {
-    if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
-      sxx = player->GfxPos;
-    else
-      syy = player->GfxPos;
+    if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
+      sxx = player->GfxPos;
+    else
+      syy = player->GfxPos;
+  }
+
+  if (!setup.soft_scrolling && ScreenMovPos)
+    sxx = syy = 0;
+
+  if (player_is_opaque)
+    DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
+  else
+    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
+
+  if (SHIELD_ON(player))
+  {
+    int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
+                  IMG_SHIELD_NORMAL_ACTIVE);
+    int frame = getGraphicAnimationFrame(graphic, -1);
+
+    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
+  }
+#endif
+
+  /* ----------------------------------------------------------------------- */
+  /* draw things in front of player (active dynamite or dynabombs)           */
+  /* ----------------------------------------------------------------------- */
+
+  if (IS_ACTIVE_BOMB(element))
+  {
+    graphic = el2img(element);
+    frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
+
+    if (game.emulation == EMU_SUPAPLEX)
+      DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
+    else
+      DrawGraphicThruMask(sx, sy, graphic, frame);
+  }
+
+  if (player_is_moving && last_element == EL_EXPLOSION)
+  {
+    int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
+                  GfxElement[last_jx][last_jy] :  EL_EMPTY);
+    int graphic = el_act2img(element, ACTION_EXPLODING);
+    int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
+    int phase = ExplodePhase[last_jx][last_jy] - 1;
+    int frame = getGraphicAnimationFrame(graphic, phase - delay);
+
+    if (phase >= delay)
+      DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
+  }
+
+  /* ----------------------------------------------------------------------- */
+  /* draw elements the player is just walking/passing through/under          */
+  /* ----------------------------------------------------------------------- */
+
+  if (player_is_moving)
+  {
+    /* handle the field the player is leaving ... */
+    if (IS_ACCESSIBLE_INSIDE(last_element))
+      DrawLevelField(last_jx, last_jy);
+    else if (IS_ACCESSIBLE_UNDER(last_element))
+      DrawLevelFieldThruMask(last_jx, last_jy);
+  }
+
+  /* do not redraw accessible elements if the player is just pushing them */
+  if (!player_is_moving || !player->is_pushing)
+  {
+    /* ... and the field the player is entering */
+    if (IS_ACCESSIBLE_INSIDE(element))
+      DrawLevelField(jx, jy);
+    else if (IS_ACCESSIBLE_UNDER(element))
+      DrawLevelFieldThruMask(jx, jy);
+  }
+
+  MarkTileDirty(sx, sy);
+}
+
+/* ------------------------------------------------------------------------- */
+
+void WaitForEventToContinue()
+{
+  boolean still_wait = TRUE;
+
+  /* simulate releasing mouse button over last gadget, if still pressed */
+  if (button_status)
+    HandleGadgets(-1, -1, 0);
+
+  button_status = MB_RELEASED;
+
+#if 1
+  ClearEventQueue();
+#endif
+
+  while (still_wait)
+  {
+    if (PendingEvent())
+    {
+      Event event;
+
+      NextEvent(&event);
+
+      switch (event.type)
+      {
+       case EVENT_BUTTONPRESS:
+       case EVENT_KEYPRESS:
+         still_wait = FALSE;
+         break;
+
+       case EVENT_KEYRELEASE:
+         ClearPlayerAction();
+         break;
+
+       default:
+         HandleOtherEvents(&event);
+         break;
+      }
+    }
+    else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
+    {
+      still_wait = FALSE;
+    }
+
+    DoAnimation();
+
+    /* don't eat all CPU time */
+    Delay(10);
+  }
+}
+
+#define MAX_REQUEST_LINES              13
+#define MAX_REQUEST_LINE_FONT1_LEN     7
+#define MAX_REQUEST_LINE_FONT2_LEN     10
+
+#if 1
+
+static int RequestHandleEvents(unsigned int req_state)
+{
+  int last_game_status = game_status;  /* save current game status */
+  int result;
+  int mx, my;
+
+  button_status = MB_RELEASED;
+
+  request_gadget_id = -1;
+  result = -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;
+         }
+
+         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
+  }
+
+  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 (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      BlitScreenToBitmap_EM(backbuffer);
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      BlitScreenToBitmap_SP(backbuffer);
+  }
+
+  /* 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 */
+    BlitBitmap(bitmap_db_door, bitmap_db_door,
+              DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
+              DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
+  }
+
+  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;
+
+    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 (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);
+
+  OpenDoor(DOOR_OPEN_1);
+
+  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 (game_status != GAME_MODE_MAIN)
+    InitAnimation();
+
+  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
+
+  // ---------- handle request buttons ----------
+  result = RequestHandleEvents(req_state);
+
+  if (game_status != GAME_MODE_MAIN)
+    StopAnimation();
+
+  UnmapToolButtons();
+
+  if (!(req_state & REQ_STAY_OPEN))
+  {
+    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;
+}
+
+static boolean RequestEnvelope(char *text, unsigned int req_state)
+{
+  int result;
+#if 0
+  int i;
+#endif
+
+  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);
+  }
+
+  /* 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]);
   }
-
-  if (!setup.soft_scrolling && ScreenMovPos)
-    sxx = syy = 0;
-
-  if (player_is_opaque)
-    DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
-  else
-    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
-
-  if (SHIELD_ON(player))
+  else if (req_state & REQ_PLAYER)
   {
-    int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
-                  IMG_SHIELD_NORMAL_ACTIVE);
-    int frame = getGraphicAnimationFrame(graphic, -1);
-
-    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
+    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
 
-  /* ----------------------------------------------------------------------- */
-  /* draw things in front of player (active dynamite or dynabombs)           */
-  /* ----------------------------------------------------------------------- */
+  ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
 
-  if (IS_ACTIVE_BOMB(element))
+#if 0
+  for (i = 0; i < NUM_TOOL_BUTTONS; i++)
   {
-    graphic = el2img(element);
-    frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
+    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;
 
-    if (game.emulation == EMU_SUPAPLEX)
-      DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
-    else
-      DrawGraphicThruMask(sx, sy, graphic, frame);
+      ModifyGadget(tool_gadget[i], GDI_X, x, GDI_Y, y, GDI_END);
+    }
   }
+#endif
 
-  if (player_is_moving && last_element == EL_EXPLOSION)
+  if (!(req_state & REQUEST_WAIT_FOR_INPUT))
   {
-    int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
-                  GfxElement[last_jx][last_jy] :  EL_EMPTY);
-    int graphic = el_act2img(element, ACTION_EXPLODING);
-    int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
-    int phase = ExplodePhase[last_jx][last_jy] - 1;
-    int frame = getGraphicAnimationFrame(graphic, phase - delay);
-
-    if (phase >= delay)
-      DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
-  }
-
-  /* ----------------------------------------------------------------------- */
-  /* draw elements the player is just walking/passing through/under          */
-  /* ----------------------------------------------------------------------- */
+    if (game_status == GAME_MODE_PLAYING)
+    {
+      SetPanelBackground();
+      SetDrawBackgroundMask(REDRAW_DOOR_1);
+    }
+    else
+    {
+      SetDrawBackgroundMask(REDRAW_FIELD);
+    }
 
-  if (player_is_moving)
-  {
-    /* handle the field the player is leaving ... */
-    if (IS_ACCESSIBLE_INSIDE(last_element))
-      DrawLevelField(last_jx, last_jy);
-    else if (IS_ACCESSIBLE_UNDER(last_element))
-      DrawLevelFieldThruMask(last_jx, last_jy);
+    return FALSE;
   }
 
-  /* do not redraw accessible elements if the player is just pushing them */
-  if (!player_is_moving || !player->is_pushing)
-  {
-    /* ... and the field the player is entering */
-    if (IS_ACCESSIBLE_INSIDE(element))
-      DrawLevelField(jx, jy);
-    else if (IS_ACCESSIBLE_UNDER(element))
-      DrawLevelFieldThruMask(jx, jy);
-  }
+#if 0
+  if (game_status != GAME_MODE_MAIN)
+    InitAnimation();
+#endif
 
-  MarkTileDirty(sx, sy);
-}
+  SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
 
-/* ------------------------------------------------------------------------- */
+  // ---------- handle request buttons ----------
+  result = RequestHandleEvents(req_state);
 
-void WaitForEventToContinue()
-{
-  boolean still_wait = TRUE;
+  if (game_status != GAME_MODE_MAIN)
+    StopAnimation();
 
-  /* simulate releasing mouse button over last gadget, if still pressed */
-  if (button_status)
-    HandleGadgets(-1, -1, 0);
+  UnmapToolButtons();
 
-  button_status = MB_RELEASED;
+  ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
 
-#if 1
-  ClearEventQueue();
-#endif
+  RemapAllGadgets();
 
-  while (still_wait)
+  if (game_status == GAME_MODE_PLAYING)
   {
-    if (PendingEvent())
-    {
-      Event event;
-
-      NextEvent(&event);
-
-      switch (event.type)
-      {
-       case EVENT_BUTTONPRESS:
-       case EVENT_KEYPRESS:
-         still_wait = FALSE;
-         break;
+    SetPanelBackground();
+    SetDrawBackgroundMask(REDRAW_DOOR_1);
+  }
+  else
+  {
+    SetDrawBackgroundMask(REDRAW_FIELD);
+  }
 
-       case EVENT_KEYRELEASE:
-         ClearPlayerAction();
-         break;
+#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
 
-       default:
-         HandleOtherEvents(&event);
-         break;
-      }
-    }
-    else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
-    {
-      still_wait = FALSE;
-    }
+  /* restore deactivated drawing when quick-loading level tape recording */
+  if (tape.playing && tape.deactivate_display)
+    TapeDeactivateDisplayOn();
 
-    DoAnimation();
+  return result;
+}
 
-    /* don't eat all CPU time */
-    Delay(10);
-  }
+boolean Request(char *text, unsigned int req_state)
+{
+  if (global.use_envelope_request)
+    return RequestEnvelope(text, req_state);
+  else
+    return RequestDoor(text, req_state);
 }
 
-#define MAX_REQUEST_LINES              13
-#define MAX_REQUEST_LINE_FONT1_LEN     7
-#define MAX_REQUEST_LINE_FONT2_LEN     10
+#else  // =====================================================================
 
 boolean Request(char *text, unsigned int req_state)
 {
@@ -3816,8 +4697,8 @@ boolean Request(char *text, unsigned int req_state)
   char *text_ptr;
   int i;
 
-#if 1
-  global.use_envelope_request = 0;
+#if 0
+  global.use_envelope_request = 1;
 #endif
 
 #if 1
@@ -3978,7 +4859,7 @@ boolean Request(char *text, unsigned int req_state)
 #if 1
   if (global.use_envelope_request)
   {
-    ShowEnvelopeDoor(text, ACTION_OPENING);
+    ShowEnvelopeRequest(text, ACTION_OPENING);
 
     for (i = 0; i < NUM_TOOL_BUTTONS; i++)
     {
@@ -4120,6 +5001,9 @@ boolean Request(char *text, unsigned int req_state)
              break;
 
            case KSYM_Escape:
+#if defined(TARGET_SDL2)
+           case KSYM_Back:
+#endif
              result = 0;
              break;
 
@@ -4196,7 +5080,7 @@ boolean Request(char *text, unsigned int req_state)
 
 #if 1
   if (global.use_envelope_request)
-    ShowEnvelopeDoor(text, ACTION_CLOSING);
+    ShowEnvelopeRequest(text, ACTION_CLOSING);
 #endif
 
 #if 1
@@ -4239,6 +5123,8 @@ boolean Request(char *text, unsigned int req_state)
   return result;
 }
 
+#endif
+
 unsigned int OpenDoor(unsigned int door_state)
 {
   if (door_state & DOOR_COPY_BACK)
@@ -4293,8 +5179,8 @@ unsigned int MoveDoor(unsigned int door_state)
 {
   static int door1 = DOOR_OPEN_1;
   static int door2 = DOOR_CLOSE_2;
-  unsigned long door_delay = 0;
-  unsigned long door_delay_value;
+  unsigned int door_delay = 0;
+  unsigned int door_delay_value;
   int stepsize = 1;
 
   if (door_1.width < 0 || door_1.width > DXSIZE)
@@ -4604,6 +5490,107 @@ void UndrawSpecialEditorDoor()
 
 /* ---------- new tool button stuff ---------------------------------------- */
 
+#if 1
+
+static struct
+{
+  int graphic;
+  struct TextPosInfo *pos;
+  int gadget_id;
+  char *infotext;
+} toolbutton_info[NUM_TOOL_BUTTONS] =
+{
+  {
+    IMG_REQUEST_BUTTON_GFX_YES,                &request.button.yes,
+    TOOL_CTRL_ID_YES,                  "yes"
+  },
+  {
+    IMG_REQUEST_BUTTON_GFX_NO,         &request.button.no,
+    TOOL_CTRL_ID_NO,                   "no"
+  },
+  {
+    IMG_REQUEST_BUTTON_GFX_CONFIRM,    &request.button.confirm,
+    TOOL_CTRL_ID_CONFIRM,              "confirm"
+  },
+  {
+    IMG_REQUEST_BUTTON_GFX_PLAYER_1,   &request.button.player_1,
+    TOOL_CTRL_ID_PLAYER_1,             "player 1"
+  },
+  {
+    IMG_REQUEST_BUTTON_GFX_PLAYER_2,   &request.button.player_2,
+    TOOL_CTRL_ID_PLAYER_2,             "player 2"
+  },
+  {
+    IMG_REQUEST_BUTTON_GFX_PLAYER_3,   &request.button.player_3,
+    TOOL_CTRL_ID_PLAYER_3,             "player 3"
+  },
+  {
+    IMG_REQUEST_BUTTON_GFX_PLAYER_4,   &request.button.player_4,
+    TOOL_CTRL_ID_PLAYER_4,             "player 4"
+  }
+};
+
+void CreateToolButtons()
+{
+  int i;
+
+  for (i = 0; i < NUM_TOOL_BUTTONS; i++)
+  {
+    struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
+    struct TextPosInfo *pos = toolbutton_info[i].pos;
+    struct GadgetInfo *gi;
+    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 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;
+
+      getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
+                           pos->size, &deco_bitmap, &deco_x, &deco_y);
+      deco_xpos = (gfx->width  - pos->size) / 2;
+      deco_ypos = (gfx->height - pos->size) / 2;
+    }
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, toolbutton_info[i].infotext,
+                     GDI_X, dx + pos->x,
+                     GDI_Y, dy + pos->y,
+                     GDI_WIDTH, gfx->width,
+                     GDI_HEIGHT, gfx->height,
+                     GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
+                     GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
+                     GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
+                     GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
+                     GDI_DECORATION_SIZE, pos->size, pos->size,
+                     GDI_DECORATION_SHIFTING, 1, 1,
+                     GDI_DIRECT_DRAW, FALSE,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleToolButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    tool_gadget[id] = gi;
+  }
+}
+
+#else
+
 /* graphic position values for tool buttons */
 #define TOOL_BUTTON_YES_XPOS           2
 #define TOOL_BUTTON_YES_YPOS           250
@@ -4713,7 +5700,7 @@ void CreateToolButtons()
     Bitmap *deco_bitmap = None;
     int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
     struct GadgetInfo *gi;
-    unsigned long event_mask;
+    unsigned int event_mask;
     int gd_xoffset, gd_yoffset;
     int gd_x1, gd_x2, gd_y;
     int id = i;
@@ -4762,6 +5749,8 @@ void CreateToolButtons()
   }
 }
 
+#endif
+
 void FreeToolButtons()
 {
   int i;
@@ -7196,7 +8185,7 @@ int getGameFrameDelay_EM(int native_em_game_frame_delay)
   return game_frame_delay_value;
 }
 
-unsigned int InitRND(long seed)
+unsigned int InitRND(int seed)
 {
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
     return InitEngineRandom_EM(seed);
@@ -8788,15 +9777,16 @@ void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
                            boolean any_player_moving,
                            boolean player_is_dropping)
 {
-  int i;
-
   if (tape.single_step && tape.recording && !tape.pausing)
   {
+#if 0
     boolean active_players = FALSE;
+    int i;
 
     for (i = 0; i < MAX_PLAYERS; i++)
       if (action[i] != JOY_NO_ACTION)
        active_players = TRUE;
+#endif
 
     // if (frame == 0)
     if (frame == 0 && !player_is_dropping)
@@ -8927,18 +9917,44 @@ void PlaySoundSelecting()
 #endif
 }
 
-void ToggleFullscreenIfNeeded()
+void ToggleFullscreenOrChangeWindowScalingIfNeeded()
 {
   boolean change_fullscreen = (setup.fullscreen !=
                               video.fullscreen_enabled);
   boolean change_fullscreen_mode = (video.fullscreen_enabled &&
                                    !strEqual(setup.fullscreen_mode,
                                              video.fullscreen_mode_current));
+  boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
+                                          setup.window_scaling_percent !=
+                                          video.window_scaling_percent);
+
+  if (change_window_scaling_percent && video.fullscreen_enabled)
+    return;
+
+  if (!change_window_scaling_percent && !video.fullscreen_available)
+    return;
+
+#if defined(TARGET_SDL2)
+  if (change_window_scaling_percent)
+  {
+    SDLSetWindowScaling(setup.window_scaling_percent);
+
+    return;
+  }
+  else if (change_fullscreen)
+  {
+    SDLSetWindowFullscreen(setup.fullscreen);
+
+    /* set setup value according to successfully changed fullscreen mode */
+    setup.fullscreen = video.fullscreen_enabled;
 
-  if (!video.fullscreen_available)
     return;
+  }
+#endif
 
-  if (change_fullscreen || change_fullscreen_mode)
+  if (change_fullscreen ||
+      change_fullscreen_mode ||
+      change_window_scaling_percent)
   {
     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
 
@@ -8951,9 +9967,16 @@ void ToggleFullscreenIfNeeded()
       video.fullscreen_enabled = FALSE;                /* force new fullscreen mode */
     }
 
+    if (change_window_scaling_percent)
+    {
+      /* keep window mode, but change window scaling */
+      video.fullscreen_enabled = TRUE;         /* force new window scaling */
+    }
+
     /* toggle fullscreen */
     ChangeVideoModeIfNeeded(setup.fullscreen);
 
+    /* set setup value according to successfully changed fullscreen mode */
     setup.fullscreen = video.fullscreen_enabled;
 
     /* restore backbuffer content from temporary backbuffer backup bitmap */