rnd-20140114-1-src
[rocksndiamonds.git] / src / tools.c
index aacba67058f44f8bebadf1863a52c87cb4bd551e..f7735c70b012d6d945adeef60a57266b63f955a9 100644 (file)
@@ -14,6 +14,7 @@
 #include "libgame/libgame.h"
 
 #include "tools.h"
+#include "init.h"
 #include "game.h"
 #include "events.h"
 #include "cartoons.h"
@@ -107,6 +108,37 @@ void SetDrawtoField(int mode)
 {
   if (mode == DRAW_BUFFERED && setup.soft_scrolling)
   {
+#if NEW_TILESIZE
+#if NEW_SCROLL
+    FX = 2 * TILEX_VAR;
+    FY = 2 * TILEY_VAR;
+    BX1 = -2;
+    BY1 = -2;
+    BX2 = SCR_FIELDX + 1;
+    BY2 = SCR_FIELDY + 1;
+    redraw_x1 = 2;
+    redraw_y1 = 2;
+#else
+    FX = TILEX_VAR;
+    FY = TILEY_VAR;
+    BX1 = -1;
+    BY1 = -1;
+    BX2 = SCR_FIELDX;
+    BY2 = SCR_FIELDY;
+    redraw_x1 = 1;
+    redraw_y1 = 1;
+#endif
+#else
+#if NEW_SCROLL
+    FX = 2 * TILEX;
+    FY = 2 * TILEY;
+    BX1 = -2;
+    BY1 = -2;
+    BX2 = SCR_FIELDX + 1;
+    BY2 = SCR_FIELDY + 1;
+    redraw_x1 = 2;
+    redraw_y1 = 2;
+#else
     FX = TILEX;
     FY = TILEY;
     BX1 = -1;
@@ -115,6 +147,8 @@ void SetDrawtoField(int mode)
     BY2 = SCR_FIELDY;
     redraw_x1 = 1;
     redraw_y1 = 1;
+#endif
+#endif
 
     drawto_field = fieldbuffer;
   }
@@ -250,6 +284,11 @@ void DrawMaskedBorder(int redraw_mask)
       effectiveGameStatus() == GAME_MODE_TITLE)
     return;
 
+  /* never draw masked screen borders when displaying request outside door */
+  if (effectiveGameStatus() == GAME_MODE_PSEUDO_DOOR &&
+      global.use_envelope_request)
+    return;
+
   if (redraw_mask & REDRAW_ALL)
     DrawMaskedBorder_ALL();
   else
@@ -265,6 +304,79 @@ void DrawMaskedBorder(int redraw_mask)
   }
 }
 
+void BlitScreenToBitmap(Bitmap *target_bitmap)
+{
+  DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
+  int fx = FX, fy = FY;
+
+#if NEW_TILESIZE
+  int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
+  int dy = (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
+  int dx_var = dx * TILESIZE_VAR / TILESIZE;
+  int dy_var = dy * TILESIZE_VAR / TILESIZE;
+  int ffx, ffy;
+
+  // fx += dx * TILESIZE_VAR / TILESIZE;
+  // fy += dy * TILESIZE_VAR / TILESIZE;
+#else
+  fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
+  fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
+#endif
+
+  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 += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
+    else
+      fx += (dx_var > 0 ? TILEX_VAR : 0);
+  }
+  else
+  {
+    fx += dx_var;
+  }
+
+  if (EVEN(SCR_FIELDY))
+  {
+    if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
+      fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
+    else
+      fy += (dy_var > 0 ? TILEY_VAR : 0);
+  }
+  else
+  {
+    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)
+    {
+      /* copy playfield buffer to backbuffer to add masked border */
+      BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
+      DrawMaskedBorder(REDRAW_FIELD);
+    }
+
+    BlitBitmap(backbuffer, target_bitmap,
+              REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+              REAL_SX, REAL_SY);
+  }
+  else
+  {
+    BlitBitmap(buffer, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
+  }
+}
+
 void BackToFront()
 {
   int x, y;
@@ -283,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])
@@ -331,7 +476,7 @@ void BackToFront()
 
   SyncDisplay();
 
-  /* prevent drawing masked border to backbuffer when using playfield buffer */
+  /* never draw masked border to backbuffer when using playfield buffer */
   if (game_status != GAME_MODE_PLAYING ||
       redraw_mask & REDRAW_FROM_BACKBUFFER ||
       buffer == backbuffer)
