rnd-20140114-1-src
[rocksndiamonds.git] / src / tools.c
index 8b348d0b4e335d484b24296cce775b24bd26bbe0..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,10 +147,12 @@ void SetDrawtoField(int mode)
     BY2 = SCR_FIELDY;
     redraw_x1 = 1;
     redraw_y1 = 1;
+#endif
+#endif
 
     drawto_field = fieldbuffer;
   }
-  else /* DRAW_DIRECT, DRAW_BACKBUFFER */
+  else /* DRAW_BACKBUFFER */
   {
     FX = SX;
     FY = SY;
@@ -129,7 +163,7 @@ void SetDrawtoField(int mode)
     redraw_x1 = 0;
     redraw_y1 = 0;
 
-    drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
+    drawto_field = backbuffer;
   }
 }
 
@@ -144,7 +178,17 @@ void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
     /* blit playfield from scroll buffer to normal back buffer for fading in */
     BlitScreenToBitmap_EM(backbuffer);
   }
-  else if (game_status == GAME_MODE_PLAYING && !game.envelope_active)
+  else if (game_status == GAME_MODE_PLAYING &&
+          level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    /* currently there is no partial redraw -- always redraw whole playfield */
+    RedrawPlayfield_SP(TRUE);
+
+    /* blit playfield from scroll buffer to normal back buffer for fading in */
+    BlitScreenToBitmap_SP(backbuffer);
+  }
+  else if (game_status == GAME_MODE_PLAYING &&
+          !game.envelope_active)
   {
     if (force_redraw)
     {
@@ -154,23 +198,17 @@ void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
       height = gfx.sysize + 2 * TILEY;
     }
 
-    if (force_redraw || setup.direct_draw)
+    if (force_redraw)
     {
       int xx, yy;
       int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
       int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
 
-      if (setup.direct_draw)
-       SetDrawtoField(DRAW_BACKBUFFER);
-
       for (xx = BX1; xx <= BX2; xx++)
        for (yy = BY1; yy <= BY2; yy++)
          if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
            DrawScreenField(xx, yy);
       DrawAllPlayers();
-
-      if (setup.direct_draw)
-       SetDrawtoField(DRAW_DIRECT);
     }
 
     if (setup.soft_scrolling)
@@ -205,16 +243,16 @@ void DrawMaskedBorder_Rect(int x, int y, int width, int height)
 
 void DrawMaskedBorder_FIELD()
 {
-  if (game_status >= GAME_MODE_TITLE &&
-      game_status <= GAME_MODE_PLAYING &&
-      border.draw_masked[game_status])
+  if (global.border_status >= GAME_MODE_TITLE &&
+      global.border_status <= GAME_MODE_PLAYING &&
+      border.draw_masked[global.border_status])
     DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
 }
 
 void DrawMaskedBorder_DOOR_1()
 {
   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
-      (game_status != GAME_MODE_EDITOR ||
+      (global.border_status != GAME_MODE_EDITOR ||
        border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
     DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
 }
@@ -222,7 +260,7 @@ void DrawMaskedBorder_DOOR_1()
 void DrawMaskedBorder_DOOR_2()
 {
   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
-      game_status != GAME_MODE_EDITOR)
+      global.border_status != GAME_MODE_EDITOR)
     DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
 }
 
@@ -241,8 +279,14 @@ void DrawMaskedBorder_ALL()
 
 void DrawMaskedBorder(int redraw_mask)
 {
-  /* do not draw masked screen borders when displaying title screens */
-  if (effectiveGameStatus() == GAME_MODE_TITLE)
+  /* never draw masked screen borders on borderless screens */
+  if (effectiveGameStatus() == GAME_MODE_LOADING ||
+      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)
@@ -260,23 +304,136 @@ 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;
+  int x, y;
   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
 
-  if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
-    redraw_mask &= ~REDRAW_MAIN;
+#if 0
+  printf("::: TILES TO REFRESH: %d\n", redraw_tiles);
+  for (x = 0; x < SCR_FIELDX; x++)
+    for (y = 0 ; y < SCR_FIELDY; y++)
+      if (redraw[redraw_x1 + x][redraw_y1 + y])
+       printf("::: - %d, %d [%s]\n",
+              LEVELX(x), LEVELY(y),
+              EL_NAME(Feld[LEVELX(x)][LEVELY(y)]));
+#endif
 
   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])
@@ -319,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)
@@ -336,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)
     {
@@ -344,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;
@@ -418,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 */
@@ -435,8 +718,12 @@ void BackToFront()
     if (!global.fps_slowdown)
       info1[0] = '\0';
 
-    sprintf(text, "%.1f fps%s", global.frames_per_second, info1);
+    sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
+#if 1
+    DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
+#else
     DrawTextExt(window, SX, SY, text, FONT_TEXT_2, BLIT_OPAQUE);
+#endif
   }
 
   FlushDisplay();
@@ -448,113 +735,80 @@ void BackToFront()
   redraw_mask = REDRAW_NONE;
 }
 