@@ -348,6 +493,10 @@ void BackToFront()
 
   if (redraw_mask & REDRAW_FIELD)
   {
+#if 0
+    printf("::: REDRAW_FIELD\n");
+#endif
+
     if (game_status != GAME_MODE_PLAYING ||
        redraw_mask & REDRAW_FROM_BACKBUFFER)
     {
@@ -356,51 +505,88 @@ void BackToFront()
     }
     else
     {
+#if 1
+      BlitScreenToBitmap(window);
+#else
       int fx = FX, fy = FY;
 
-      if (setup.soft_scrolling)
+#if NEW_TILESIZE
+      int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
+      int dy = (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
+      int dx_var = dx * TILESIZE_VAR / TILESIZE;
+      int dy_var = dy * TILESIZE_VAR / TILESIZE;
+      int ffx, ffy;
+
+      // fx += dx * TILESIZE_VAR / TILESIZE;
+      // fy += dy * TILESIZE_VAR / TILESIZE;
+#else
+      fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
+      fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
+#endif
+
+      /* !!! THIS WORKS !!! */
+
+      printf("::: %d, %d\n", scroll_x, scroll_y);
+
+      ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
+      ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
+
+      if (EVEN(SCR_FIELDX))
       {
-       fx += (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
-       fy += (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
+       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);
       }
-
-      if (setup.soft_scrolling ||
-         ABS(ScreenMovPos) + ScrollStepSize == TILEX ||
-         ABS(ScreenMovPos) == ScrollStepSize ||
-         redraw_tiles > REDRAWTILES_THRESHOLD)
+      else
       {
-       if (border.draw_masked[GAME_MODE_PLAYING])
-       {
-         if (buffer != backbuffer)
-         {
-           /* copy playfield buffer to backbuffer to add masked border */
-           BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
-           DrawMaskedBorder(REDRAW_FIELD);
-         }
+       fx += dx;
+      }
 
-         BlitBitmap(backbuffer, window,
-                    REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
-                    REAL_SX, REAL_SY);
-       }
+      if (EVEN(SCR_FIELDY))
+      {
+       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);
+      }
+      else
+      {
+       fy += dy;
+      }
+
+      if (border.draw_masked[GAME_MODE_PLAYING])
+      {
+       if (buffer != backbuffer)
        {
-         BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+         /* copy playfield buffer to backbuffer to add masked border */
+         BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
+         DrawMaskedBorder(REDRAW_FIELD);
        }
 
+       BlitBitmap(backbuffer, window,
+                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+                  REAL_SX, REAL_SY);
+      }
+      else
+      {
+       BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+      }
+#endif
+
 #if 0
 #ifdef DEBUG
-       printf("redrawing all (ScreenGfxPos == %d) because %s\n",
-              ScreenGfxPos,
-              (setup.soft_scrolling ?
-               "setup.soft_scrolling" :
-               ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
-               "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
-               ABS(ScreenGfxPos) == ScrollStepSize ?
-               "ABS(ScreenGfxPos) == ScrollStepSize" :
-               "redraw_tiles > REDRAWTILES_THRESHOLD"));
+      printf("redrawing all (ScreenGfxPos == %d) because %s\n",
+            ScreenGfxPos,
+            (setup.soft_scrolling ?
+             "setup.soft_scrolling" :
+             ABS(ScreenGfxPos) + ScrollStepSize == TILEX ?
+             "ABS(ScreenGfxPos) + ScrollStepSize == TILEX" :
+             ABS(ScreenGfxPos) == ScrollStepSize ?
+             "ABS(ScreenGfxPos) == ScrollStepSize" :
+             "redraw_tiles > REDRAWTILES_THRESHOLD"));
 #endif
 #endif
-      }
     }
 
     redraw_mask &= ~REDRAW_MAIN;
@@ -430,12 +616,97 @@ void BackToFront()
 
   if (redraw_mask & REDRAW_TILES)
   {
+#if 0
+    printf("::: REDRAW_TILES\n");
+#endif
+
+#if NEW_TILESIZE
+
+#if 1
+    InitGfxClipRegion(TRUE, SX, SY, SXSIZE, SYSIZE);
+
+    {
+      int sx = SX; // - (EVEN(SCR_FIELDX) ? TILEX_VAR / 2 : 0);
+      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;
+
+      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 += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
+
+         if (fx % TILEX_VAR)
+           sx -= TILEX_VAR / 2;
+         else
+           sx -= TILEX_VAR;
+       }
+       else
+       {
+         fx += (dx_var > 0 ? TILEX_VAR : 0);
+       }
+      }
+
+      if (EVEN(SCR_FIELDY))
+      {
+       if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
+       {
+         fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
+
+         if (fy % TILEY_VAR)
+           sy -= TILEY_VAR / 2;
+         else
+           sy -= TILEY_VAR;
+       }
+       else
+       {
+         fy += (dy_var > 0 ? TILEY_VAR : 0);
+       }
+      }
+
+#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,
+                      TILEX_VAR, TILEY_VAR,
+                      sx + x * TILEX_VAR, sy + y * TILEY_VAR);
+    }
+
+    InitGfxClipRegion(FALSE, -1, -1, -1, -1);
+#else
+    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,
+                    TILEX_VAR, TILEY_VAR,
+                    SX + x * TILEX_VAR, SY + y * TILEY_VAR);
+#endif
+
+#else
     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, FY + y * TILEY, TILEX, TILEY,
                     SX + x * TILEX, SY + y * TILEY);
+#endif
   }
 
   if (redraw_mask & REDRAW_FPS)                /* display frames per second */
@@ -786,8 +1057,22 @@ void SetDoorBackgroundImage(int graphic)
 
 void SetPanelBackground()
 {
+#if 1
+  struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
+
+#if 1
+  BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
+                 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
+#else
+  /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
+  ClearRectangle(bitmap_db_panel, DX, DY, DXSIZE, DYSIZE);
+  BlitBitmap(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
+            MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), 0, 0);
+#endif
+#else
   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, bitmap_db_panel,
              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, 0, 0);
+#endif
 
   SetDoorBackgroundBitmap(bitmap_db_panel);
 }
@@ -802,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)
@@ -923,8 +1219,9 @@ inline int getGraphicAnimationFrame(int graphic, int sync_frame)
                           sync_frame);
 }
 
-void getSizedGraphicSource(int graphic, int frame, int tilesize_raw,
-                          Bitmap **bitmap, int *x, int *y)
+void getSizedGraphicSourceExt(int graphic, int frame, int tilesize_raw,
+                             Bitmap **bitmap, int *x, int *y,
+                             boolean get_backside)
 {
   struct
   {
@@ -950,8 +1247,15 @@ void getSizedGraphicSource(int graphic, int frame, int tilesize_raw,
   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;
+#if NEW_TILESIZE
+  int src_x = (g->src_x + (get_backside ? g->offset2_x : 0)) *
+    tilesize / TILESIZE;
+  int src_y = (g->src_y + (get_backside ? g->offset2_y : 0)) *
+    tilesize / TILESIZE;
+#else
   int src_x = g->src_x * tilesize / TILESIZE;
   int src_y = g->src_y * tilesize / TILESIZE;
+#endif
   int width = g->width * tilesize / TILESIZE;
   int height = g->height * tilesize / TILESIZE;
   int offset_x = g->offset_x * tilesize / TILESIZE;
@@ -984,6 +1288,25 @@ void getSizedGraphicSource(int graphic, int frame, int tilesize_raw,
   *y = starty + src_y;
 }
 
+void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
+                             int *x, int *y, boolean get_backside)
+{
+  getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
+                          get_backside);
+}
+
+void getSizedGraphicSource(int graphic, int frame, int tilesize_raw,
+                          Bitmap **bitmap, int *x, int *y)
+{
+  getSizedGraphicSourceExt(graphic, frame, tilesize_raw, bitmap, x, y, FALSE);
+}
+
+void getFixedGraphicSource(int graphic, int frame,
+                          Bitmap **bitmap, int *x, int *y)
+{
+  getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
+}
+
 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
 {
 #if 1
@@ -1006,6 +1329,12 @@ inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
 
+#if NEW_TILESIZE
+  if (TILESIZE_VAR != TILESIZE)
+    return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
+                                   get_backside);
+#endif
+
   *bitmap = g->bitmap;
 
   if (g->offset_y == 0)                /* frames are ordered horizontally */
@@ -1047,7 +1376,28 @@ void DrawGraphic(int x, int y, int graphic, int frame)
   }
 #endif
 