-void FadeToFront()
+static void FadeCrossSaveBackbuffer()
 {
-#if 0
-  long fading_delay = 300;
-
-  if (setup.fading && (redraw_mask & REDRAW_FIELD))
-  {
-#endif
-
-#if 0
-    int x,y;
+  BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+}
 
-    ClearRectangle(window, REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
-    FlushDisplay();
+static void FadeExt(int fade_mask, int fade_mode, int fade_type)
+{
+  static int fade_type_skip = FADE_TYPE_NONE;
+  void (*draw_border_function)(void) = NULL;
+  Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
+  int x, y, width, height;
+  int fade_delay, post_delay;
 
-    for (i = 0; i < 2 * FULL_SYSIZE; i++)
+  if (fade_type == FADE_TYPE_FADE_OUT)
+  {
+    if (fade_type_skip != FADE_TYPE_NONE)
     {
-      for (y = 0; y < FULL_SYSIZE; y++)
-      {
-       BlitBitmap(backbuffer, window,
-                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
-      }
-      FlushDisplay();
-      Delay(10);
-    }
-#endif
-
 #if 0
-    for (i = 1; i < FULL_SYSIZE; i+=2)
-      BlitBitmap(backbuffer, window,
-                REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
-    FlushDisplay();
-    Delay(fading_delay);
+      printf("::: skipping %d ... [%d] (X)\n", fade_mode, fade_type_skip);
 #endif
 
-#if 0
-    SetClipOrigin(clip_gc[PIX_FADEMASK], 0, 0);
-    BlitBitmapMasked(backbuffer, window,
-                    REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-                    REAL_SX,REAL_SY);
-    FlushDisplay();
-    Delay(fading_delay);
-
-    SetClipOrigin(clip_gc[PIX_FADEMASK], -1, -1);
-    BlitBitmapMasked(backbuffer, window,
-                    REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-                    REAL_SX,REAL_SY);
-    FlushDisplay();
-    Delay(fading_delay);
-
-    SetClipOrigin(clip_gc[PIX_FADEMASK], 0, -1);
-    BlitBitmapMasked(backbuffer, window,
-                    REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-                    REAL_SX,REAL_SY);
-    FlushDisplay();
-    Delay(fading_delay);
-
-    SetClipOrigin(clip_gc[PIX_FADEMASK], -1, 0);
-    BlitBitmapMasked(backbuffer, window,
-                    REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-                    REAL_SX,REAL_SY);
-    FlushDisplay();
-    Delay(fading_delay);
+      /* skip all fade operations until specified fade operation */
+      if (fade_type & fade_type_skip)
+       fade_type_skip = FADE_TYPE_NONE;
 
-    redraw_mask &= ~REDRAW_MAIN;
-  }
-#endif
+      return;
+    }
 
-  BackToFront();
-}
+    if (fading.fade_mode & FADE_TYPE_TRANSFORM)
+    {
+      FadeCrossSaveBackbuffer();
 
-void FadeExt(int fade_mask, int fade_mode)
-{
-  static int fade_mode_skip = FADE_MODE_NONE;
-  void (*draw_border_function)(void) = NULL;
-#if 0
-  Bitmap *bitmap = (fade_mode != FADE_MODE_FADE_IN ? bitmap_db_cross : NULL);
-#else
-  Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
-#endif
-  int x, y, width, height;
-  int fade_delay, post_delay;
+      return;
+    }
+  }
 
   redraw_mask |= fade_mask;
 
-  if (fade_mode & FADE_TYPE_SKIP)
+  if (fade_type == FADE_TYPE_SKIP)
   {
 #if 0
-    printf("::: will skip %d ... [%d]\n", fade_mode, fade_mode_skip);
+    printf("::: will skip %d ... [%d]\n", fade_mode, fade_type_skip);
 #endif
 
-    fade_mode_skip = fade_mode;
+    fade_type_skip = fade_mode;
 
     return;
   }
 
-  if (fade_mode_skip & FADE_TYPE_SKIP)
+#if 0
+  printf("::: !!! FADING %d ... [%d] [%d]\n", fade_mode, fade_type,
+        fade_type_skip);
+#endif
+
+#if 1
+  fade_delay = fading.fade_delay;
+  post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+#endif
+
+  if (fade_type_skip != FADE_TYPE_NONE)
   {
 #if 0
-    printf("::: skipping %d ... [%d]\n", fade_mode, fade_mode_skip);
+    printf("::: skipping %d ... [%d]\n", fade_mode, fade_type_skip);
 #endif
 
     /* skip all fade operations until specified fade operation */
-    if (fade_mode & fade_mode_skip)
-      fade_mode_skip = FADE_MODE_NONE;
+    if (fade_type & fade_type_skip)
+      fade_type_skip = FADE_TYPE_NONE;
 
+#if 1
+    fade_delay = 0;
+#else
     return;
+#endif
   }
 
 #if 1
@@ -566,7 +820,7 @@ void FadeExt(int fade_mask, int fade_mode)
   }
 #endif
 
-#if 1
+#if 0
   if (fading.fade_mode == FADE_MODE_NONE)
   {
     BackToFront();
@@ -575,17 +829,34 @@ void FadeExt(int fade_mask, int fade_mode)
   }
 #endif
 
-  if (fade_mask & REDRAW_FIELD)
+  /* !!! what about fade_mask == REDRAW_FIELD | REDRAW_ALL ??? !!! */
+
+#if 0
+  printf("::: NOW FADING %d ... [%d]\n", fade_mode, fade_type);
+#endif
+
+#if 0
+  if (fade_mask == REDRAW_NONE)
+    fade_mask = REDRAW_FIELD;
+#endif
+
+  // if (fade_mask & REDRAW_FIELD)
+  if (fade_mask == REDRAW_FIELD)
   {
     x = REAL_SX;
     y = REAL_SY;
     width  = FULL_SXSIZE;
     height = FULL_SYSIZE;
 
+#if 0
     fade_delay = fading.fade_delay;
     post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+#endif
 
-    draw_border_function = DrawMaskedBorder_FIELD;
+    if (border.draw_masked_when_fading)
+      draw_border_function = DrawMaskedBorder_FIELD;   /* update when fading */
+    else
+      DrawMaskedBorder_FIELD();                                /* draw once */
   }
   else         /* REDRAW_ALL */
   {
@@ -594,20 +865,35 @@ void FadeExt(int fade_mask, int fade_mode)
     width  = WIN_XSIZE;
     height = WIN_YSIZE;
 
+#if 0
     fade_delay = fading.fade_delay;
     post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+#endif
   }
 
 #if 1
-  if (!setup.fade_screens || fade_delay == 0)
+  if (!setup.fade_screens ||
+      fade_delay == 0 ||
+      fading.fade_mode == FADE_MODE_NONE)
 #else
-  if (!setup.fade_screens || fade_delay == 0 || fading.anim_mode == ANIM_NONE)
+  if (!setup.fade_screens || fade_delay == 0)
 #endif
   {
     if (fade_mode == FADE_MODE_FADE_OUT)
+      return;
+
+#if 0
+    if (fade_mode == FADE_MODE_FADE_OUT &&
+       fading.fade_mode != FADE_MODE_NONE)
       ClearRectangle(backbuffer, x, y, width, height);
+#endif
 
+#if 1
+    BlitBitmap(backbuffer, window, x, y, width, height, x, y);
+    redraw_mask = REDRAW_NONE;
+#else
     BackToFront();
+#endif
 
     return;
   }
@@ -620,57 +906,23 @@ void FadeExt(int fade_mask, int fade_mode)
 
 void FadeIn(int fade_mask)
 {
-#if 1
-  // printf("::: now fading in...\n");
-
   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
-    FadeExt(fade_mask, fading.fade_mode);
-  else
-    FadeExt(fade_mask, FADE_MODE_FADE_IN);
-#else
-#if 1
-  if (fading.fade_mode == FADE_MODE_CROSSFADE)
-    FadeExt(fade_mask, FADE_MODE_CROSSFADE);
+    FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
   else
-    FadeExt(fade_mask, FADE_MODE_FADE_IN);
-#else
-  FadeExt(fade_mask, FADE_MODE_FADE_IN);
-#endif
-#endif
+    FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
 }
 
 void FadeOut(int fade_mask)
 {
-#if 1
-  // printf("::: fading.fade_mode == %d\n", fading.fade_mode);
-
   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
-    FadeCrossSaveBackbuffer();
-  else
-    FadeExt(fade_mask, FADE_MODE_FADE_OUT);
-#else
-#if 1
-  if (fading.fade_mode == FADE_MODE_CROSSFADE)
-    FadeCrossSaveBackbuffer();
+    FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
   else
-    FadeExt(fade_mask, FADE_MODE_FADE_OUT);
-#else
-  FadeExt(fade_mask, FADE_MODE_FADE_OUT);
-#endif
-#endif
-}
+    FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
 
-void FadeCross(int fade_mask)
-{
-  FadeExt(fade_mask, FADE_MODE_CROSSFADE);
+  global.border_status = game_status;
 }
 
-void FadeCrossSaveBackbuffer()
-{
-  BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
-}
-
-static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
+static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
 {
   static struct TitleFadingInfo fading_leave_stored;
 
@@ -684,6 +936,10 @@ void FadeSetEnterMenu()
 {
   fading = menu.enter_menu;
 
+#if 0
+  printf("::: storing enter_menu\n");
+#endif
+
   FadeSetLeaveNext(fading, TRUE);      /* (keep same fade mode) */
 }
 
@@ -691,6 +947,10 @@ void FadeSetLeaveMenu()
 {
   fading = menu.leave_menu;
 
+#if 0
+  printf("::: storing leave_menu\n");
+#endif
+
   FadeSetLeaveNext(fading, TRUE);      /* (keep same fade mode) */
 }
 
@@ -698,11 +958,31 @@ void FadeSetEnterScreen()
 {
   fading = menu.enter_screen[game_status];
 
+#if 0
+  printf("::: storing leave_screen[%d]\n", game_status);
+#endif
+
   FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);      /* store */
 }
 
+void FadeSetNextScreen()
+{
+  fading = menu.next_screen;
+
+#if 0
+  printf("::: storing next_screen\n");
+#endif
+
+  // (do not overwrite fade mode set by FadeSetEnterScreen)
+  // FadeSetLeaveNext(fading, TRUE);   /* (keep same fade mode) */
+}
+
 void FadeSetLeaveScreen()
 {
+#if 0
+  printf("::: recalling last stored value\n");
+#endif
+
   FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);     /* recall */
 }
 
@@ -725,12 +1005,12 @@ void FadeSetDisabled()
 
 void FadeSkipNextFadeIn()
 {
-  FadeExt(0, FADE_MODE_SKIP_FADE_IN);
+  FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
 }
 
 void FadeSkipNextFadeOut()
 {
-  FadeExt(0, FADE_MODE_SKIP_FADE_OUT);
+  FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
 }
 
 void SetWindowBackgroundImageIfDefined(int graphic)
@@ -777,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);
 }
@@ -793,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)
@@ -816,7 +1121,7 @@ void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
   DrawBackground(x, y, width, height);
 }
 
-void ClearWindow()
+void ClearField()
 {
   /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
   /* (when entering hall of fame after playing) */
@@ -830,12 +1135,6 @@ void ClearWindow()
   }
   else
     SetDrawtoField(DRAW_BACKBUFFER);
-
-  if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
-  {
-    ClearRectangle(window, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
-    SetDrawtoField(DRAW_DIRECT);
-  }
 }
 
 void MarkTileDirty(int x, int y)
@@ -920,8 +1219,9 @@ inline int getGraphicAnimationFrame(int graphic, int sync_frame)
                           sync_frame);
 }
 
-void getSizedGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y,
-                          int tilesize_raw)
+void getSizedGraphicSourceExt(int graphic, int frame, int tilesize_raw,
+                             Bitmap **bitmap, int *x, int *y,
+                             boolean get_backside)
 {
   struct
   {
@@ -937,27 +1237,80 @@ void getSizedGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y,
     { 0, 1,    2, 3    },      /* 16 x 16 */
     { 0, 1,    0, 1    },      /* 32 x 32 */
   };
+  struct GraphicInfo *g = &graphic_info[graphic];
+  Bitmap *src_bitmap = g->bitmap;
   int tilesize = MIN(MAX(1, tilesize_raw), TILESIZE);
   int offset_calc_pos = log_2(tilesize);
-  Bitmap *src_bitmap = graphic_info[graphic].bitmap;
   int width_mult  = offset_calc[offset_calc_pos].width_mult;
   int width_div   = offset_calc[offset_calc_pos].width_div;
   int height_mult = offset_calc[offset_calc_pos].height_mult;
   int height_div  = offset_calc[offset_calc_pos].height_div;
   int startx = src_bitmap->width * width_mult / width_div;
   int starty = src_bitmap->height * height_mult / height_div;
-  int src_x = startx + graphic_info[graphic].src_x * tilesize / TILESIZE;
-  int src_y = starty + graphic_info[graphic].src_y * tilesize / TILESIZE;
+#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;
+  int offset_y = g->offset_y * tilesize / TILESIZE;
+
+  if (g->offset_y == 0)                /* frames are ordered horizontally */
+  {
+    int max_width = g->anim_frames_per_line * width;
+    int pos = (src_y / height) * max_width + src_x + frame * offset_x;
+
+    src_x = pos % max_width;
+    src_y = src_y % height + pos / max_width * height;
+  }
+  else if (g->offset_x == 0)   /* frames are ordered vertically */
+  {
+    int max_height = g->anim_frames_per_line * height;
+    int pos = (src_x / width) * max_height + src_y + frame * offset_y;
+
+    src_x = src_x % width + pos / max_height * width;
+    src_y = pos % max_height;
+  }
+  else                         /* frames are ordered diagonally */
+  {
+    src_x = src_x + frame * offset_x;
+    src_y = src_y + frame * offset_y;
+  }
 
   *bitmap = src_bitmap;
-  *x = src_x;
-  *y = src_y;
+  *x = startx + src_x;
+  *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
-  getSizedGraphicSource(graphic, bitmap, x, y, MINI_TILESIZE);
+  getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
 #else
   struct GraphicInfo *g = &graphic_info[graphic];
   int mini_startx = 0;
@@ -976,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 */
@@ -1017,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);
 }
 