+#if NEW_TILESIZE
+  DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
+                frame);
+#else
   DrawGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic, frame);
+#endif
+  MarkTileDirty(x, y);
+}
+
+void DrawFixedGraphic(int x, int y, int graphic, int frame)
+{
+#if DEBUG
+  if (!IN_SCR_FIELD(x, y))
+  {
+    printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
+    printf("DrawGraphic(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
+                     frame);
   MarkTileDirty(x, y);
 }
 
@@ -1058,6 +1408,20 @@ void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
   int src_x, src_y;
 
   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+#if NEW_TILESIZE
+  BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
+#else
+  BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
+#endif
+}
+
+void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
+                        int frame)
+{
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+
+  getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
   BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
 }
 
@@ -1072,8 +1436,29 @@ void DrawGraphicThruMask(int x, int y, int graphic, int frame)
   }
 #endif
 
-  DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y *TILEY, graphic,
+#if NEW_TILESIZE
+  DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
+                        graphic, frame);
+#else
+  DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
                         frame);
+#endif
+  MarkTileDirty(x, y);
+}
+
+void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
+{
+#if DEBUG
+  if (!IN_SCR_FIELD(x, y))
+  {
+    printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
+    printf("DrawGraphicThruMask(): This should never happen!\n");
+    return;
+  }
+#endif
+
+  DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
+                             graphic, frame);
   MarkTileDirty(x, y);
 }
 
@@ -1085,6 +1470,24 @@ void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
 
   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
 
+  SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+               dst_x - src_x, dst_y - src_y);
+#if NEW_TILESIZE
+  BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
+                  dst_x, dst_y);
+#else
+  BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dst_x, dst_y);
+#endif
+}
+
+void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
+                                int graphic, int frame)
+{
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+
+  getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+
   SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
                dst_x - src_x, dst_y - src_y);
   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dst_x, dst_y);
@@ -1205,6 +1608,15 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
   }
 #endif
 