@@ -1028,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);
 }
 
@@ -1042,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);
 }
 
@@ -1055,24 +1470,43 @@ 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);
 }
 
-void DrawSizedGraphic(int x, int y, int graphic, int tilesize)
+void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
 {
   DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
-                     tilesize);
+                     frame, tilesize);
   MarkTileDirty(x / tilesize, y / tilesize);
 }
 
-void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int tilesize)
+void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
+                        int tilesize)
 {
   Bitmap *src_bitmap;
   int src_x, src_y;
 
-  getSizedGraphicSource(graphic, &src_bitmap, &src_x, &src_y, tilesize);
+  getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
 }
 
@@ -1174,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);
@@ -1181,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)
     {
@@ -1206,26 +1654,64 @@ 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);
   int y2 = y + SIGN(dy);
+#if 0
+  /* !!! DOES NOT WORK FOR SLOW MOVEMENT !!! */
+  int sync_frame = GfxFrame[LEVELX(x)][LEVELY(y)];
+#else
+  /* movement with two-tile animations must be sync'ed with movement position,
+     not with current GfxFrame (which can be higher when using slow movement) */
+  int anim_pos = (dx ? ABS(dx) : ABS(dy));
   int anim_frames = graphic_info[graphic].anim_frames;
-  int sync_frame = (dx ? ABS(dx) : ABS(dy)) * anim_frames / TILESIZE;
+#if 1
+  /* (we also need anim_delay here for movement animations with less frames) */
+  int anim_delay = graphic_info[graphic].anim_delay;
+  int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
+#else
+  int sync_frame = anim_pos * anim_frames / TILESIZE;
+#endif
+#endif
   boolean draw_start_tile = (cut_mode != CUT_ABOVE);   /* only for falling! */
   boolean draw_end_tile   = (cut_mode != CUT_BELOW);   /* only for falling! */
 
   /* re-calculate animation frame for two-tile movement animation */
   frame = getGraphicAnimationFrame(graphic, sync_frame);
 
+#if 0
+#if 0
+  printf("::: %d, %d, %d => %d [%d]\n",
+        anim_pos, anim_frames, anim_delay, sync_frame, graphic);
+#else
+  printf("::: %d, %d => %d\n",
+        anim_pos, anim_frames, sync_frame);
+#endif
+#endif
+
+#if 0
+  printf("::: %d [%d, %d] [%d] [%d]\n", frame, sync_frame, dy,
+        GfxFrame[LEVELX(x)][LEVELY(y)], mask_mode);
+#endif
+
   /* check if movement start graphic inside screen area and should be drawn */
   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
   {
     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)
     {
@@ -1246,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)
     {
@@ -1375,14 +1866,242 @@ void DrawLevelFieldThruMask(int x, int y)
   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
 }
 
-static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
+/* !!! implementation of quicksand is totally broken !!! */
+#define IS_CRUMBLED_TILE(x, y, e)                                      \
+       (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
+                            !IS_MOVING(x, y) ||                        \
+                            (e) == EL_QUICKSAND_EMPTYING ||            \
+                            (e) == EL_QUICKSAND_FAST_EMPTYING))
+
+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 crumbled_border_size = graphic_info[graphic].border_size;
+  int i;
+
+  getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+
+  for (i = 1; i < 4; i++)
+  {
+    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);
+
+    /* 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);
+
+    /* return if check prevents inner corner */
+    if (same == (dxx == dx && dyy == dy))
+      return;
+  }
+
+  /* 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 element;
-  int width, height, cx, cy, i;
   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 (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 },
@@ -1397,13 +2116,11 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
   element = TILE_GFX_ELEMENT(x, y);
 
   /* crumble field itself */
-  if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
+  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];
@@ -1412,32 +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 */
-      if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
+      /* check if neighbour field is of same crumble type */
+#if 1
+      if (IS_CRUMBLED_TILE(xx, yy, element) &&
+         graphic_info[graphic].class ==
+         graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
+       continue;
+#else
+      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++)
     {
@@ -1447,8 +2167,7 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
       int syy = sy + xy[i][1];
 
       if (!IN_LEV_FIELD(xx, yy) ||
-         !IN_SCR_FIELD(sxx, syy) ||
-         IS_MOVING(xx, yy))
+         !IN_SCR_FIELD(sxx, syy))
        continue;
 
       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
@@ -1456,38 +2175,19 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
 
       element = TILE_GFX_ELEMENT(xx, yy);
 
-      if (!GFX_CRUMBLED(element))
+      if (!IS_CRUMBLED_TILE(xx, yy, element))
        continue;
 
       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;
 
@@ -1506,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;
   }
@@ -1518,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);
@@ -1531,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] =
@@ -1589,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)
@@ -1611,6 +2311,7 @@ void DrawScreenField(int x, int y)
       element = getBorderElement(lx, ly);
 
     DrawScreenElement(x, y, element);
+
     return;
   }
 
@@ -1636,8 +2337,22 @@ void DrawScreenField(int x, int y)
             element == EL_DC_MAGIC_WALL_FILLING)
       cut_mode = CUT_BELOW;
 
+#if 0
+    if (lx == 9 && ly == 1)
+      printf("::: %s [%d] [%d, %d] [%d]\n",
+            EL_NAME(TILE_GFX_ELEMENT(lx, ly)),
+            el_act2crm(TILE_GFX_ELEMENT(lx, ly), ACTION_DEFAULT),
+            element_info[EL_QUICKSAND_EMPTYING].graphic[ACTION_DEFAULT],
+            element_info[EL_QUICKSAND_EMPTYING].crumbled[ACTION_DEFAULT],
+            GFX_CRUMBLED(TILE_GFX_ELEMENT(lx, ly)));
+#endif
+
     if (cut_mode == CUT_ABOVE)
+#if 1
+      DrawScreenElement(x, y, element);
+#else
       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
+#endif
     else
       DrawScreenElement(x, y, EL_EMPTY);
 
@@ -1646,8 +2361,16 @@ void DrawScreenField(int x, int y)
     else if (cut_mode == NO_CUTTING)
       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
     else
+    {
       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
 
+#if 1
+      if (cut_mode == CUT_BELOW &&
+         IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
+       DrawLevelElement(lx, ly + 1, element);
+#endif
+    }
+
     if (content == EL_ACID)
     {
       int dir = MovDir[lx][ly];
@@ -1760,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)
   {
@@ -1792,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;
@@ -1808,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;
@@ -1816,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)
@@ -1888,13 +2795,134 @@ 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;
   int src_x, src_y;
   int graphic = el2preimg(element);
 
-  getSizedGraphicSource(graphic, &src_bitmap, &src_x, &src_y, tilesize);
+  getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
   BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
 }
 
@@ -1902,8 +2930,14 @@ void DrawLevel()
 {
   int x,y;
 
+#if 1
+  SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
+  SetDrawBackgroundMask(REDRAW_FIELD);
+#else
   SetDrawBackgroundMask(REDRAW_NONE);
-  ClearWindow();
+#endif
+
+  ClearField();
 
   for (x = BX1; x <= BX2; x++)
     for (y = BY1; y <= BY2; y++)
@@ -2061,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);
@@ -2256,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);
@@ -2263,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);
@@ -2294,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
 }
 
@@ -2317,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)
@@ -2374,6 +3437,8 @@ void DrawPlayerField(int x, int y)
   DrawPlayer(PLAYERINFO(x, y));
 }
 
+#define DRAW_PLAYER_OVER_PUSHED_ELEMENT        1
+
 void DrawPlayer(struct PlayerInfo *player)
 {
   int jx = player->jx;
@@ -2470,9 +3535,6 @@ void DrawPlayer(struct PlayerInfo *player)
   if (!IN_SCR_FIELD(sx, sy))
     return;
 
-  if (setup.direct_draw)
-    SetDrawtoField(DRAW_BUFFERED);
-
   /* ----------------------------------------------------------------------- */
   /* draw things behind the player, if needed                                */
   /* ----------------------------------------------------------------------- */
@@ -2490,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);
 
@@ -2516,6 +3578,7 @@ void DrawPlayer(struct PlayerInfo *player)
     }
   }
 
+#if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
   /* ----------------------------------------------------------------------- */
   /* draw player himself                                                     */
   /* ----------------------------------------------------------------------- */
@@ -2553,8 +3616,19 @@ void DrawPlayer(struct PlayerInfo *player)
 
     DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
   }
+#endif
 
-  /* ----------------------------------------------------------------------- */
+#if DRAW_PLAYER_OVER_PUSHED_ELEMENT
+  if (player->GfxPos)
+  {
+    if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
+      sxx = player->GfxPos;
+    else
+      syy = player->GfxPos;
+  }
+#endif
+
+  /* ----------------------------------------------------------------------- */
   /* draw things the player is pushing, if needed                            */
   /* ----------------------------------------------------------------------- */
 
@@ -2592,11 +3666,90 @@ void DrawPlayer(struct PlayerInfo *player)
 #endif
 
     /* draw background element under pushed element (like the Sokoban field) */