+#if NEW_TILESIZE
+  width = width * TILESIZE_VAR / TILESIZE;
+  height = height * TILESIZE_VAR / TILESIZE;
+  cx = cx * TILESIZE_VAR / TILESIZE;
+  cy = cy * TILESIZE_VAR / TILESIZE;
+  dx = dx * TILESIZE_VAR / TILESIZE;
+  dy = dy * TILESIZE_VAR / TILESIZE;
+#endif
+
   if (width > 0 && height > 0)
   {
     getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
@@ -1212,8 +1624,13 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
     src_x += cx;
     src_y += cy;
 
+#if NEW_TILESIZE
+    dst_x = FX + x * TILEX_VAR + dx;
+    dst_y = FY + y * TILEY_VAR + dy;
+#else
     dst_x = FX + x * TILEX + dx;
     dst_y = FY + y * TILEY + dy;
+#endif
 
     if (mask_mode == USE_MASKING)
     {
@@ -1237,7 +1654,11 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
   Bitmap *src_bitmap;
   int src_x, src_y;
   int dst_x, dst_y;
+#if NEW_TILESIZE
+  int width = TILEX_VAR, height = TILEY_VAR;
+#else
   int width = TILEX, height = TILEY;
+#endif
   int x1 = x;
   int y1 = y;
   int x2 = x + SIGN(dx);
@@ -1284,8 +1705,13 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
   {
     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
 
+#if NEW_TILESIZE
+    dst_x = FX + x1 * TILEX_VAR;
+    dst_y = FY + y1 * TILEY_VAR;
+#else
     dst_x = FX + x1 * TILEX;
     dst_y = FY + y1 * TILEY;
+#endif
 
     if (mask_mode == USE_MASKING)
     {
@@ -1306,8 +1732,13 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
   {
     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
 
+#if NEW_TILESIZE
+    dst_x = FX + x2 * TILEX_VAR;
+    dst_y = FY + y2 * TILEY_VAR;
+#else
     dst_x = FX + x2 * TILEX;
     dst_y = FY + y2 * TILEY;
+#endif
 
     if (mask_mode == USE_MASKING)
     {
@@ -1442,39 +1873,254 @@ void DrawLevelFieldThruMask(int x, int y)
                             (e) == EL_QUICKSAND_EMPTYING ||            \
                             (e) == EL_QUICKSAND_FAST_EMPTYING))
 
-static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
+static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
+                                              int graphic)
 {
   Bitmap *src_bitmap;
   int src_x, src_y;
+  int width, height, cx, cy;
   int sx = SCREENX(x), sy = SCREENY(y);
-  int element;
-  int width, height, cx, cy, i;
   int crumbled_border_size = graphic_info[graphic].border_size;
-  static int xy[4][2] =
+  int i;
+
+  getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+
+  for (i = 1; i < 4; i++)
   {
-    { 0, -1 },
-    { -1, 0 },
-    { +1, 0 },
-    { 0, +1 }
-  };
+    int dxx = (i & 1 ? dx : 0);
+    int dyy = (i & 2 ? dy : 0);
+    int xx = x + dxx;
+    int yy = y + dyy;
+    int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
+                  BorderElement);
 
-  if (!IN_LEV_FIELD(x, y))
-    return;
+    /* check if neighbour field is of same crumble type */
+    boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
+                   graphic_info[graphic].class ==
+                   graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
 
-  element = TILE_GFX_ELEMENT(x, y);
+    /* return if check prevents inner corner */
+    if (same == (dxx == dx && dyy == dy))
+      return;
+  }
 
-  /* crumble field itself */
-#if 1
-  if (IS_CRUMBLED_TILE(x, y, element))
+  /* if we reach this point, we have an inner corner */
+
+  getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
+
+#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;
+
+  BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
+            width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
+#else
+  width  = crumbled_border_size;
+  height = crumbled_border_size;
+  cx = (dx > 0 ? TILEX - crumbled_border_size : 0);
+  cy = (dy > 0 ? TILEY - crumbled_border_size : 0);
+
+  BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
+            width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
+#endif
+}
+
+static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
+                                         int dir)
+{
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+  int width, height, bx, by, cx, cy;
+  int sx = SCREENX(x), sy = SCREENY(y);
+  int crumbled_border_size = graphic_info[graphic].border_size;
+  int i;
+
+  getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+
+  /* 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);
+#else
+  if (dir == 1 || dir == 2)            /* left or right crumbled border */
+  {
+    width = crumbled_border_size;
+    height = TILEY;
+    cx = (dir == 2 ? TILEX - crumbled_border_size : 0);
+    cy = 0;
+  }
+  else                                 /* top or bottom crumbled border */
+  {
+    width = TILEX;
+    height = crumbled_border_size;
+    cx = 0;
+    cy = (dir == 3 ? TILEY - crumbled_border_size : 0);
+  }
+#endif
+
+#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);
+#else
+  BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
+            width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
+#endif
+
+  /* (remaining middle border part must be at least as big as corner part) */
+  if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
+      crumbled_border_size >= TILESIZE / 3)
+    return;
+
+  /* correct corners of crumbled border, if needed */
+
+#if 1
+  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);
+    int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
+                  BorderElement);
+
+    /* check if neighbour field is of same crumble type */
+    if (IS_CRUMBLED_TILE(xx, yy, element) &&
+       graphic_info[graphic].class ==
+       graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
+    {
+      /* 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);
+
+      width  = crumbled_border_size;
+      height = crumbled_border_size;
+
+      if (dir == 1 || dir == 2)
+      {
+       cx = c1;
+       cy = c2;
+       bx = cx;
+       by = b1;
+      }
+      else
+      {
+       cx = c2;
+       cy = c1;
+       bx = b1;
+       by = cy;
+      }
+
+#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);
+#else
+      BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
+                width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
+#endif
+    }
+  }
 #else
-  if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
+  if (dir == 1 || dir == 2)            /* left or right crumbled border */
+  {
+    for (i = -1; i <= 1; i+=2)
+    {
+      int xx = x;
+      int yy = y + i;
+      int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
+                    BorderElement);
+
+      /* check if neighbour field is of same crumble type */
+      if (IS_CRUMBLED_TILE(xx, yy, element) &&
+         graphic_info[graphic].class ==
+         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
+      {
+       /* no crumbled corner, but continued crumbled border */
+
+       width  = crumbled_border_size;
+       height = crumbled_border_size;
+       cx = (dir == 2 ? TILEX - crumbled_border_size : 0);
+       cy = (i == 1 ? TILEY - crumbled_border_size : 0);
+       bx = cx;
+       by = (i == 1 ? crumbled_border_size :
+             TILEY - 2 * crumbled_border_size);
+
+       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
+                  width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
+      }
+    }
+  }
+  else                         /* top or bottom crumbled border */
+  {
+    for (i = -1; i <= 1; i+=2)
+    {
+      int xx = x + i;
+      int yy = y;
+      int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
+                    BorderElement);
+
+      /* check if neighbour field is of same crumble type */
+      if (IS_CRUMBLED_TILE(xx, yy, element) &&
+         graphic_info[graphic].class ==
+         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
+      {
+       /* no crumbled corner, but continued crumbled border */
+
+       width  = crumbled_border_size;
+       height = crumbled_border_size;
+       cx = (i == 1 ? TILEX - crumbled_border_size : 0);
+       cy = (dir == 3 ? TILEY - crumbled_border_size : 0);
+       bx = (i == 1 ? crumbled_border_size :
+             TILEX - 2 * crumbled_border_size);
+       by = cy;
+
+       BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
+                  width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
+      }
+    }
+  }
 #endif
+}
+
+static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
+{
+  int sx = SCREENX(x), sy = SCREENY(y);
+  int element;
+  int i;
+  static int xy[4][2] =
+  {
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
+  };
+
+  if (!IN_LEV_FIELD(x, y))
+    return;
+
+  element = TILE_GFX_ELEMENT(x, y);
+
+  /* crumble field itself */
+  if (IS_CRUMBLED_TILE(x, y, element))
   {
     if (!IN_SCR_FIELD(sx, sy))
       return;
 
-    getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
-
     for (i = 0; i < 4; i++)
     {
       int xx = x + xy[i][0];
@@ -1483,37 +2129,35 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
                 BorderElement);
 
-      /* check if neighbour field is of same type */
+      /* check if neighbour field is of same crumble type */
 #if 1
-      if (IS_CRUMBLED_TILE(xx, yy, element))
+      if (IS_CRUMBLED_TILE(xx, yy, element) &&
+         graphic_info[graphic].class ==
+         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
        continue;
 #else
-      if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
+      if (IS_CRUMBLED_TILE(xx, yy, element))
        continue;
 #endif
 
-      if (i == 1 || i == 2)
-      {
-       width = crumbled_border_size;
-       height = TILEY;
-       cx = (i == 2 ? TILEX - crumbled_border_size : 0);
-       cy = 0;
-      }
-      else
+      DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
+    }
+
+    if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
+       graphic_info[graphic].anim_frames == 2)
+    {
+      for (i = 0; i < 4; i++)
       {
-       width = TILEX;
-       height = crumbled_border_size;
-       cx = 0;
-       cy = (i == 3 ? TILEY - crumbled_border_size : 0);
-      }
+       int dx = (i & 1 ? +1 : -1);
+       int dy = (i & 2 ? +1 : -1);
 
-      BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
-                width, height, FX + sx * TILEX + cx, FY + sy * TILEY + cy);
+       DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
+      }
     }
 
     MarkTileDirty(sx, sy);
   }
-  else         /* crumble neighbour fields */
+  else         /* center field not crumbled -- crumble neighbour fields */
   {
     for (i = 0; i < 4; i++)
     {
@@ -1522,59 +2166,28 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
       int sxx = sx + xy[i][0];
       int syy = sy + xy[i][1];
 
-#if 1
       if (!IN_LEV_FIELD(xx, yy) ||
          !IN_SCR_FIELD(sxx, syy))
        continue;
-#else
-      if (!IN_LEV_FIELD(xx, yy) ||
-         !IN_SCR_FIELD(sxx, syy) ||
-         IS_MOVING(xx, yy))
-       continue;
-#endif
 
       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
        continue;
 
       element = TILE_GFX_ELEMENT(xx, yy);
 
-#if 1
       if (!IS_CRUMBLED_TILE(xx, yy, element))
        continue;
-#else
-      if (!GFX_CRUMBLED(element))
-       continue;
-#endif
 
       graphic = el_act2crm(element, ACTION_DEFAULT);
-      crumbled_border_size = graphic_info[graphic].border_size;
-
-      getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
-
-      if (i == 1 || i == 2)
-      {
-       width = crumbled_border_size;
-       height = TILEY;
-       cx = (i == 1 ? TILEX - crumbled_border_size : 0);
-       cy = 0;
-      }
-      else
-      {
-       width = TILEX;
-       height = crumbled_border_size;
-       cx = 0;
-       cy = (i == 0 ? TILEY - crumbled_border_size : 0);
-      }
 
-      BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
-                width, height, FX + sxx * TILEX + cx, FY + syy * TILEY + cy);
+      DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
 
       MarkTileDirty(sxx, syy);
     }
   }
 }
 
-void DrawLevelFieldCrumbledSand(int x, int y)
+void DrawLevelFieldCrumbled(int x, int y)
 {
   int graphic;
 
@@ -1593,7 +2206,7 @@ void DrawLevelFieldCrumbledSand(int x, int y)
       GfxElement[x][y] != EL_UNDEFINED &&
       GFX_CRUMBLED(GfxElement[x][y]))
   {
-    DrawLevelFieldCrumbledSandDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
+    DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
 
     return;
   }
@@ -1605,11 +2218,11 @@ void DrawLevelFieldCrumbledSand(int x, int y)
   graphic = el_act2crm(Feld[x][y], ACTION_DEFAULT);
 #endif
 
-  DrawLevelFieldCrumbledSandExt(x, y, graphic, 0);
+  DrawLevelFieldCrumbledExt(x, y, graphic, 0);
 }
 
-void DrawLevelFieldCrumbledSandDigging(int x, int y, int direction,
-                                      int step_frame)
+void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
+                                  int step_frame)
 {
   int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
   int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
@@ -1618,10 +2231,10 @@ void DrawLevelFieldCrumbledSandDigging(int x, int y, int direction,
   int sx = SCREENX(x), sy = SCREENY(y);
 
   DrawGraphic(sx, sy, graphic1, frame1);
-  DrawLevelFieldCrumbledSandExt(x, y, graphic2, frame2);
+  DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
 }
 
-void DrawLevelFieldCrumbledSandNeighbours(int x, int y)
+void DrawLevelFieldCrumbledNeighbours(int x, int y)
 {
   int sx = SCREENX(x), sy = SCREENY(y);
   static int xy[4][2] =
@@ -1676,7 +2289,7 @@ static int getBorderElement(int x, int y)
 void DrawScreenElement(int x, int y, int element)
 {
   DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
-  DrawLevelFieldCrumbledSand(LEVELX(x), LEVELY(y));
+  DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
 }
 
 void DrawLevelElement(int x, int y, int element)
@@ -1870,7 +2483,7 @@ void DrawEnvelopeBackground(int envelope_nr, int startx, int starty,
   int inner_sy = (height >= 3 * font_height ? font_height : 0);
   boolean draw_masked = graphic_info[graphic].draw_masked;
 
-  getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+  getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
 
   if (src_bitmap == NULL || width < font_width || height < font_height)
   {
@@ -1902,14 +2515,97 @@ 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;
+  int font_width = getFontWidth(font_nr);
+  int font_height = getFontHeight(font_nr);
+  int max_xsize = level.envelope[envelope_nr].xsize;
+  int max_ysize = level.envelope[envelope_nr].ysize;
+  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;
+
+  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 = (SXSIZE - xsize * font_width)  / 2;
+    int 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);
+
+#if 1
+    DrawTextBuffer(SX + sx + font_width, SY + 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,
+                      level.envelope[envelope_nr].text, font_nr, max_xsize,
+                      xsize - 2, ysize - 2, mask_mode);
+#endif
+
+    redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
+    BackToFront();
+
+    WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
+  }
+}
+
+void AnimateEnvelopeDoor(char *text, int anim_mode, int action)
+{
+#if 1
+  int envelope_nr = 0;
+#endif
+  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);
+  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
+
+#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
+
+#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;
@@ -1918,6 +2614,54 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
   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++)
+  {
+    max_word_len = (*text_ptr != ' ' ? max_word_len + 1 : 0);
+
+    if (max_word_len > 7)      /* tools.c: MAX_REQUEST_LINE_FONT1_LEN == 7 */
+    {
+      max_xsize = 10;  /* tools.c: MAX_REQUEST_LINE_FONT2_LEN == 10 */
+      font_nr = FONT_TEXT_1;
+
+      break;
+    }
+  }
+#endif
+#endif
+
+#if 1
+  for (text_ptr = text_copy; *text_ptr; text_ptr++)
+    if (*text_ptr == ' ')
+      *text_ptr = '\n';
+#endif
+
+#if 1
+  dDX = SX + (SXSIZE - DXSIZE) / 2 - DX;
+  dDY = SY + (SYSIZE - DYSIZE) / 2 - DY;
+#else
+  dDX = SX + SXSIZE / 2 - max_xsize * font_width  / 2 - DX;
+  dDY = SY + SYSIZE / 2 - max_ysize * font_height / 2 - DY;
+#endif
+
   for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
   {
     int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
@@ -1926,32 +2670,85 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
     int sy = (SYSIZE - ysize * font_height) / 2;
     int xx, yy;
 
+#if 1
+    BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+#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);
 
 #if 1
+
+#if 1
+    DrawTextBuffer(SX + sx + font_width, SY + sy + font_height + 8,
+                  text_copy, font_nr, max_xsize,
+                  xsize - 2, ysize - 2, 2, mask_mode,
+                  FALSE, TRUE, FALSE);
+#else
     DrawTextBuffer(SX + sx + font_width, SY + sy + font_height,
                   level.envelope[envelope_nr].text, font_nr, max_xsize,
-                  xsize - 2, ysize - 2, mask_mode,
+                  xsize - 2, ysize - 2, 0, mask_mode,
                   level.envelope[envelope_nr].autowrap,
                   level.envelope[envelope_nr].centered, FALSE);
+#endif
+
 #else
     DrawTextToTextArea(SX + sx + font_width, SY + 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));
+#else
+    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));
+#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);
   }
+
+#if 1
+  free(text_copy);
+#endif
 }
 
 void ShowEnvelope(int envelope_nr)