+#if 1
+    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
+       DrawLevelElement(jx, jy, EL_EMPTY);
+
+      if (Back[next_jx][next_jy])
+       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
+      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]);
+#endif
 
+#if 0
+    printf("::: %d, %d, %d, %d [%d] [%d, %d, %d] [%d] [%d, %d] [%d, %d]\n",
+          jx, px, player->GfxPos, player->StepFrame,
+          player->is_pushing,
+          dx, sxx, pxx,
+          IS_MOVING(jx, jy),
+          graphic, frame,
+          GfxFrame[jx][jy], GfxFrame[next_jx][next_jy]);
+#endif
+
+#if 1
+    /* do not draw (EM style) pushing animation when pushing is finished */
+    /* (two-tile animations usually do not contain start and end frame) */
+    if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
+      DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
+    else
+      DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
+#else
     /* masked drawing is needed for EMC style (double) movement graphics */
+    /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
+#endif
+  }
+#endif
+
+#if DRAW_PLAYER_OVER_PUSHED_ELEMENT
+  /* ----------------------------------------------------------------------- */
+  /* draw player himself                                                     */
+  /* ----------------------------------------------------------------------- */
+
+  graphic = getPlayerGraphic(player, move_dir);
+
+  /* in the case of changed player action or direction, prevent the current
+     animation frame from being restarted for identical animations */
+  if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
+    player->Frame = last_player_frame;
+
+  frame = getGraphicAnimationFrame(graphic, player->Frame);
+
+  if (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
 
@@ -2651,18 +3804,6 @@ void DrawPlayer(struct PlayerInfo *player)
       DrawLevelFieldThruMask(jx, jy);
   }
 
-  if (setup.direct_draw)
-  {
-    int dst_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
-    int dst_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
-    int x_size = TILEX * (1 + ABS(jx - last_jx));
-    int y_size = TILEY * (1 + ABS(jy - last_jy));
-
-    BlitBitmap(drawto_field, window,
-              dst_x, dst_y, x_size, y_size, dst_x, dst_y);
-    SetDrawtoField(DRAW_DIRECT);
-  }
-
   MarkTileDirty(sx, sy);
 }
 
@@ -2729,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);
@@ -2748,10 +3903,15 @@ boolean Request(char *text, unsigned int req_state)
       break;
     }
   }
+#endif
 
-  if (game_status == GAME_MODE_PLAYING &&
-      level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    BlitScreenToBitmap_EM(backbuffer);
+  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)
@@ -2775,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,
@@ -2798,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]);
@@ -2854,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))
   {
@@ -2871,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;
 
@@ -2965,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;
 
@@ -3000,7 +4218,6 @@ boolean Request(char *text, unsigned int req_state)
     if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
     {
       HandleGameActions();
-      BackToFront();
     }
     else
     {
@@ -3010,6 +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();
@@ -3030,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);
 
@@ -3120,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)
@@ -3173,6 +4409,11 @@ unsigned int MoveDoor(unsigned int door_state)
     door_state &= ~DOOR_CLOSE_ALL;
   }
 
+#if 1
+  if (game_status == GAME_MODE_EDITOR)
+    door_state |= DOOR_NO_DELAY;
+#endif
+
   if (door_state & DOOR_ACTION)
   {
     boolean handle_door_1 = (door_state & DOOR_ACTION_1);
@@ -3426,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
@@ -3535,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;
@@ -3584,6 +4921,8 @@ void CreateToolButtons()
   }
 }
 
+#endif
+
 void FreeToolButtons()
 {
   int i;
@@ -4821,6 +6160,32 @@ em_object_mapping_list[] =
     Xsand_stonein_4,                   FALSE,  TRUE,
     EL_ROCK,                           ACTION_FILLING, -1
   },
+#if 1
+  {
+    Xsand_stonesand_1,                 FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_2,                 FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_3,                 FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_4,                 FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_quickout_1,                FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_quickout_2,                FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+#else
   {
     Xsand_stonesand_1,                 FALSE,  FALSE,
     EL_QUICKSAND_FULL,                 -1, -1
@@ -4837,6 +6202,7 @@ em_object_mapping_list[] =
     Xsand_stonesand_4,                 FALSE,  FALSE,
     EL_QUICKSAND_FULL,                 -1, -1
   },
+#endif
   {
     Xsand_stoneout_1,                  FALSE,  FALSE,
     EL_ROCK,                           ACTION_EMPTYING, -1
@@ -4845,6 +6211,24 @@ em_object_mapping_list[] =
     Xsand_stoneout_2,                  FALSE,  FALSE,
     EL_ROCK,                           ACTION_EMPTYING, -1
   },
+#if 1
+  {
+    Xsand_sandstone_1,                 FALSE,  FALSE,
+    EL_QUICKSAND_FILLING,              -1, -1
+  },
+  {
+    Xsand_sandstone_2,                 FALSE,  FALSE,
+    EL_QUICKSAND_FILLING,              -1, -1
+  },
+  {
+    Xsand_sandstone_3,                 FALSE,  FALSE,
+    EL_QUICKSAND_FILLING,              -1, -1
+  },
+  {
+    Xsand_sandstone_4,                 FALSE,  FALSE,
+    EL_QUICKSAND_FILLING,              -1, -1
+  },
+#else
   {
     Xsand_sandstone_1,                 FALSE,  FALSE,
     EL_QUICKSAND_FULL,                 -1, -1
@@ -4861,6 +6245,7 @@ em_object_mapping_list[] =
     Xsand_sandstone_4,                 FALSE,  FALSE,
     EL_QUICKSAND_FULL,                 -1, -1
   },
+#endif
   {
     Xplant,                            TRUE,   FALSE,
     EL_EMC_PLANT,                      -1, -1
@@ -5650,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)
@@ -5856,7 +7287,7 @@ int getBeltDirFromBeltSwitchElement(int element)
   return belt_move_dir[belt_dir_nr];
 }
 
-int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
+int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
 {
   static int belt_base_element[4] =
   {
@@ -5865,12 +7296,18 @@ int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
     EL_CONVEYOR_BELT_3_LEFT,
     EL_CONVEYOR_BELT_4_LEFT
   };
-  int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
 
   return belt_base_element[belt_nr] + belt_dir_nr;
 }
 
-int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
+int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
+{
+  int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
+
+  return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
+}
+
+int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
 {
   static int belt_base_element[4] =
   {
@@ -5879,11 +7316,17 @@ int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
     EL_CONVEYOR_BELT_4_SWITCH_LEFT
   };
-  int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
 
   return belt_base_element[belt_nr] + belt_dir_nr;
 }
 
+int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
+{
+  int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
+
+  return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
+}
+
 int getNumActivePlayers_EM()
 {
   int num_players = 0;
@@ -5914,10 +7357,12 @@ 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);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    return InitEngineRandom_SP(seed);
   else
     return InitEngineRandom_RND(seed);
 }
@@ -5927,50 +7372,877 @@ static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
 #endif
 
+inline static int get_effective_element_EM(int tile, int frame_em)
+{
+  int element             = object_mapping[tile].element_rnd;
+  int action              = object_mapping[tile].action;
+  boolean is_backside     = object_mapping[tile].is_backside;
+  boolean action_removing = (action == ACTION_DIGGING ||
+                            action == ACTION_SNAPPING ||
+                            action == ACTION_COLLECTING);
+
+  if (frame_em < 7)
+  {
+    switch (tile)
+    {
+      case Yacid_splash_eB:
+      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;
+    }
+  }
+  else /* frame_em == 7 */
+  {
+    switch (tile)
+    {
+      case Yacid_splash_eB:
+      case Yacid_splash_wB:
+       return EL_EMPTY;
+
+      case Yemerald_stone:
+       return EL_EMERALD;
+
+      case Ydiamond_stone:
+       return EL_ROCK;
+
+      case Xdrip_stretch:
+      case Xdrip_stretchB:
+      case Ydrip_s1:
+      case Ydrip_s1B:
+      case Xball_1B:
+      case Xball_2:
+      case Xball_2B:
+      case Yball_eat:
+      case Ykey_1_eat:
+      case Ykey_2_eat:
+      case Ykey_3_eat:
+      case Ykey_4_eat:
+      case Ykey_5_eat:
+      case Ykey_6_eat:
+      case Ykey_7_eat:
+      case Ykey_8_eat:
+      case Ylenses_eat:
+      case Ymagnify_eat:
+      case Ygrass_eat:
+      case Ydirt_eat:
+      case Xsand_stonein_1:
+      case Xsand_stonein_2:
+      case Xsand_stonein_3:
+      case Xsand_stonein_4:
+       return element;
+
+      default:
+       return (is_backside || action_removing ? EL_EMPTY : element);
+    }
+  }
+}
+
+inline static boolean check_linear_animation_EM(int tile)
+{
+  switch (tile)
+  {
+    case Xsand_stonesand_1:
+    case Xsand_stonesand_quickout_1:
+    case Xsand_sandstone_1:
+    case Xsand_stonein_1:
+    case Xsand_stoneout_1:
+    case Xboom_1:
+    case Xdynamite_1:
+    case Ybug_w_n:
+    case Ybug_n_e:
+    case Ybug_e_s:
+    case Ybug_s_w:
+    case Ybug_e_n:
+    case Ybug_s_e:
+    case Ybug_w_s:
+    case Ybug_n_w:
+    case Ytank_w_n:
+    case Ytank_n_e:
+    case Ytank_e_s:
+    case Ytank_s_w:
+    case Ytank_e_n:
+    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;
+  }
+
+  return FALSE;
+}
+
+inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
+                                           boolean has_crumbled_graphics,
+                                           int crumbled, int sync_frame)
+{
+  /* if element can be crumbled, but certain action graphics are just empty
+     space (like instantly snapping sand to empty space in 1 frame), do not
+     treat these empty space graphics as crumbled graphics in EMC engine */
+  if (crumbled == IMG_EMPTY_SPACE)
+    has_crumbled_graphics = FALSE;
+
+  if (has_crumbled_graphics)
+  {
+    struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+    int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
+                                          g_crumbled->anim_delay,
+                                          g_crumbled->anim_mode,
+                                          g_crumbled->anim_start_frame,
+                                          sync_frame);
+
+    getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
+                    &g_em->crumbled_src_x, &g_em->crumbled_src_y);
+
+    g_em->crumbled_border_size = graphic_info[crumbled].border_size;
+
+    g_em->has_crumbled_graphics = TRUE;
+  }
+  else
+  {
+    g_em->crumbled_bitmap = NULL;
+    g_em->crumbled_src_x = 0;
+    g_em->crumbled_src_y = 0;
+    g_em->crumbled_border_size = 0;
+
+    g_em->has_crumbled_graphics = FALSE;
+  }
+}
+
 void ResetGfxAnimation_EM(int x, int y, int tile)
 {
   GfxFrame[x][y] = 0;
 }
 