@@ -1998,6 +2795,127 @@ void ShowEnvelope(int envelope_nr)
   BackToFront();
 }
 
+void ShowEnvelopeDoor(char *text, 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
+  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];
+#if 0
+  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);
+#endif
+  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
+  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);
+    else
+    {
+      BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+    }
+  }
+
+  SetDrawtoField(DRAW_BACKBUFFER);
+
+  // SetDrawBackgroundMask(REDRAW_NONE);
+
+  if (action == ACTION_OPENING)
+  {
+    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)
+  {
+    PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
+
+    if (anim_mode == ANIM_DEFAULT)
+      AnimateEnvelopeDoor(text, ANIM_DEFAULT, ACTION_OPENING);
+
+    AnimateEnvelopeDoor(text, main_anim_mode, ACTION_OPENING);
+
+#if 0
+    if (tape.playing)
+      Delay(wait_delay_value);
+    else
+      WaitForEventToContinue();
+#endif
+  }
+  else
+  {
+    PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
+
+    if (anim_mode != ANIM_NONE)
+      AnimateEnvelopeDoor(text, main_anim_mode, ACTION_CLOSING);
+
+    if (anim_mode == ANIM_DEFAULT)
+      AnimateEnvelopeDoor(text, ANIM_DEFAULT, ACTION_CLOSING);
+  }
+
+  game.envelope_active = FALSE;
+
+#if 1
+  // game_status = last_game_status;   /* restore current game status */
+
+  if (action == ACTION_CLOSING)
+  {
+    if (game_status != GAME_MODE_MAIN)
+      StopAnimation();
+
+    BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+  }
+#else
+  SetDrawtoField(DRAW_BUFFERED);
+#endif
+
+  // SetDrawBackgroundMask(last_draw_background_mask);
+
+#if 1
+  redraw_mask = REDRAW_FIELD;
+  // redraw_mask |= REDRAW_ALL;
+#else
+  redraw_mask |= REDRAW_FIELD;
+#endif
+
+#if 1
+  if (game_status == GAME_MODE_MAIN)
+    DoAnimation();
+
+  BackToFront();
+
+  /* (important: after "BackToFront()", but before "SetDrawtoField()") */
+  game_status = last_game_status;      /* restore current game status */
+
+  if (game_status == GAME_MODE_PLAYING &&
+      level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    SetDrawtoField(DRAW_BUFFERED);
+#else
+  BackToFront();
+#endif
+}
+
 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
 {
   Bitmap *src_bitmap;
@@ -2177,11 +3095,11 @@ static void DrawPreviewLevelLabelExt(int mode)
 
 void DrawPreviewLevel(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);
@@ -2372,6 +3290,18 @@ inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
     DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
 }
 
+inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
+                                        int graphic, int sync_frame,
+                                        int mask_mode)
+{
+  int frame = getGraphicAnimationFrame(graphic, sync_frame);
+
+  if (mask_mode == USE_MASKING)
+    DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
+  else
+    DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
+}
+
 inline void DrawGraphicAnimation(int x, int y, int graphic)
 {
   int lx = LEVELX(x), ly = LEVELY(y);
@@ -2379,6 +3309,23 @@ inline void DrawGraphicAnimation(int x, int y, int graphic)
   if (!IN_SCR_FIELD(x, y))
     return;
 
+#if NEW_TILESIZE
+  DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
+                         graphic, GfxFrame[lx][ly], NO_MASKING);
+#else
+  DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
+                         graphic, GfxFrame[lx][ly], NO_MASKING);
+#endif
+  MarkTileDirty(x, y);
+}
+
+inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
+{
+  int lx = LEVELX(x), ly = LEVELY(y);
+
+  if (!IN_SCR_FIELD(x, y))
+    return;
+
   DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
                          graphic, GfxFrame[lx][ly], NO_MASKING);
   MarkTileDirty(x, y);
@@ -2410,10 +3357,10 @@ inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
 
 #if 1
   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
-    DrawLevelFieldCrumbledSand(x, y);
+    DrawLevelFieldCrumbled(x, y);
 #else
   if (GFX_CRUMBLED(Feld[x][y]))
-    DrawLevelFieldCrumbledSand(x, y);
+    DrawLevelFieldCrumbled(x, y);
 #endif
 }
 
@@ -2433,7 +3380,7 @@ void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
   DrawGraphicAnimation(sx, sy, graphic);
 
   if (GFX_CRUMBLED(element))
-    DrawLevelFieldCrumbledSand(x, y);
+    DrawLevelFieldCrumbled(x, y);
 }
 
 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
@@ -2605,7 +3552,7 @@ void DrawPlayer(struct PlayerInfo *player)
       int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
 
       if (GFX_CRUMBLED(old_element))
-       DrawLevelFieldCrumbledSandDigging(jx, jy, move_dir, player->StepFrame);
+       DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
       else
        DrawGraphic(sx, sy, old_graphic, frame);
 
@@ -2720,9 +3667,10 @@ void DrawPlayer(struct PlayerInfo *player)
 
     /* draw background element under pushed element (like the Sokoban field) */
 #if 1
-    /* this allows transparent pushing animation over non-black background */
-    if (IS_MOVING(jx, jy))
+    if (game.use_masked_pushing && IS_MOVING(jx, jy))
     {
+      /* this allows transparent pushing animation over non-black background */
+
       if (Back[jx][jy])
        DrawLevelElement(jx, jy, Back[jx][jy]);
       else
@@ -2733,6 +3681,8 @@ void DrawPlayer(struct PlayerInfo *player)
       else
        DrawLevelElement(next_jx, next_jy, EL_EMPTY);
     }
+    else if (Back[next_jx][next_jy])
+      DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
 #else
     if (Back[next_jx][next_jy])
       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
@@ -2920,9 +3870,23 @@ boolean Request(char *text, unsigned int req_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 1
+  global.use_envelope_request = 0;
+#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);
@@ -2939,6 +3903,7 @@ boolean Request(char *text, unsigned int req_state)
       break;
     }
   }