-void getGraphicSourceObjectExt_EM(int tile, int frame_em,
-                                 Bitmap **src_bitmap, int *src_x, int *src_y,
-                                 int x, int y)
-{
-  int element         = object_mapping[tile].element_rnd;
-  int action          = object_mapping[tile].action;
-  int direction       = object_mapping[tile].direction;
-  boolean is_backside = object_mapping[tile].is_backside;
-  boolean action_removing = (action == ACTION_DIGGING ||
-                            action == ACTION_SNAPPING ||
-                            action == ACTION_COLLECTING);
-  int effective_element = (frame_em > 0 ? element :
-                          is_backside ? EL_EMPTY :
-                          action_removing ? EL_EMPTY :
-                          element);
-  int graphic = (direction == MV_NONE ?
-                el_act2img(effective_element, action) :
-                el_act_dir2img(effective_element, action, direction));
-  struct GraphicInfo *g = &graphic_info[graphic];
-  int sync_frame;
+void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
+                       int tile, int frame_em, int x, int y)
+{
+  int action = object_mapping[tile].action;
+#if 1
+  int direction = object_mapping[tile].direction;
+  int effective_element = get_effective_element_EM(tile, frame_em);
+  int graphic = (direction == MV_NONE ?
+                el_act2img(effective_element, action) :
+                el_act_dir2img(effective_element, action, direction));
+  struct GraphicInfo *g = &graphic_info[graphic];
+  int sync_frame;
+#endif
+  boolean action_removing = (action == ACTION_DIGGING ||
+                            action == ACTION_SNAPPING ||
+                            action == ACTION_COLLECTING);
+  boolean action_moving   = (action == ACTION_FALLING ||
+                            action == ACTION_MOVING ||
+                            action == ACTION_PUSHING ||
+                            action == ACTION_EATING ||
+                            action == ACTION_FILLING ||
+                            action == ACTION_EMPTYING);
+  boolean action_falling  = (action == ACTION_FALLING ||
+                            action == ACTION_FILLING ||
+                            action == ACTION_EMPTYING);
+
+  /* special case: graphic uses "2nd movement tile" and has defined
+     7 frames for movement animation (or less) => use default graphic
+     for last (8th) frame which ends the movement animation */
+  if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
+  {
+    action = ACTION_DEFAULT;   /* (keep action_* unchanged for now) */
+    graphic = (direction == MV_NONE ?
+              el_act2img(effective_element, action) :
+              el_act_dir2img(effective_element, action, direction));
+
+    g = &graphic_info[graphic];
+  }
+
+#if 0
+  if (tile == Xsand_stonesand_1 ||
+      tile == Xsand_stonesand_2 ||
+      tile == Xsand_stonesand_3 ||
+      tile == Xsand_stonesand_4)
+    printf("::: 1: quicksand frame %d [%d]\n", GfxFrame[x][y], tile);
+#endif
+
+#if 1
+  if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
+  {
+    GfxFrame[x][y] = 0;
+
+    // printf("::: resetting... [%d]\n", tile);
+  }
+#else
+  if (action_removing || check_linear_animation_EM(tile))
+  {
+    GfxFrame[x][y] = frame_em;
+
+    // printf("::: resetting... [%d]\n", tile);
+  }
+#endif
+  else if (action_moving)
+  {
+    boolean is_backside = object_mapping[tile].is_backside;
+
+    if (is_backside)
+    {
+      int direction = object_mapping[tile].direction;
+      int move_dir = (action_falling ? MV_DOWN : direction);
+
+      GfxFrame[x][y]++;
+
+#if 1
+      /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
+      if (g->double_movement && frame_em == 0)
+      {
+       GfxFrame[x][y] = 0;
+
+       // printf("::: resetting... [%d]\n", tile);
+      }
+#endif
+
+      if (move_dir == MV_LEFT)
+       GfxFrame[x - 1][y] = GfxFrame[x][y];
+      else if (move_dir == MV_RIGHT)
+       GfxFrame[x + 1][y] = GfxFrame[x][y];
+      else if (move_dir == MV_UP)
+       GfxFrame[x][y - 1] = GfxFrame[x][y];
+      else if (move_dir == MV_DOWN)
+       GfxFrame[x][y + 1] = GfxFrame[x][y];
+    }
+  }
+  else
+  {
+    GfxFrame[x][y]++;
+
+    /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
+    if (tile == Xsand_stonesand_quickout_1 ||
+       tile == Xsand_stonesand_quickout_2)
+      GfxFrame[x][y]++;
+  }
+
+#if 0
+  if (tile == Xsand_stonesand_1 ||
+      tile == Xsand_stonesand_2 ||
+      tile == Xsand_stonesand_3 ||
+      tile == Xsand_stonesand_4)
+    printf("::: 2: quicksand frame %d [%d]\n", GfxFrame[x][y], tile);
+#endif
+
+#if 1
+  if (graphic_info[graphic].anim_global_sync)
+    sync_frame = FrameCounter;
+  else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
+    sync_frame = GfxFrame[x][y];
+  else
+    sync_frame = 0;    /* playfield border (pseudo steel) */
+
+  SetRandomAnimationValue(x, y);
+
+  int frame = getAnimationFrame(g->anim_frames,
+                               g->anim_delay,
+                               g->anim_mode,
+                               g->anim_start_frame,
+                               sync_frame);
+
+  g_em->unique_identifier =
+    (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
+#endif
+}
+
+void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
+                                 int tile, int frame_em, int x, int y)
+{
+  int action = object_mapping[tile].action;
+  int direction = object_mapping[tile].direction;
+  boolean is_backside = object_mapping[tile].is_backside;
+  int effective_element = get_effective_element_EM(tile, frame_em);
+#if 1
+  int effective_action = action;
+#else
+  int effective_action = (frame_em < 7 ? action : ACTION_DEFAULT);
+#endif
+  int graphic = (direction == MV_NONE ?
+                el_act2img(effective_element, effective_action) :
+                el_act_dir2img(effective_element, effective_action,
+                               direction));
+  int crumbled = (direction == MV_NONE ?
+                 el_act2crm(effective_element, effective_action) :
+                 el_act_dir2crm(effective_element, effective_action,
+                                direction));
+  int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
+  int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
+  boolean has_crumbled_graphics = (base_crumbled != base_graphic);
+  struct GraphicInfo *g = &graphic_info[graphic];
+#if 0
+  struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+#endif
+  int sync_frame;
+
+  /* special case: graphic uses "2nd movement tile" and has defined
+     7 frames for movement animation (or less) => use default graphic
+     for last (8th) frame which ends the movement animation */
+  if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
+  {
+    effective_action = ACTION_DEFAULT;
+    graphic = (direction == MV_NONE ?
+              el_act2img(effective_element, effective_action) :
+              el_act_dir2img(effective_element, effective_action,
+                             direction));
+    crumbled = (direction == MV_NONE ?
+               el_act2crm(effective_element, effective_action) :
+               el_act_dir2crm(effective_element, effective_action,
+                              direction));
+
+    g = &graphic_info[graphic];
+  }
+
+#if 0
+  if (frame_em == 7)
+    return;
+#endif
+
+
+#if 0
+  if (frame_em == 0)   /* reset animation frame for certain elements */
+  {
+    if (check_linear_animation_EM(tile))
+      GfxFrame[x][y] = 0;
+  }
+#endif
+
+  if (graphic_info[graphic].anim_global_sync)
+    sync_frame = FrameCounter;
+  else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
+    sync_frame = GfxFrame[x][y];
+  else
+    sync_frame = 0;    /* playfield border (pseudo steel) */
+
+  SetRandomAnimationValue(x, y);
+
+#if 0
+  int i = tile;
+  int j = frame_em;
+  int xxx_sync_frame = (i == Xdrip_stretch ? 7 :
+                       i == Xdrip_stretchB ? 7 :
+                       i == Ydrip_s2 ? j + 8 :
+                       i == Ydrip_s2B ? j + 8 :
+                       i == Xacid_1 ? 0 :
+                       i == Xacid_2 ? 10 :
+                       i == Xacid_3 ? 20 :
+                       i == Xacid_4 ? 30 :
+                       i == Xacid_5 ? 40 :
+                       i == Xacid_6 ? 50 :
+                       i == Xacid_7 ? 60 :
+                       i == Xacid_8 ? 70 :
+                       i == Xfake_acid_1 ? 0 :
+                       i == Xfake_acid_2 ? 10 :
+                       i == Xfake_acid_3 ? 20 :
+                       i == Xfake_acid_4 ? 30 :
+                       i == Xfake_acid_5 ? 40 :
+                       i == Xfake_acid_6 ? 50 :
+                       i == Xfake_acid_7 ? 60 :
+                       i == Xfake_acid_8 ? 70 :
+                       i == Xball_2 ? 7 :
+                       i == Xball_2B ? j + 8 :
+                       i == Yball_eat ? j + 1 :
+                       i == Ykey_1_eat ? j + 1 :
+                       i == Ykey_2_eat ? j + 1 :
+                       i == Ykey_3_eat ? j + 1 :
+                       i == Ykey_4_eat ? j + 1 :
+                       i == Ykey_5_eat ? j + 1 :
+                       i == Ykey_6_eat ? j + 1 :
+                       i == Ykey_7_eat ? j + 1 :
+                       i == Ykey_8_eat ? j + 1 :
+                       i == Ylenses_eat ? j + 1 :
+                       i == Ymagnify_eat ? j + 1 :
+                       i == Ygrass_eat ? j + 1 :
+                       i == Ydirt_eat ? j + 1 :
+                       i == Xamoeba_1 ? 0 :
+                       i == Xamoeba_2 ? 1 :
+                       i == Xamoeba_3 ? 2 :
+                       i == Xamoeba_4 ? 3 :
+                       i == Xamoeba_5 ? 0 :
+                       i == Xamoeba_6 ? 1 :
+                       i == Xamoeba_7 ? 2 :
+                       i == Xamoeba_8 ? 3 :
+                       i == Xexit_2 ? j + 8 :
+                       i == Xexit_3 ? j + 16 :
+                       i == Xdynamite_1 ? 0 :
+                       i == Xdynamite_2 ? 8 :
+                       i == Xdynamite_3 ? 16 :
+                       i == Xdynamite_4 ? 24 :
+                       i == Xsand_stonein_1 ? j + 1 :
+                       i == Xsand_stonein_2 ? j + 9 :
+                       i == Xsand_stonein_3 ? j + 17 :
+                       i == Xsand_stonein_4 ? j + 25 :
+                       i == Xsand_stoneout_1 && j == 0 ? 0 :
+                       i == Xsand_stoneout_1 && j == 1 ? 0 :
+                       i == Xsand_stoneout_1 && j == 2 ? 1 :
+                       i == Xsand_stoneout_1 && j == 3 ? 2 :
+                       i == Xsand_stoneout_1 && j == 4 ? 2 :
+                       i == Xsand_stoneout_1 && j == 5 ? 3 :
+                       i == Xsand_stoneout_1 && j == 6 ? 4 :
+                       i == Xsand_stoneout_1 && j == 7 ? 4 :
+                       i == Xsand_stoneout_2 && j == 0 ? 5 :
+                       i == Xsand_stoneout_2 && j == 1 ? 6 :
+                       i == Xsand_stoneout_2 && j == 2 ? 7 :
+                       i == Xsand_stoneout_2 && j == 3 ? 8 :
+                       i == Xsand_stoneout_2 && j == 4 ? 9 :
+                       i == Xsand_stoneout_2 && j == 5 ? 11 :
+                       i == Xsand_stoneout_2 && j == 6 ? 13 :
+                       i == Xsand_stoneout_2 && j == 7 ? 15 :
+                       i == Xboom_bug && j == 1 ? 2 :
+                       i == Xboom_bug && j == 2 ? 2 :
+                       i == Xboom_bug && j == 3 ? 4 :
+                       i == Xboom_bug && j == 4 ? 4 :
+                       i == Xboom_bug && j == 5 ? 2 :
+                       i == Xboom_bug && j == 6 ? 2 :
+                       i == Xboom_bug && j == 7 ? 0 :
+                       i == Xboom_bomb && j == 1 ? 2 :
+                       i == Xboom_bomb && j == 2 ? 2 :
+                       i == Xboom_bomb && j == 3 ? 4 :
+                       i == Xboom_bomb && j == 4 ? 4 :
+                       i == Xboom_bomb && j == 5 ? 2 :
+                       i == Xboom_bomb && j == 6 ? 2 :
+                       i == Xboom_bomb && j == 7 ? 0 :
+                       i == Xboom_android && j == 7 ? 6 :
+                       i == Xboom_1 && j == 1 ? 2 :
+                       i == Xboom_1 && j == 2 ? 2 :
+                       i == Xboom_1 && j == 3 ? 4 :
+                       i == Xboom_1 && j == 4 ? 4 :
+                       i == Xboom_1 && j == 5 ? 6 :
+                       i == Xboom_1 && j == 6 ? 6 :
+                       i == Xboom_1 && j == 7 ? 8 :
+                       i == Xboom_2 && j == 0 ? 8 :
+                       i == Xboom_2 && j == 1 ? 8 :
+                       i == Xboom_2 && j == 2 ? 10 :
+                       i == Xboom_2 && j == 3 ? 10 :
+                       i == Xboom_2 && j == 4 ? 10 :
+                       i == Xboom_2 && j == 5 ? 12 :
+                       i == Xboom_2 && j == 6 ? 12 :
+                       i == Xboom_2 && j == 7 ? 12 :
+#if 0
+                       special_animation && j == 4 ? 3 :
+                       effective_action != action ? 0 :
+#endif
+                       j);
+#endif
+
+#if 0
+  int xxx_effective_action;
+  int xxx_has_action_graphics;
+
+  {
+    int element = object_mapping[i].element_rnd;
+    int action = object_mapping[i].action;
+    int direction = object_mapping[i].direction;
+    boolean is_backside = object_mapping[i].is_backside;
+#if 0
+    boolean action_removing = (action == ACTION_DIGGING ||
+                              action == ACTION_SNAPPING ||
+                              action == ACTION_COLLECTING);
+#endif
+    boolean action_exploding = ((action == ACTION_EXPLODING ||
+                                action == ACTION_SMASHED_BY_ROCK ||
+                                action == ACTION_SMASHED_BY_SPRING) &&
+                               element != EL_DIAMOND);
+    boolean action_active = (action == ACTION_ACTIVE);
+    boolean action_other = (action == ACTION_OTHER);
+
+    {
+#if 1
+      int effective_element = get_effective_element_EM(i, j);
+#else
+      int effective_element = (j > 5 && i == Yacid_splash_eB ? EL_EMPTY :
+                              j > 5 && i == Yacid_splash_wB ? EL_EMPTY :
+                              j < 7 ? element :
+                              i == Xdrip_stretch ? element :
+                              i == Xdrip_stretchB ? element :
+                              i == Ydrip_s1 ? element :
+                              i == Ydrip_s1B ? element :
+                              i == Xball_1B ? element :
+                              i == Xball_2 ? element :
+                              i == Xball_2B ? element :
+                              i == Yball_eat ? element :
+                              i == Ykey_1_eat ? element :
+                              i == Ykey_2_eat ? element :
+                              i == Ykey_3_eat ? element :
+                              i == Ykey_4_eat ? element :
+                              i == Ykey_5_eat ? element :
+                              i == Ykey_6_eat ? element :
+                              i == Ykey_7_eat ? element :
+                              i == Ykey_8_eat ? element :
+                              i == Ylenses_eat ? element :
+                              i == Ymagnify_eat ? element :
+                              i == Ygrass_eat ? element :
+                              i == Ydirt_eat ? element :
+                              i == Yemerald_stone ? EL_EMERALD :
+                              i == Ydiamond_stone ? EL_ROCK :
+                              i == Xsand_stonein_1 ? element :
+                              i == Xsand_stonein_2 ? element :
+                              i == Xsand_stonein_3 ? element :
+                              i == Xsand_stonein_4 ? element :
+                              is_backside ? EL_EMPTY :
+                              action_removing ? EL_EMPTY :
+                              element);
+#endif
+      int effective_action = (j < 7 ? action :
+                             i == Xdrip_stretch ? action :
+                             i == Xdrip_stretchB ? action :
+                             i == Ydrip_s1 ? action :
+                             i == Ydrip_s1B ? action :
+                             i == Xball_1B ? action :
+                             i == Xball_2 ? action :
+                             i == Xball_2B ? action :
+                             i == Yball_eat ? action :
+                             i == Ykey_1_eat ? action :
+                             i == Ykey_2_eat ? action :
+                             i == Ykey_3_eat ? action :
+                             i == Ykey_4_eat ? action :
+                             i == Ykey_5_eat ? action :
+                             i == Ykey_6_eat ? action :
+                             i == Ykey_7_eat ? action :
+                             i == Ykey_8_eat ? action :
+                             i == Ylenses_eat ? action :
+                             i == Ymagnify_eat ? action :
+                             i == Ygrass_eat ? action :
+                             i == Ydirt_eat ? action :
+                             i == Xsand_stonein_1 ? action :
+                             i == Xsand_stonein_2 ? action :
+                             i == Xsand_stonein_3 ? action :
+                             i == Xsand_stonein_4 ? action :
+                             i == Xsand_stoneout_1 ? action :
+                             i == Xsand_stoneout_2 ? action :
+                             i == Xboom_android ? ACTION_EXPLODING :
+                             action_exploding ? ACTION_EXPLODING :
+                             action_active ? action :
+                             action_other ? action :
+                             ACTION_DEFAULT);
+      int graphic = (el_act_dir2img(effective_element, effective_action,
+                                   direction));
+      int crumbled = (el_act_dir2crm(effective_element, effective_action,
+                                    direction));
+      int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
+      int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
+      boolean has_action_graphics = (graphic != base_graphic);
+      boolean has_crumbled_graphics = (base_crumbled != base_graphic);
+      struct GraphicInfo *g = &graphic_info[graphic];
+#if 0
+      struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+#endif
+      struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
+      Bitmap *src_bitmap;
+      int src_x, src_y;
+      /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
+      boolean special_animation = (action != ACTION_DEFAULT &&
+                                  g->anim_frames == 3 &&
+                                  g->anim_delay == 2 &&
+                                  g->anim_mode & ANIM_LINEAR);
+      xxx_sync_frame = (i == Xdrip_stretch ? 7 :
+                       i == Xdrip_stretchB ? 7 :
+                       i == Ydrip_s2 ? j + 8 :
+                       i == Ydrip_s2B ? j + 8 :
+                       i == Xacid_1 ? 0 :
+                       i == Xacid_2 ? 10 :
+                       i == Xacid_3 ? 20 :
+                       i == Xacid_4 ? 30 :
+                       i == Xacid_5 ? 40 :
+                       i == Xacid_6 ? 50 :
+                       i == Xacid_7 ? 60 :
+                       i == Xacid_8 ? 70 :
+                       i == Xfake_acid_1 ? 0 :
+                       i == Xfake_acid_2 ? 10 :
+                       i == Xfake_acid_3 ? 20 :
+                       i == Xfake_acid_4 ? 30 :
+                       i == Xfake_acid_5 ? 40 :
+                       i == Xfake_acid_6 ? 50 :
+                       i == Xfake_acid_7 ? 60 :
+                       i == Xfake_acid_8 ? 70 :
+                       i == Xball_2 ? 7 :
+                       i == Xball_2B ? j + 8 :
+                       i == Yball_eat ? j + 1 :
+                       i == Ykey_1_eat ? j + 1 :
+                       i == Ykey_2_eat ? j + 1 :
+                       i == Ykey_3_eat ? j + 1 :
+                       i == Ykey_4_eat ? j + 1 :
+                       i == Ykey_5_eat ? j + 1 :
+                       i == Ykey_6_eat ? j + 1 :
+                       i == Ykey_7_eat ? j + 1 :
+                       i == Ykey_8_eat ? j + 1 :
+                       i == Ylenses_eat ? j + 1 :
+                       i == Ymagnify_eat ? j + 1 :
+                       i == Ygrass_eat ? j + 1 :
+                       i == Ydirt_eat ? j + 1 :
+                       i == Xamoeba_1 ? 0 :
+                       i == Xamoeba_2 ? 1 :
+                       i == Xamoeba_3 ? 2 :
+                       i == Xamoeba_4 ? 3 :
+                       i == Xamoeba_5 ? 0 :
+                       i == Xamoeba_6 ? 1 :
+                       i == Xamoeba_7 ? 2 :
+                       i == Xamoeba_8 ? 3 :
+                       i == Xexit_2 ? j + 8 :
+                       i == Xexit_3 ? j + 16 :
+                       i == Xdynamite_1 ? 0 :
+                       i == Xdynamite_2 ? 8 :
+                       i == Xdynamite_3 ? 16 :
+                       i == Xdynamite_4 ? 24 :
+                       i == Xsand_stonein_1 ? j + 1 :
+                       i == Xsand_stonein_2 ? j + 9 :
+                       i == Xsand_stonein_3 ? j + 17 :
+                       i == Xsand_stonein_4 ? j + 25 :
+                       i == Xsand_stoneout_1 && j == 0 ? 0 :
+                       i == Xsand_stoneout_1 && j == 1 ? 0 :
+                       i == Xsand_stoneout_1 && j == 2 ? 1 :
+                       i == Xsand_stoneout_1 && j == 3 ? 2 :
+                       i == Xsand_stoneout_1 && j == 4 ? 2 :
+                       i == Xsand_stoneout_1 && j == 5 ? 3 :
+                       i == Xsand_stoneout_1 && j == 6 ? 4 :
+                       i == Xsand_stoneout_1 && j == 7 ? 4 :
+                       i == Xsand_stoneout_2 && j == 0 ? 5 :
+                       i == Xsand_stoneout_2 && j == 1 ? 6 :
+                       i == Xsand_stoneout_2 && j == 2 ? 7 :
+                       i == Xsand_stoneout_2 && j == 3 ? 8 :
+                       i == Xsand_stoneout_2 && j == 4 ? 9 :
+                       i == Xsand_stoneout_2 && j == 5 ? 11 :
+                       i == Xsand_stoneout_2 && j == 6 ? 13 :
+                       i == Xsand_stoneout_2 && j == 7 ? 15 :
+                       i == Xboom_bug && j == 1 ? 2 :
+                       i == Xboom_bug && j == 2 ? 2 :
+                       i == Xboom_bug && j == 3 ? 4 :
+                       i == Xboom_bug && j == 4 ? 4 :
+                       i == Xboom_bug && j == 5 ? 2 :
+                       i == Xboom_bug && j == 6 ? 2 :
+                       i == Xboom_bug && j == 7 ? 0 :
+                       i == Xboom_bomb && j == 1 ? 2 :
+                       i == Xboom_bomb && j == 2 ? 2 :
+                       i == Xboom_bomb && j == 3 ? 4 :
+                       i == Xboom_bomb && j == 4 ? 4 :
+                       i == Xboom_bomb && j == 5 ? 2 :
+                       i == Xboom_bomb && j == 6 ? 2 :
+                       i == Xboom_bomb && j == 7 ? 0 :
+                       i == Xboom_android && j == 7 ? 6 :
+                       i == Xboom_1 && j == 1 ? 2 :
+                       i == Xboom_1 && j == 2 ? 2 :
+                       i == Xboom_1 && j == 3 ? 4 :
+                       i == Xboom_1 && j == 4 ? 4 :
+                       i == Xboom_1 && j == 5 ? 6 :
+                       i == Xboom_1 && j == 6 ? 6 :
+                       i == Xboom_1 && j == 7 ? 8 :
+                       i == Xboom_2 && j == 0 ? 8 :
+                       i == Xboom_2 && j == 1 ? 8 :
+                       i == Xboom_2 && j == 2 ? 10 :
+                       i == Xboom_2 && j == 3 ? 10 :
+                       i == Xboom_2 && j == 4 ? 10 :
+                       i == Xboom_2 && j == 5 ? 12 :
+                       i == Xboom_2 && j == 6 ? 12 :
+                       i == Xboom_2 && j == 7 ? 12 :
+                       special_animation && j == 4 ? 3 :
+                       effective_action != action ? 0 :
+                       j);
+
+      xxx_effective_action = effective_action;
+      xxx_has_action_graphics = has_action_graphics;
+    }
+  }
+#endif
+
+  int frame = getAnimationFrame(g->anim_frames,
+                               g->anim_delay,
+                               g->anim_mode,
+                               g->anim_start_frame,
+                               sync_frame);
+
+
+#if 0
+  return;
+#endif
+
+#if 0
+  if (frame_em == 7)
+    return;
+#endif
+
+#if 0
+  int old_src_x = g_em->src_x;
+  int old_src_y = g_em->src_y;
+#endif
+
+#if 1
+  getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
+                     g->double_movement && is_backside);
+#else
+  getGraphicSourceExt(graphic, frame, &g_em->bitmap,
+                     &g_em->src_x, &g_em->src_y, FALSE);
+#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
+
+#if 0
+  if (frame_em == 7)
+  {
+    if (graphic == IMG_BUG_MOVING_RIGHT)
+      printf("::: %d, %d, %d: %d, %d [%d, %d -> %d, %d]\n", graphic, x, y,
+            g->double_movement, is_backside,
+            old_src_x, old_src_y, g_em->src_x, g_em->src_y);
+
+    return;
+  }
+#endif
+
+
+#if 0
+  g_em->src_offset_x = 0;
+  g_em->src_offset_y = 0;
+  g_em->dst_offset_x = 0;
+  g_em->dst_offset_y = 0;
+  g_em->width  = TILEX;
+  g_em->height = TILEY;
+
+  g_em->preserve_background = FALSE;
+#endif
+
+  /* (updating the "crumbled" graphic definitions is probably not really needed,
+     as animations for crumbled graphics can't be longer than one EMC cycle) */
+#if 1
+  set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
+                          sync_frame);
+
+#else
+
+  g_em->crumbled_bitmap = NULL;
+  g_em->crumbled_src_x = 0;
+  g_em->crumbled_src_y = 0;
+
+  g_em->has_crumbled_graphics = FALSE;
+
+  if (has_crumbled_graphics && crumbled != IMG_EMPTY_SPACE)
+  {
+    int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
+                                          g_crumbled->anim_delay,
+                                          g_crumbled->anim_mode,
+                                          g_crumbled->anim_start_frame,
+                                          sync_frame);
+
+    getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
+                    &g_em->crumbled_src_x, &g_em->crumbled_src_y);
+
+    g_em->has_crumbled_graphics = TRUE;
+  }
+#endif
+
+#if 0
+ {
+   int effective_action = xxx_effective_action;
+   int has_action_graphics = xxx_has_action_graphics;
 
-  if (graphic_info[graphic].anim_global_sync)
-    sync_frame = FrameCounter;
-  else
-    sync_frame = 7 - frame_em;
+      if ((!g->double_movement && (effective_action == ACTION_FALLING ||
+                                  effective_action == ACTION_MOVING  ||
+                                  effective_action == ACTION_PUSHING ||
+                                  effective_action == ACTION_EATING)) ||
+         (!has_action_graphics && (effective_action == ACTION_FILLING ||
+                                   effective_action == ACTION_EMPTYING)))
+      {
+       int move_dir =
+         (effective_action == ACTION_FALLING ||
+          effective_action == ACTION_FILLING ||
+          effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
+       int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
+       int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? 1 : 0);
+       int num_steps = (i == Ydrip_s1  ? 16 :
+                        i == Ydrip_s1B ? 16 :
+                        i == Ydrip_s2  ? 16 :
+                        i == Ydrip_s2B ? 16 :
+                        i == Xsand_stonein_1 ? 32 :
+                        i == Xsand_stonein_2 ? 32 :
+                        i == Xsand_stonein_3 ? 32 :
+                        i == Xsand_stonein_4 ? 32 :
+                        i == Xsand_stoneout_1 ? 16 :
+                        i == Xsand_stoneout_2 ? 16 : 8);
+       int cx = ABS(dx) * (TILEX / num_steps);
+       int cy = ABS(dy) * (TILEY / num_steps);
+       int step_frame = (i == Ydrip_s2         ? j + 8 :
+                         i == Ydrip_s2B        ? j + 8 :
+                         i == Xsand_stonein_2  ? j + 8 :
+                         i == Xsand_stonein_3  ? j + 16 :
+                         i == Xsand_stonein_4  ? j + 24 :
+                         i == Xsand_stoneout_2 ? j + 8 : j) + 1;
+       int step = (is_backside ? step_frame : num_steps - step_frame);
 
-  SetRandomAnimationValue(x, y);
+       if (is_backside)        /* tile where movement starts */
+       {
+         if (dx < 0 || dy < 0)
+         {
+           g_em->src_offset_x = cx * step;
+           g_em->src_offset_y = cy * step;
+         }
+         else
+         {
+           g_em->dst_offset_x = cx * step;
+           g_em->dst_offset_y = cy * step;
+         }
+       }
+       else                    /* tile where movement ends */
+       {
+         if (dx < 0 || dy < 0)
+         {
+           g_em->dst_offset_x = cx * step;
+           g_em->dst_offset_y = cy * step;
+         }
+         else
+         {
+           g_em->src_offset_x = cx * step;
+           g_em->src_offset_y = cy * step;
+         }
+       }
 
-  int frame = getAnimationFrame(g->anim_frames,
-                               g->anim_delay,
-                               g->anim_mode,
-                               g->anim_start_frame,
-                               sync_frame);
+       g_em->width  = TILEX - cx * step;
+       g_em->height = TILEY - cy * step;
+      }
+
+      /* create unique graphic identifier to decide if tile must be redrawn */
+      /* bit 31 - 16 (16 bit): EM style graphic
+        bit 15 - 12 ( 4 bit): EM style frame
+        bit 11 -  6 ( 6 bit): graphic width
+        bit  5 -  0 ( 6 bit): graphic height */
+      g_em->unique_identifier =
+       (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
+ }
+#endif
 
-  getGraphicSourceExt(graphic, frame, src_bitmap, src_x, src_y, FALSE);
 }
 