+#endif
 
   if (game_status == GAME_MODE_PLAYING)
   {
@@ -2970,9 +3935,21 @@ boolean Request(char *text, unsigned int req_state)
 
   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,
@@ -2993,40 +3970,49 @@ boolean Request(char *text, unsigned int req_state)
   game_status = GAME_MODE_PSEUDO_DOOR;
 
   /* write text for request */
-  for (ty = 0; ty < MAX_REQUEST_LINES; ty++)
+  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)
+    if (!*text_ptr)
       break;
 
     for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
     {
-      tc = *(text + tx);
+      tc = *(text_ptr + tx);
       if (!tc || tc == ' ')
        break;
     }
 
     if (!tl)
     { 
-      text++; 
+      text_ptr++; 
       ty--; 
       continue; 
     }
 
-    strncpy(text_line, text, tl);
+    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 += tl + (tc == ' ' ? 1 : 0);
+    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]);
@@ -3049,7 +4035,36 @@ boolean Request(char *text, unsigned int req_state)
             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))
   {
@@ -3066,8 +4081,13 @@ 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;
 
@@ -3160,6 +4180,9 @@ boolean Request(char *text, unsigned int req_state)
              break;
 
            case KSYM_Escape:
+#if defined(TARGET_SDL2)
+           case KSYM_Back:
+#endif
              result = 0;
              break;
 
@@ -3204,8 +4227,16 @@ boolean Request(char *text, unsigned int req_state)
        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();
@@ -3226,7 +4257,16 @@ boolean Request(char *text, unsigned int req_state)
 
   UnmapToolButtons();
 
+#if 1
+  if (global.use_envelope_request)
+    ShowEnvelopeDoor(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);
 
@@ -3316,8 +4356,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)
@@ -3627,6 +4667,102 @@ 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 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 (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
@@ -3736,7 +4872,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;
@@ -3785,6 +4921,8 @@ void CreateToolButtons()
   }
 }
 
+#endif
+
 void FreeToolButtons()
 {
   int i;
@@ -5897,6 +7035,52 @@ int map_direction_EM_to_RND(int direction)
          MV_NONE);
 }
 
+int map_element_RND_to_SP(int element_rnd)
+{
+  int element_sp = 0x20;       /* map unknown elements to yellow "hardware" */
+
+  if (element_rnd >= EL_SP_START &&
+      element_rnd <= EL_SP_END)
+    element_sp = element_rnd - EL_SP_START;
+  else if (element_rnd == EL_EMPTY_SPACE)
+    element_sp = 0x00;
+  else if (element_rnd == EL_INVISIBLE_WALL)
+    element_sp = 0x28;
+
+  return element_sp;
+}
+
+int map_element_SP_to_RND(int element_sp)
+{
+  int element_rnd = EL_UNKNOWN;
+
+  if (element_sp >= 0x00 &&
+      element_sp <= 0x27)
+    element_rnd = EL_SP_START + element_sp;
+  else if (element_sp == 0x28)
+    element_rnd = EL_INVISIBLE_WALL;
+
+  return element_rnd;
+}
+
+int map_action_SP_to_RND(int action_sp)
+{
+  switch (action_sp)
+  {
+    case actActive:            return ACTION_ACTIVE;
+    case actImpact:            return ACTION_IMPACT;
+    case actExploding:         return ACTION_EXPLODING;
+    case actDigging:           return ACTION_DIGGING;
+    case actSnapping:          return ACTION_SNAPPING;
+    case actCollecting:                return ACTION_COLLECTING;
+    case actPassing:           return ACTION_PASSING;
+    case actPushing:           return ACTION_PUSHING;
+    case actDropping:          return ACTION_DROPPING;
+
+    default:                   return ACTION_DEFAULT;
+  }
+}
+
 int get_next_element(int element)
 {
   switch (element)
@@ -6173,7 +7357,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);
@@ -6205,6 +7389,13 @@ inline static int get_effective_element_EM(int tile, int frame_em)
       case Yacid_splash_wB:
        return (frame_em > 5 ? EL_EMPTY : element);
 
+#if 0
+       /* !!! FIX !!! */
+      case Ydiamond_stone:
+       //  if (!game.use_native_emc_graphics_engine)
+       return EL_ROCK;
+#endif
+
       default:
        return element;
     }
@@ -6282,6 +7473,11 @@ inline static boolean check_linear_animation_EM(int tile)
     case Ytank_s_e:
     case Ytank_w_s:
     case Ytank_n_w:
+#if 1
+    case Yacid_splash_eB:
+    case Yacid_splash_wB:
+    case Yemerald_stone:
+#endif
       return TRUE;
   }
 
@@ -6890,6 +8086,23 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
 #endif
 
 
+#if 0
+  if (tile == Ydiamond_stone)
+    printf("::: stone smashing diamond... %d: %d, %d, %d, %d, %d -> %d [%d, %d, %d, %d, %d, %d] [%d]\n",
+          frame_em,
+          g->anim_frames,
+          g->anim_delay,
+          g->anim_mode,
+          g->anim_start_frame,
+          sync_frame,
+          frame,
+          g_em->src_x, g_em->src_y,
+          g_em->src_offset_x, g_em->src_offset_y,
+          g_em->dst_offset_x, g_em->dst_offset_y,
+          graphic);
+#endif
+
+
 #if 0
   return;
 #endif
@@ -7732,6 +8945,67 @@ void InitGraphicInfo_EM(void)
 #endif
 }
 
+void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
+                           boolean any_player_moving,
+                           boolean player_is_dropping)
+{
+  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)
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+  }
+}
+
+void CheckSingleStepMode_SP(boolean murphy_is_waiting,
+                           boolean murphy_is_dropping)
+{
+#if 0
+  printf("::: waiting: %d, dropping: %d\n",
+        murphy_is_waiting, murphy_is_dropping);
+#endif
+
+  if (tape.single_step && tape.recording && !tape.pausing)
+  {
+    // if (murphy_is_waiting || murphy_is_dropping)
+    if (murphy_is_waiting)
+    {
+#if 0
+      printf("::: murphy is waiting -> pause mode\n");
+#endif
+
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+    }
+  }
+}
+
+void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
+                        int graphic, int sync_frame, int x, int y)
+{
+  int frame = getGraphicAnimationFrame(graphic, sync_frame);
+
+  getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
+}
+
+boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
+{
+  return (IS_NEXT_FRAME(sync_frame, graphic));
+}
+
+int getGraphicInfo_Delay(int graphic)
+{
+  return graphic_info[graphic].anim_delay;
+}
+
 void PlayMenuSoundExt(int sound)
 {
   if (sound == SND_UNDEFINED)
@@ -7822,40 +9096,59 @@ void ToggleFullscreenIfNeeded()
   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 (!video.fullscreen_available)
+  if (change_window_scaling_percent && video.fullscreen_enabled)
     return;
 
-#if 1
-  if (change_fullscreen || change_fullscreen_mode)
-#else
-  if (setup.fullscreen != video.fullscreen_enabled ||
-      setup.fullscreen_mode != video.fullscreen_mode_current)
+  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;
+
+    return;
+  }
 #endif