-void getGraphicSourcePlayerExt_EM(int player_nr, int anim, int frame_em,
-                                 Bitmap **src_bitmap, int *src_x, int *src_y)
+void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
+                                 int player_nr, int anim, int frame_em)
 {
   int element   = player_mapping[player_nr][anim].element_rnd;
   int action    = player_mapping[player_nr][anim].action;
@@ -5983,10 +8255,19 @@ void getGraphicSourcePlayerExt_EM(int player_nr, int anim, int frame_em,
 
   InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
 
-  stored_player[player_nr].StepFrame = 7 - frame_em;
+  stored_player[player_nr].StepFrame = frame_em;
 
   sync_frame = stored_player[player_nr].Frame;
 
+  int frame = getAnimationFrame(g->anim_frames,
+                               g->anim_delay,
+                               g->anim_mode,
+                               g->anim_start_frame,
+                               sync_frame);
+
+  getGraphicSourceExt(graphic, frame, &g_em->bitmap,
+                     &g_em->src_x, &g_em->src_y, FALSE);
+
 #if 0
   printf("::: %d: %d, %d [%d]\n",
         player_nr,
@@ -5994,14 +8275,6 @@ void getGraphicSourcePlayerExt_EM(int player_nr, int anim, int frame_em,
         stored_player[player_nr].StepFrame,
         FrameCounter);
 #endif
-
-  int frame = getAnimationFrame(g->anim_frames,
-                               g->anim_delay,
-                               g->anim_mode,
-                               g->anim_start_frame,
-                               sync_frame);
-
-  getGraphicSourceExt(graphic, frame, src_bitmap, src_x, src_y, FALSE);
 }
 
 void InitGraphicInfo_EM(void)
@@ -6081,9 +8354,11 @@ void InitGraphicInfo_EM(void)
     int action = object_mapping[i].action;
     int direction = object_mapping[i].direction;
     boolean is_backside = object_mapping[i].is_backside;
+#if 0
     boolean action_removing = (action == ACTION_DIGGING ||
                               action == ACTION_SNAPPING ||
                               action == ACTION_COLLECTING);
+#endif
     boolean action_exploding = ((action == ACTION_EXPLODING ||
                                 action == ACTION_SMASHED_BY_ROCK ||
                                 action == ACTION_SMASHED_BY_SPRING) &&
@@ -6093,6 +8368,9 @@ void InitGraphicInfo_EM(void)
 
     for (j = 0; j < 8; j++)
     {
+#if 1
+      int effective_element = get_effective_element_EM(i, j);
+#else
       int effective_element = (j > 5 && i == Yacid_splash_eB ? EL_EMPTY :
                               j > 5 && i == Yacid_splash_wB ? EL_EMPTY :
                               j < 7 ? element :
@@ -6125,6 +8403,7 @@ void InitGraphicInfo_EM(void)
                               is_backside ? EL_EMPTY :
                               action_removing ? EL_EMPTY :
                               element);
+#endif
       int effective_action = (j < 7 ? action :
                              i == Xdrip_stretch ? action :
                              i == Xdrip_stretchB ? action :
@@ -6166,6 +8445,9 @@ void InitGraphicInfo_EM(void)
       boolean has_action_graphics = (graphic != base_graphic);
       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
       struct GraphicInfo *g = &graphic_info[graphic];
+#if 0
+      struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+#endif
       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
       Bitmap *src_bitmap;
       int src_x, src_y;
@@ -6302,13 +8584,20 @@ void InitGraphicInfo_EM(void)
       g_em->width  = TILEX;
       g_em->height = TILEY;
 
+      g_em->preserve_background = FALSE;
+
+#if 1
+      set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
+                              sync_frame);
+
+#else
+
       g_em->crumbled_bitmap = NULL;
       g_em->crumbled_src_x = 0;
       g_em->crumbled_src_y = 0;
       g_em->crumbled_border_size = 0;
 
       g_em->has_crumbled_graphics = FALSE;
-      g_em->preserve_background = FALSE;
 
 #if 0
       if (has_crumbled_graphics && crumbled == IMG_EMPTY_SPACE)
@@ -6318,18 +8607,47 @@ void InitGraphicInfo_EM(void)
 #endif
 
       /* if element can be crumbled, but certain action graphics are just empty
-        space (like snapping sand with the original R'n'D graphics), do not
+        space (like instantly snapping sand to empty space in 1 frame), do not
         treat these empty space graphics as crumbled graphics in EMC engine */
       if (has_crumbled_graphics && crumbled != IMG_EMPTY_SPACE)
       {
-       getGraphicSource(crumbled, frame, &src_bitmap, &src_x, &src_y);
+       int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
+                                              g_crumbled->anim_delay,
+                                              g_crumbled->anim_mode,
+                                              g_crumbled->anim_start_frame,
+                                              sync_frame);
+
+       getGraphicSource(crumbled, frame_crumbled, &src_bitmap, &src_x, &src_y);
 
        g_em->has_crumbled_graphics = TRUE;
        g_em->crumbled_bitmap = src_bitmap;
        g_em->crumbled_src_x = src_x;
        g_em->crumbled_src_y = src_y;
        g_em->crumbled_border_size = graphic_info[crumbled].border_size;
+
+
+#if 0
+       if (g_em == &graphic_info_em_object[207][0])
+         printf("... %d, %d [%d, %d, %d, %d] [%d, %d, %d, %d, %d, %d => %d]\n",
+                graphic_info_em_object[207][0].crumbled_src_x,
+                graphic_info_em_object[207][0].crumbled_src_y,
+
+                crumbled, frame, src_x, src_y,
+
+                g->anim_frames,
+                g->anim_delay,
+                g->anim_mode,
+                g->anim_start_frame,
+                sync_frame,
+                gfx.anim_random_frame,
+                frame);
+#endif
+
+#if 0
+       printf("::: EMC tile %d is crumbled\n", i);
+#endif
       }
+#endif
 
 #if 0
       if (element == EL_ROCK &&
@@ -6627,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)
@@ -6717,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 */
@@ -6766,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
+}