+
+  if (change_fullscreen ||
+      change_fullscreen_mode ||
+      change_window_scaling_percent)
   {
     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
 
     /* save backbuffer content which gets lost when toggling fullscreen mode */
     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
-#if 1
     if (change_fullscreen_mode)
-#else
-    if (setup.fullscreen && video.fullscreen_enabled)
-#endif
     {
       /* keep fullscreen, but change fullscreen mode (screen resolution) */
-#if 1
-      /* (this is now set in sdl.c) */
-#else
-      video.fullscreen_mode_current = setup.fullscreen_mode;
-#endif
       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 */
@@ -7871,3 +9164,187 @@ void ToggleFullscreenIfNeeded()
 #endif
   }
 }
+
+void ChangeViewportPropertiesIfNeeded()
+{
+  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);
+  int gfx_game_mode = (game_status == GAME_MODE_PLAYING ||
+                      game_status == GAME_MODE_EDITOR ? game_status :
+                      GAME_MODE_MAIN);
+  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;
+#if NEW_TILESIZE
+  int new_tilesize_var = TILESIZE / (setup.small_game_graphics ? 2 : 1);
+  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;
+  int new_scr_fieldy = new_sysize / tilesize;
+  int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
+  int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
+#else
+  int new_scr_fieldx = (vp_playfield->width  - 2 * border_size) / TILESIZE;
+  int new_scr_fieldy = (vp_playfield->height - 2 * border_size) / TILESIZE;
+#endif
+  boolean init_gfx_buffers = FALSE;
+  boolean init_video_buffer = FALSE;
+  boolean init_gadgets_and_toons = FALSE;
+
+#if 0
+  /* !!! TEST ONLY !!! */
+  // InitGfxBuffers();
+  return;
+#endif
+
+  if (viewport.window.width  != WIN_XSIZE ||
+      viewport.window.height != WIN_YSIZE)
+  {
+    WIN_XSIZE = viewport.window.width;
+    WIN_YSIZE = viewport.window.height;
+
+#if 1
+    init_video_buffer = TRUE;
+    init_gfx_buffers = TRUE;
+#else
+    InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
+    InitGfxBuffers();
+
+#if 1
+    SetDrawDeactivationMask(REDRAW_NONE);
+    SetDrawBackgroundMask(REDRAW_FIELD);
+
+    // RedrawBackground();
+#endif
+#endif
+
+    // printf("::: video: init_video_buffer, init_gfx_buffers\n");
+  }
+
+  if (new_scr_fieldx != SCR_FIELDX ||
+      new_scr_fieldy != SCR_FIELDY)
+  {
+    /* this always toggles between MAIN and GAME when using small tile size */
+
+    SCR_FIELDX = new_scr_fieldx;
+    SCR_FIELDY = new_scr_fieldy;
+
+    // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
+  }
+
+#if 0
+  if (new_tilesize_var != TILESIZE_VAR &&
+      gfx_game_mode == GAME_MODE_PLAYING)
+  {
+    /* doing this outside GAME_MODE_PLAYING would give wrong playfield size */
+
+    TILESIZE_VAR = new_tilesize_var;
+
+    init_gfx_buffers = TRUE;
+
+    // printf("::: tilesize: init_gfx_buffers\n");
+  }
+#endif
+
+  if (new_sx != SX ||
+      new_sy != SY ||
+      new_sxsize != SXSIZE ||
+      new_sysize != SYSIZE ||
+      new_real_sx != REAL_SX ||
+      new_real_sy != REAL_SY ||
+      new_full_sxsize != FULL_SXSIZE ||
+      new_full_sysize != FULL_SYSIZE ||
+      new_tilesize_var != TILESIZE_VAR ||
+      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)
+  {
+    SX = new_sx;
+    SY = new_sy;
+    SXSIZE = new_sxsize;
+    SYSIZE = new_sysize;
+    REAL_SX = new_real_sx;
+    REAL_SY = new_real_sy;
+    FULL_SXSIZE = new_full_sxsize;
+    FULL_SYSIZE = new_full_sysize;
+    TILESIZE_VAR = new_tilesize_var;
+
+#if 0
+    printf("::: %d, %d, %d [%d]\n",
+          SCR_FIELDX, SCR_FIELDY, TILESIZE_VAR,
+          setup.small_game_graphics);
+#endif
+
+    *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;
+
+#if 1
+    init_gfx_buffers = TRUE;
+
+    // printf("::: viewports: init_gfx_buffers\n");
+#else
+    InitGfxBuffers();
+#endif
+
+    if (gfx_game_mode == GAME_MODE_MAIN)
+    {
+#if 1
+      init_gadgets_and_toons = TRUE;
+
+      // printf("::: viewports: init_gadgets_and_toons\n");
+#else
+      InitGadgets();
+      InitToons();
+#endif
+    }
+  }
+
+  if (init_gfx_buffers)
+  {
+    // printf("::: init_gfx_buffers\n");
+
+    SCR_FIELDX = new_scr_fieldx_buffers;
+    SCR_FIELDY = new_scr_fieldy_buffers;
+
+    InitGfxBuffers();
+
+    SCR_FIELDX = new_scr_fieldx;
+    SCR_FIELDY = new_scr_fieldy;
+  }
+
+  if (init_video_buffer)
+  {
+    // printf("::: init_video_buffer\n");
+
+    InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
+
+    SetDrawDeactivationMask(REDRAW_NONE);
+    SetDrawBackgroundMask(REDRAW_FIELD);
+  }
+
+  if (init_gadgets_and_toons)
+  {
+    // printf("::: init_gadgets_and_toons\n");
+
+    InitGadgets();
+    InitToons();
+  }
+
+#if 0
+  printf("::: %d, %d  /  %d, %d [%d]\n", VX, VY, EX, EY, game_status);
+#endif
+}