fixed and improved single step mode for all game engines
[rocksndiamonds.git] / src / game.c
index 11a6fed866271b03c2f5ff544ec29cd68827848c..8978e70750489ebe4c15af503f0d3ae9c461537c 100644 (file)
@@ -1,15 +1,13 @@
-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
-*----------------------------------------------------------*
-* (c) 1995-2006 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* game.c                                                   *
-***********************************************************/
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// game.c
+// ============================================================================
 
 #include "libgame/libgame.h"
 
 #include "init.h"
 #include "tools.h"
 #include "screens.h"
+#include "events.h"
 #include "files.h"
 #include "tape.h"
 #include "network.h"
+#include "anim.h"
 
 
 /* DEBUG SETTINGS */
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 /* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF                  (                         1)
-
-#define USE_NEW_SP_SLIPPERY            (USE_NEW_STUFF          * 1)
-#define USE_NEW_CUSTOM_VALUE           (USE_NEW_STUFF          * 1)
-#define USE_NEW_PLAYER_ANIM            (USE_NEW_STUFF          * 1)
-#define USE_NEW_ALL_SLIPPERY           (USE_NEW_STUFF          * 1)
-#define USE_NEW_PLAYER_SPEED           (USE_NEW_STUFF          * 1)
-#define USE_NEW_DELAYED_ACTION         (USE_NEW_STUFF          * 1)
-#define USE_NEW_SNAP_DELAY             (USE_NEW_STUFF          * 1)
-#define USE_ONLY_ONE_CHANGE_PER_FRAME  (USE_NEW_STUFF          * 1)
-#define USE_ONE_MORE_CHANGE_PER_FRAME  (USE_NEW_STUFF          * 1)
-#define USE_FIXED_DONT_RUN_INTO                (USE_NEW_STUFF          * 1)
-#define USE_NEW_SPRING_BUMPER          (USE_NEW_STUFF          * 1)
-#define USE_STOP_CHANGED_ELEMENTS      (USE_NEW_STUFF          * 1)
-#define USE_ELEMENT_TOUCHING_BUGFIX    (USE_NEW_STUFF          * 1)
-#define USE_NEW_CONTINUOUS_SNAPPING    (USE_NEW_STUFF          * 1)
-#define USE_GFX_RESET_GFX_ANIMATION    (USE_NEW_STUFF          * 1)
-#define USE_BOTH_SWITCHGATE_SWITCHES   (USE_NEW_STUFF          * 1)
-#define USE_PLAYER_GRAVITY             (USE_NEW_STUFF          * 1)
-#define USE_FIXED_BORDER_RUNNING_GFX   (USE_NEW_STUFF          * 1)
-#define USE_QUICKSAND_BD_ROCK_BUGFIX   (USE_NEW_STUFF          * 0)
-
-#define USE_QUICKSAND_IMPACT_BUGFIX    (USE_NEW_STUFF          * 0)
-
-#define USE_CODE_THAT_BREAKS_SNAKE_BITE        (USE_NEW_STUFF          * 1)
-
-#define USE_UFAST_PLAYER_EXIT_BUGFIX   (USE_NEW_STUFF          * 1)
-
-#define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF          * 1)
-#define USE_GFX_RESET_PLAYER_ARTWORK   (USE_NEW_STUFF          * 1)
-
-#define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF          * 1)
-#define USE_FIX_IMPACT_COLLISION       (USE_NEW_STUFF          * 1)
-#define USE_FIX_CE_ACTION_WITH_PLAYER  (USE_NEW_STUFF          * 1)
-#define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF          * 1)
-
-#define USE_PLAYER_REANIMATION         (USE_NEW_STUFF          * 1)
-
-#define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
-
-#define USE_NEW_PLAYER_ASSIGNMENTS     (USE_NEW_STUFF          * 1)
-
-#define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 0)
+#define USE_QUICKSAND_BD_ROCK_BUGFIX   0
+#define USE_QUICKSAND_IMPACT_BUGFIX    0
+#define USE_DELAYED_GFX_REDRAW         0
+#define USE_NEW_PLAYER_ASSIGNMENTS     1
 
 #if USE_DELAYED_GFX_REDRAW
 #define TEST_DrawLevelField(x, y)                              \
@@ -866,6 +827,14 @@ static struct GamePanelControlInfo game_panel_controls[] =
 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
 #define HALVE_PLAYER_SPEED(p)  (DOUBLE_MOVE_DELAY((p)->move_delay_value))
 
+/* values for scroll positions */
+#define SCROLL_POSITION_X(x)   ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
+                                (x) > SBX_Right + MIDPOSX ? SBX_Right :\
+                                (x) - MIDPOSX)
+#define SCROLL_POSITION_Y(y)   ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
+                                (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
+                                (y) - MIDPOSY)
+
 /* values for other actions */
 #define MOVE_STEPSIZE_NORMAL   (TILEX / MOVE_DELAY_NORMAL_SPEED)
 #define MOVE_STEPSIZE_MIN      (1)
@@ -1011,13 +980,16 @@ static struct GamePanelControlInfo game_panel_controls[] =
 #define GAME_CTRL_ID_STOP              0
 #define GAME_CTRL_ID_PAUSE             1
 #define GAME_CTRL_ID_PLAY              2
-#define SOUND_CTRL_ID_MUSIC            3
-#define SOUND_CTRL_ID_LOOPS            4
-#define SOUND_CTRL_ID_SIMPLE           5
-#define GAME_CTRL_ID_SAVE              6
+#define GAME_CTRL_ID_UNDO              3
+#define GAME_CTRL_ID_REDO              4
+#define GAME_CTRL_ID_SAVE              5
+#define GAME_CTRL_ID_PAUSE2            6
 #define GAME_CTRL_ID_LOAD              7
+#define SOUND_CTRL_ID_MUSIC            8
+#define SOUND_CTRL_ID_LOOPS            9
+#define SOUND_CTRL_ID_SIMPLE           10
 
-#define NUM_GAME_BUTTONS               8
+#define NUM_GAME_BUTTONS               11
 
 
 /* forward declaration for internal use */
@@ -1049,9 +1021,6 @@ static void KillPlayerUnlessExplosionProtected(int, int);
 static void TestIfPlayerTouchesCustomElement(int, int);
 static void TestIfElementTouchesCustomElement(int, int);
 static void TestIfElementHitsCustomElement(int, int, int);
-#if 0
-static void TestIfElementSmashesCustomElement(int, int, int);
-#endif
 
 static void HandleElementChange(int, int, int);
 static void ExecuteCustomElementAction(int, int, int, int);
@@ -1237,11 +1206,7 @@ static struct ChangingElementInfo change_delay_list[] =
   },
   {
     EL_EM_EXIT_CLOSING,
-#if 1
     EL_EMPTY,
-#else
-    EL_EM_EXIT_CLOSED,
-#endif
     29,
     NULL,
     NULL,
@@ -1257,11 +1222,7 @@ static struct ChangingElementInfo change_delay_list[] =
   },
   {
     EL_EM_STEEL_EXIT_CLOSING,
-#if 1
     EL_STEELWALL,
-#else
-    EL_EM_STEEL_EXIT_CLOSED,
-#endif
     29,
     NULL,
     NULL,
@@ -1658,7 +1619,6 @@ void GetPlayerConfig()
   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
 
   SetAudioMode(setup.sound);
-  InitJoysticks();
 }
 
 int GetElementFromGroupElement(int element)
@@ -1763,7 +1723,6 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
     player->jy = player->last_jy = y;
   }
 
-#if USE_PLAYER_REANIMATION
   if (!init_game)
   {
     int player_nr = GET_PLAYER_NR(element);
@@ -1772,7 +1731,6 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
     if (player->active && player->killed)
       player->reanimated = TRUE; /* if player was just killed, reanimate him */
   }
-#endif
 }
 
 static void InitField(int x, int y, boolean init_game)
@@ -1925,18 +1883,6 @@ static void InitField(int x, int y, boolean init_game)
       }
       break;
 
-#if !USE_BOTH_SWITCHGATE_SWITCHES
-    case EL_SWITCHGATE_SWITCH_DOWN:    /* always start with same switch pos */
-      if (init_game)
-       Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
-      break;
-
-    case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
-      if (init_game)
-       Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
-      break;
-#endif
-
     case EL_LIGHT_SWITCH_ACTIVE:
       if (init_game)
        game.light_time_left = level.time_light * FRAMES_PER_SECOND;
@@ -1994,10 +1940,8 @@ static void InitField(int x, int y, boolean init_game)
        if (CAN_MOVE(element))
          InitMovDir(x, y);
 
-#if USE_NEW_CUSTOM_VALUE
        if (!element_info[element].use_last_ce_value || init_game)
          CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
-#endif
       }
       else if (IS_GROUP_ELEMENT(element))
       {
@@ -2013,7 +1957,7 @@ static void InitField(int x, int y, boolean init_game)
     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
 }
 
-static inline void InitField_WithBug1(int x, int y, boolean init_game)
+inline static void InitField_WithBug1(int x, int y, boolean init_game)
 {
   InitField(x, y, init_game);
 
@@ -2023,7 +1967,7 @@ static inline void InitField_WithBug1(int x, int y, boolean init_game)
     InitMovDir(x, y);
 }
 
-static inline void InitField_WithBug2(int x, int y, boolean init_game)
+inline static void InitField_WithBug2(int x, int y, boolean init_game)
 {
   int old_element = Feld[x][y];
 
@@ -2113,6 +2057,16 @@ static int compareGamePanelOrderInfo(const void *object1, const void *object2)
   return compare_result;
 }
 
+int getPlayerInventorySize(int player_nr)
+{
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    return level.native_em_level->ply[player_nr]->dynamite;
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    return level.native_sp_level->game_sp->red_disk_count;
+  else
+    return stored_player[player_nr].inventory_size;
+}
+
 void InitGameControlValues()
 {
   int i;
@@ -2250,15 +2204,8 @@ void UpdateGameControlValues()
            get_key_element_from_nr(k);
       }
 
-      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-         level.native_em_level->ply[i]->dynamite;
-      else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
-       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-         level.native_sp_level->game_sp->red_disk_count;
-      else
-       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-         stored_player[i].inventory_size;
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       getPlayerInventorySize(i);
 
       if (stored_player[i].num_white_keys > 0)
        game_panel_controls[GAME_PANEL_KEY_WHITE].value =
@@ -2285,15 +2232,8 @@ void UpdateGameControlValues()
          get_key_element_from_nr(k);
     }
 
-    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-       level.native_em_level->ply[player_nr]->dynamite;
-    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
-      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-       level.native_sp_level->game_sp->red_disk_count;
-    else
-      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-       stored_player[player_nr].inventory_size;
+    game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+      getPlayerInventorySize(player_nr);
 
     if (stored_player[player_nr].num_white_keys > 0)
       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
@@ -2403,12 +2343,8 @@ void UpdateGameControlValues()
   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
     game.magic_wall_time_left;
 
-#if USE_PLAYER_GRAVITY
   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
     local_player->gravity;
-#else
-  game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
-#endif
 
   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
@@ -2505,47 +2441,25 @@ void DisplayGameControlValues()
     return;
 
   /* copy default game door content to main double buffer */
-#if 1
+
   /* !!! CHECK AGAIN !!! */
   SetPanelBackground();
   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
   DrawBackground(DX, DY, DXSIZE, DYSIZE);
-#else
-  BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-            DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
-#endif
 
   /* redraw game control buttons */
-#if 1
   RedrawGameButtons();
-#else
-  UnmapGameButtons();
-  MapGameButtons();
-#endif
 
-  game_status = GAME_MODE_PSEUDO_PANEL;
+  SetGameStatus(GAME_MODE_PSEUDO_PANEL);
 
-#if 1
   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
-#else
-  for (i = 0; game_panel_controls[i].nr != -1; i++)
-#endif
   {
-#if 1
     int nr = game_panel_order[i].nr;
     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
-#else
-    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
-    int nr = gpc->nr;
-#endif
     struct TextPosInfo *pos = gpc->pos;
     int type = gpc->type;
     int value = gpc->value;
     int frame = gpc->frame;
-#if 0
-    int last_value = gpc->last_value;
-    int last_frame = gpc->last_frame;
-#endif
     int size = pos->size;
     int font = pos->font;
     boolean draw_masked = pos->draw_masked;
@@ -2554,18 +2468,9 @@ void DisplayGameControlValues()
     if (PANEL_DEACTIVATED(pos))
       continue;
 
-#if 0
-    if (value == last_value && frame == last_frame)
-      continue;
-#endif
-
     gpc->last_value = value;
     gpc->last_frame = frame;
 
-#if 0
-    printf("::: value %d changed from %d to %d\n", nr, last_value, value);
-#endif
-
     if (type == TYPE_INTEGER)
     {
       if (nr == GAME_PANEL_LEVEL_NUMBER ||
@@ -2583,33 +2488,15 @@ void DisplayGameControlValues()
 
          size = (value < value_change ? size1 : size2);
          font = (value < value_change ? font1 : font2);
-
-#if 0
-         /* clear background if value just changed its size (dynamic digits) */
-         if ((last_value < value_change) != (value < value_change))
-         {
-           int width1 = size1 * getFontWidth(font1);
-           int width2 = size2 * getFontWidth(font2);
-           int max_width = MAX(width1, width2);
-           int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
-
-           pos->width = max_width;
-
-           ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
-                                      max_width, max_height);
-         }
-#endif
        }
       }
 
-#if 1
       /* correct text size if "digits" is zero or less */
       if (size <= 0)
        size = strlen(int2str(value, size));
 
       /* dynamically correct text alignment */
       pos->width = size * getFontWidth(font);
-#endif
 
       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
                  int2str(value, size), font, mask_mode);
@@ -2623,7 +2510,6 @@ void DisplayGameControlValues()
       int dst_x = PANEL_XPOS(pos);
       int dst_y = PANEL_YPOS(pos);
 
-#if 1
       if (value != EL_UNDEFINED && value != EL_EMPTY)
       {
        element = value;
@@ -2631,10 +2517,8 @@ void DisplayGameControlValues()
 
        // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
 
-#if 1
        if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
          size = TILESIZE;
-#endif
 
        getSizedGraphicSource(graphic, frame, size, &src_bitmap,
                              &src_x, &src_y);
@@ -2643,41 +2527,12 @@ void DisplayGameControlValues()
        height = graphic_info[graphic].height * size / TILESIZE;
 
        if (draw_masked)
-       {
-         SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
-                       dst_x - src_x, dst_y - src_y);
          BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
                           dst_x, dst_y);
-       }
        else
-       {
          BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
                     dst_x, dst_y);
-       }
-      }
-#else
-      if (value == EL_UNDEFINED || value == EL_EMPTY)
-      {
-       element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
-       graphic = el2panelimg(element);
-
-       src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
-       src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
-       src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
-      }
-      else
-      {
-       element = value;
-       graphic = el2panelimg(value);
-
-       getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
       }
-
-      width  = graphic_info[graphic].width  * size / TILESIZE;
-      height = graphic_info[graphic].height * size / TILESIZE;
-
-      BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
-#endif
     }
     else if (type == TYPE_STRING)
     {
@@ -2694,20 +2549,6 @@ void DisplayGameControlValues()
       {
        int font1 = pos->font;          /* (used for normal state) */
        int font2 = pos->font_alt;      /* (used for active state) */
-#if 0
-       int size1 = strlen(state_normal);
-       int size2 = strlen(state_active);
-       int width1 = size1 * getFontWidth(font1);
-       int width2 = size2 * getFontWidth(font2);
-       int max_width = MAX(width1, width2);
-       int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
-
-       pos->width = max_width;
-
-       /* clear background for values that may have changed its size */
-       ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
-                                  max_width, max_height);
-#endif
 
        font = (active ? font2 : font1);
       }
@@ -2716,7 +2557,6 @@ void DisplayGameControlValues()
       {
        char *s_cut;
 
-#if 1
        if (size <= 0)
        {
          /* don't truncate output if "chars" is zero or less */
@@ -2725,7 +2565,6 @@ void DisplayGameControlValues()
          /* dynamically correct text alignment */
          pos->width = size * getFontWidth(font);
        }
-#endif
 
        s_cut = getStringCopyN(s, size);
 
@@ -2739,228 +2578,18 @@ void DisplayGameControlValues()
     redraw_mask |= REDRAW_DOOR_1;
   }
 
-  game_status = GAME_MODE_PLAYING;
+  SetGameStatus(GAME_MODE_PLAYING);
 }
 
 void UpdateAndDisplayGameControlValues()
 {
-  if (tape.warp_forward)
+  if (tape.deactivate_display)
     return;
 
   UpdateGameControlValues();
   DisplayGameControlValues();
 }
 
-void DrawGameValue_Emeralds(int value)
-{
-  struct TextPosInfo *pos = &game.panel.gems;
-  int font_nr = pos->font;
-  int font_width = getFontWidth(font_nr);
-  int chars = pos->size;
-
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
-
-  if (PANEL_DEACTIVATED(pos))
-    return;
-
-  pos->width = chars * font_width;
-
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
-}
-
-void DrawGameValue_Dynamite(int value)
-{
-  struct TextPosInfo *pos = &game.panel.inventory_count;
-  int font_nr = pos->font;
-  int font_width = getFontWidth(font_nr);
-  int chars = pos->size;
-
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
-
-  if (PANEL_DEACTIVATED(pos))
-    return;
-
-  pos->width = chars * font_width;
-
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
-}
-
-void DrawGameValue_Score(int value)
-{
-  struct TextPosInfo *pos = &game.panel.score;
-  int font_nr = pos->font;
-  int font_width = getFontWidth(font_nr);
-  int chars = pos->size;
-
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
-
-  if (PANEL_DEACTIVATED(pos))
-    return;
-
-  pos->width = chars * font_width;
-
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
-}
-
-void DrawGameValue_Time(int value)
-{
-  struct TextPosInfo *pos = &game.panel.time;
-  static int last_value = -1;
-  int chars1 = 3;
-  int chars2 = 4;
-  int chars = pos->size;
-  int font1_nr = pos->font;
-  int font2_nr = pos->font_alt;
-  int font_nr = font1_nr;
-  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
-
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
-
-  if (PANEL_DEACTIVATED(pos))
-    return;
-
-  if (use_dynamic_chars)               /* use dynamic number of chars */
-  {
-    chars   = (value < 1000 ? chars1   : chars2);
-    font_nr = (value < 1000 ? font1_nr : font2_nr);
-  }
-
-  /* clear background if value just changed its size (dynamic chars only) */
-  if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
-  {
-    int width1 = chars1 * getFontWidth(font1_nr);
-    int width2 = chars2 * getFontWidth(font2_nr);
-    int max_width = MAX(width1, width2);
-    int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
-
-    pos->width = max_width;
-
-    ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
-                              max_width, max_height);
-  }
-
-  pos->width = chars * getFontWidth(font_nr);
-
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
-
-  last_value = value;
-}
-
-void DrawGameValue_Level(int value)
-{
-  struct TextPosInfo *pos = &game.panel.level_number;
-  int chars1 = 2;
-  int chars2 = 3;
-  int chars = pos->size;
-  int font1_nr = pos->font;
-  int font2_nr = pos->font_alt;
-  int font_nr = font1_nr;
-  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
-
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
-
-  if (PANEL_DEACTIVATED(pos))
-    return;
-
-  if (use_dynamic_chars)               /* use dynamic number of chars */
-  {
-    chars   = (level_nr < 100 ? chars1   : chars2);
-    font_nr = (level_nr < 100 ? font1_nr : font2_nr);
-  }
-
-  pos->width = chars * getFontWidth(font_nr);
-
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
-}
-
-void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
-{
-  int i;
-
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
-
-  for (i = 0; i < MAX_NUM_KEYS; i++)
-  {
-    struct TextPosInfo *pos = &game.panel.key[i];
-    int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
-    int src_y = DOOR_GFX_PAGEY1 + 123;
-    int dst_x = PANEL_XPOS(pos);
-    int dst_y = PANEL_YPOS(pos);
-
-    int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
-                  level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
-                  EL_KEY_1) + i;
-    int graphic = el2edimg(element);
-
-    if (PANEL_DEACTIVATED(pos))
-      continue;
-
-#if 0
-    /* masked blit with tiles from half-size scaled bitmap does not work yet
-       (no mask bitmap created for these sizes after loading and scaling) --
-       solution: load without creating mask, scale, then create final mask */
-
-    BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
-              MINI_TILEX, MINI_TILEY, dst_x, dst_y);
-
-    if (key[i])
-    {
-      Bitmap *src_bitmap;
-      int src_x, src_y;
-
-      getMiniGraphicSource(graphic, &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, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
-                      dst_x, dst_y);
-    }
-#else
-    if (key[i])
-      DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
-    else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
-                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
-#endif
-  }
-}
-
-void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
-                      int key_bits)
-{
-  int key[MAX_NUM_KEYS];
-  int i;
-
-  /* prevent EM engine from updating time/score values parallel to GameWon() */
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
-      local_player->LevelSolved)
-    return;
-
-  for (i = 0; i < MAX_NUM_KEYS; i++)
-    key[i] = key_bits & (1 << i);
-
-  DrawGameValue_Level(level_nr);
-
-  DrawGameValue_Emeralds(emeralds);
-  DrawGameValue_Dynamite(dynamite);
-  DrawGameValue_Score(score);
-  DrawGameValue_Time(time);
-
-  DrawGameValue_Keys(key);
-}
-
 void UpdateGameDoorValues()
 {
   UpdateGameControlValues();
@@ -2971,49 +2600,6 @@ void DrawGameDoorValues()
   DisplayGameControlValues();
 }
 
-void DrawGameDoorValues_OLD()
-{
-  int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
-  int dynamite_value = 0;
-  int score_value = (local_player->LevelSolved ? local_player->score_final :
-                    local_player->score);
-  int gems_value = local_player->gems_still_needed;
-  int key_bits = 0;
-  int i, j;
-
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-  {
-    DrawGameDoorValues_EM();
-
-    return;
-  }
-
-  if (game.centered_player_nr == -1)
-  {
-    for (i = 0; i < MAX_PLAYERS; i++)
-    {
-      for (j = 0; j < MAX_NUM_KEYS; j++)
-       if (stored_player[i].key[j])
-         key_bits |= (1 << j);
-
-      dynamite_value += stored_player[i].inventory_size;
-    }
-  }
-  else
-  {
-    int player_nr = game.centered_player_nr;
-
-    for (i = 0; i < MAX_NUM_KEYS; i++)
-      if (stored_player[player_nr].key[i])
-       key_bits |= (1 << i);
-
-    dynamite_value = stored_player[player_nr].inventory_size;
-  }
-
-  DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
-                   key_bits);
-}
-
 
 /*
   =============================================================================
@@ -3112,39 +2698,13 @@ static void InitGameEngine()
   game.use_block_last_field_bug =
     (game.engine_version < VERSION_IDENT(3,1,1,0));
 
-  /*
-    Summary of bugfix/change:
-    Changed behaviour of CE changes with multiple changes per single frame.
-
-    Fixed/changed in version:
-    3.2.0-6
-
-    Description:
-    Before 3.2.0-6, only one single CE change was allowed in each engine frame.
-    This resulted in race conditions where CEs seem to behave strange in some
-    situations (where triggered CE changes were just skipped because there was
-    already a CE change on that tile in the playfield in that engine frame).
-    Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
-    (The number of changes per frame must be limited in any case, because else
-    it is easily possible to define CE changes that would result in an infinite
-    loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
-    should be set large enough so that it would only be reached in cases where
-    the corresponding CE change conditions run into a loop. Therefore, it seems
-    to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
-    maximal number of change pages for custom elements.)
+  game_em.use_single_button =
+    (game.engine_version > VERSION_IDENT(4,0,0,2));
 
-    Affected levels/tapes:
-    Probably many.
-  */
+  /* ---------------------------------------------------------------------- */
 
-#if USE_ONLY_ONE_CHANGE_PER_FRAME
+  /* set maximal allowed number of custom element changes per game frame */
   game.max_num_changes_per_frame = 1;
-#else
-  game.max_num_changes_per_frame =
-    (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
-#endif
-
-  /* ---------------------------------------------------------------------- */
 
   /* default scan direction: scan playfield from top/left to bottom/right */
   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
@@ -3266,7 +2826,6 @@ static void InitGameEngine()
     }
   }
 
-#if 1
   /* ---------- initialize reference elements in change conditions --------- */
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -3285,7 +2844,6 @@ static void InitGameEngine()
       ei->change_page[j].trigger_element = trigger_element;
     }
   }
-#endif
 
   /* ---------- initialize run-time trigger player and element ------------- */
 
@@ -3484,6 +3042,20 @@ static void InitGameEngine()
      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
   game.scroll_delay_value =
     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
+
+  /* ---------- initialize game engine snapshots ---------------------------- */
+  for (i = 0; i < MAX_PLAYERS; i++)
+    game.snapshot.last_action[i] = 0;
+  game.snapshot.changed_action = FALSE;
+  game.snapshot.collected_item = FALSE;
+  game.snapshot.mode =
+    (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
+     SNAPSHOT_MODE_EVERY_STEP :
+     strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
+     SNAPSHOT_MODE_EVERY_MOVE :
+     strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
+     SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
+  game.snapshot.save_snapshot = FALSE;
 }
 
 int get_num_special_action(int element, int action_first, int action_last)
@@ -3520,29 +3092,46 @@ int get_num_special_action(int element, int action_first, int action_last)
 
 void InitGame()
 {
+  int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
+  int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
+  int fade_mask = REDRAW_FIELD;
+
   boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
-#if 0
-  boolean do_fading = (game_status == GAME_MODE_MAIN);
-#endif
-#if 1
   int initial_move_dir = MV_DOWN;
-#else
-  int initial_move_dir = MV_NONE;
-#endif
   int i, j, x, y;
 
-  game_status = GAME_MODE_PLAYING;
-
-#if 1
-  /* needed if different viewport properties defined for playing */
-  ChangeViewportPropertiesIfNeeded();
-#endif
+  // required here to update video display before fading (FIX THIS)
+  DrawMaskedBorder(REDRAW_DOOR_2);
+
+  if (!game.restart_level)
+    CloseDoor(DOOR_CLOSE_1);
+
+  SetGameStatus(GAME_MODE_PLAYING);
+
+  if (level_editor_test_game)
+    FadeSkipNextFadeIn();
+  else
+    FadeSetEnterScreen();
+
+  if (CheckIfGlobalBorderHasChanged())
+    fade_mask = REDRAW_ALL;
+
+  FadeSoundsAndMusic();
+
+  ExpireSoundLoops(TRUE);
+
+  FadeOut(fade_mask);
+
+  /* needed if different viewport properties defined for playing */
+  ChangeViewportPropertiesIfNeeded();
+
+  ClearField();
+
+  OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
 
-#if 1
   DrawCompleteVideoDisplay();
-#endif
 
   InitGameEngine();
   InitGameControlValues();
@@ -3630,6 +3219,13 @@ void InitGame()
     player->is_bored = FALSE;
     player->is_sleeping = FALSE;
 
+    player->was_waiting = TRUE;
+    player->was_moving = FALSE;
+    player->was_snapping = FALSE;
+    player->was_dropping = FALSE;
+
+    player->force_dropping = FALSE;
+
     player->frame_counter_bored = -1;
     player->frame_counter_sleeping = -1;
 
@@ -3742,11 +3338,6 @@ void InitGame()
   game.switchgate_pos = 0;
   game.wind_direction = level.wind_direction_initial;
 
-#if !USE_PLAYER_GRAVITY
-  game.gravity = FALSE;
-  game.explosions_delayed = TRUE;
-#endif
-
   game.lenses_time_left = 0;
   game.magnify_time_left = 0;
 
@@ -3789,9 +3380,7 @@ void InitGame()
     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
     ChangeDelay[x][y] = 0;
     ChangePage[x][y] = -1;
-#if USE_NEW_CUSTOM_VALUE
     CustomValue[x][y] = 0;             /* initialized in InitField() */
-#endif
     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
     AmoebaNr[x][y] = 0;
     WasJustMoving[x][y] = 0;
@@ -3852,7 +3441,6 @@ void InitGame()
                    emulate_sb ? EMU_SOKOBAN :
                    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
 
-#if USE_NEW_ALL_SLIPPERY
   /* initialize type of slippery elements */
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
@@ -3870,7 +3458,6 @@ void InitGame()
        element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
     }
   }
-#endif
 
   /* initialize explosion and ignition delay */
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
@@ -3890,14 +3477,6 @@ void InitGame()
       if (i == EL_BLACK_ORB)
        element_info[i].ignition_delay = 1;
     }
-
-#if 0
-    if (element_info[i].explosion_delay < 1)   /* !!! check again !!! */
-      element_info[i].explosion_delay = 1;
-
-    if (element_info[i].ignition_delay < 1)    /* !!! check again !!! */
-      element_info[i].ignition_delay = 1;
-#endif
   }
 
   /* correct non-moving belts to start moving left */
@@ -3916,23 +3495,10 @@ void InitGame()
   local_player->connected = TRUE;
   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
 
-#if 0
-  printf("::: TEAM MODE: %d\n", game.team_mode);
-#endif
-
   if (tape.playing)
   {
-#if 1
     for (i = 0; i < MAX_PLAYERS; i++)
       stored_player[i].connected = tape.player_participates[i];
-#else
-    /* try to guess locally connected team mode players (needed for correct
-       assignment of player figures from level to locally playing players) */
-
-    for (i = 0; i < MAX_PLAYERS; i++)
-      if (tape.player_participates[i])
-       stored_player[i].connected = TRUE;
-#endif
   }
   else if (game.team_mode && !options.network)
   {
@@ -3989,7 +3555,6 @@ void InitGame()
 
       /* assign first free player found that is present in the playfield */
 
-#if 1
       /* first try: look for unmapped playfield player that is not connected */
       for (j = 0; j < MAX_PLAYERS; j++)
        if (field_player == NULL &&
@@ -4004,22 +3569,6 @@ void InitGame()
            stored_player[j].present &&
            !stored_player[j].mapped)
          field_player = &stored_player[j];
-#else
-      /* first try: look for unmapped playfield player that is not connected */
-      if (field_player == NULL)
-       for (j = 0; j < MAX_PLAYERS; j++)
-         if (stored_player[j].present &&
-             !stored_player[j].mapped &&
-             !stored_player[j].connected)
-           field_player = &stored_player[j];
-
-      /* second try: look for *any* unmapped playfield player */
-      if (field_player == NULL)
-       for (j = 0; j < MAX_PLAYERS; j++)
-         if (stored_player[j].present &&
-             !stored_player[j].mapped)
-           field_player = &stored_player[j];
-#endif
 
       if (field_player != NULL)
       {
@@ -4142,26 +3691,25 @@ void InitGame()
 
 #if USE_NEW_PLAYER_ASSIGNMENTS
 
-#if 1
     if (!game.team_mode)
-#endif
-
-    for (i = 0; i < MAX_PLAYERS; i++)
     {
-      if (stored_player[i].active &&
-         !tape.player_participates[map_player_action[i]])
+      for (i = 0; i < MAX_PLAYERS; i++)
       {
-       struct PlayerInfo *player = &stored_player[i];
-       int jx = player->jx, jy = player->jy;
+       if (stored_player[i].active &&
+           !tape.player_participates[map_player_action[i]])
+       {
+         struct PlayerInfo *player = &stored_player[i];
+         int jx = player->jx, jy = player->jy;
 
 #if DEBUG_INIT_PLAYER
-       if (options.debug)
-         printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
+         if (options.debug)
+           printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
 #endif
 
-       player->active = FALSE;
-       StorePlayer[jx][jy] = 0;
-       Feld[jx][jy] = EL_EMPTY;
+         player->active = FALSE;
+         StorePlayer[jx][jy] = 0;
+         Feld[jx][jy] = EL_EMPTY;
+       }
       }
     }
 
@@ -4260,28 +3808,16 @@ void InitGame()
     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
   }
 
-#if NEW_TILESIZE
-
-  if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
+  if (full_lev_fieldx <= SCR_FIELDX)
     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
-
-  if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
+  if (full_lev_fieldy <= SCR_FIELDY)
     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
 
-  if (EVEN(SCR_FIELDX))
+  if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
     SBX_Left--;
-  if (EVEN(SCR_FIELDY))
+  if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
     SBY_Upper--;
 
-#else
-
-  if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
-    SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
-
-  if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
-    SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
-#endif
-
   /* if local player not found, look for custom element that might create
      the player (make some assumptions about the right custom element) */
   if (!local_player->present)
@@ -4372,157 +3908,82 @@ void InitGame()
       }
     }
 
-    scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
-               start_x > SBX_Right + MIDPOSX ? SBX_Right :
-               start_x - MIDPOSX);
-
-    scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-               start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-               start_y - MIDPOSY);
+    scroll_x = SCROLL_POSITION_X(start_x);
+    scroll_y = SCROLL_POSITION_Y(start_y);
   }
   else
   {
-    scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-               local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-               local_player->jx - MIDPOSX);
-
-    scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-               local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-               local_player->jy - MIDPOSY);
+    scroll_x = SCROLL_POSITION_X(local_player->jx);
+    scroll_y = SCROLL_POSITION_Y(local_player->jy);
   }
 
-#if 0
-  printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
-#endif
-
-#if 0
-  /* do not use PLAYING mask for fading out from main screen */
-  game_status = GAME_MODE_MAIN;
-#endif
-
-  StopAnimation();
-
-  if (!game.restart_level)
-    CloseDoor(DOOR_CLOSE_1);
-
-#if 1
-  if (level_editor_test_game)
-    FadeSkipNextFadeIn();
-  else
-    FadeSetEnterScreen();
-#else
-  if (level_editor_test_game)
-    fading = fading_none;
-  else
-    fading = menu.destination;
-#endif
-
-#if 1
-  FadeOut(REDRAW_FIELD);
-#else
-  if (do_fading)
-    FadeOut(REDRAW_FIELD);
-#endif
-
-#if 0
-  game_status = GAME_MODE_PLAYING;
-#endif
-
   /* !!! FIX THIS (START) !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     InitGameEngine_EM();
-
-    /* blit playfield from scroll buffer to normal back buffer for fading in */
-    BlitScreenToBitmap_EM(backbuffer);
   }
   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
   {
     InitGameEngine_SP();
-
-    /* blit playfield from scroll buffer to normal back buffer for fading in */
-    BlitScreenToBitmap_SP(backbuffer);
   }
   else
   {
-    DrawLevel();
+    DrawLevel(REDRAW_FIELD);
     DrawAllPlayers();
 
     /* after drawing the level, correct some elements */
     if (game.timegate_time_left == 0)
       CloseAllOpenTimegates();
-
-#if NEW_TILESIZE
-    BlitScreenToBitmap(backbuffer);
-#else
-    /* blit playfield from scroll buffer to normal back buffer for fading in */
-    if (setup.soft_scrolling)
-      BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
-#endif
-
-    redraw_mask |= REDRAW_FROM_BACKBUFFER;
   }
+
+  /* blit playfield from scroll buffer to normal back buffer for fading in */
+  BlitScreenToBitmap(backbuffer);
   /* !!! FIX THIS (END) !!! */
 
-#if 1
-  FadeIn(REDRAW_FIELD);
-#else
-  if (do_fading)
-    FadeIn(REDRAW_FIELD);
+  DrawMaskedBorder(fade_mask);
 
+  FadeIn(fade_mask);
+
+#if 1
+  // full screen redraw is required at this point in the following cases:
+  // - special editor door undrawn when game was started from level editor
+  // - drawing area (playfield) was changed and has to be removed completely
+  redraw_mask = REDRAW_ALL;
   BackToFront();
 #endif
 
   if (!game.restart_level)
   {
     /* copy default game door content to main double buffer */
-#if 1
-#if 1
+
     /* !!! CHECK AGAIN !!! */
     SetPanelBackground();
     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
     DrawBackground(DX, DY, DXSIZE, DYSIZE);
-#else
-    struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
-
-    /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
-    ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
-    BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
-              MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
-#endif
-#else
-    BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
-#endif
   }
 
   SetPanelBackground();
   SetDrawBackgroundMask(REDRAW_DOOR_1);
 
-#if 1
   UpdateAndDisplayGameControlValues();
-#else
-  UpdateGameDoorValues();
-  DrawGameDoorValues();
-#endif
 
   if (!game.restart_level)
   {
     UnmapGameButtons();
     UnmapTapeButtons();
+
+    FreeGameButtons();
+    CreateGameButtons();
+
     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
+
     MapGameButtons();
     MapTapeButtons();
 
     /* copy actual game door content to door double buffer for OpenDoor() */
-#if 1
     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
-#else
-    BlitBitmap(drawto, bitmap_db_door,
-              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
-#endif
 
     OpenDoor(DOOR_OPEN_ALL);
 
@@ -4557,34 +4018,41 @@ void InitGame()
 #endif
   }
 
-#if 1
   UnmapAllGadgets();
 
   MapGameButtons();
   MapTapeButtons();
-#endif
 
   if (!game.restart_level && !tape.playing)
   {
     LevelStats_incPlayed(level_nr);
 
     SaveLevelSetup_SeriesInfo();
-
-#if 0
-    printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
-#endif
   }
 
   game.restart_level = FALSE;
+
+  SaveEngineSnapshotToListInitial();
 }
 
-void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
+void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
+                       int actual_player_x, int actual_player_y)
 {
   /* this is used for non-R'n'D game engines to update certain engine values */
 
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+  {
+    actual_player_x = correctLevelPosX_EM(actual_player_x);
+    actual_player_y = correctLevelPosY_EM(actual_player_y);
+  }
+
   /* needed to determine if sounds are played within the visible screen area */
   scroll_x = actual_scroll_x;
   scroll_y = actual_scroll_y;
+
+  /* needed to get player position for "follow finger" playing input method */
+  local_player->jx = actual_player_x;
+  local_player->jy = actual_player_y;
 }
 
 void InitMovDir(int x, int y)
@@ -4824,18 +4292,12 @@ void GameWon()
       LevelStats_incSolved(level_nr);
 
       SaveLevelSetup_SeriesInfo();
-
-#if 0
-      printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
-#endif
     }
 
     if (tape.auto_play)                /* tape might already be stopped here */
       tape.auto_play_level_solved = TRUE;
 
-#if 1
     TapeStop();
-#endif
 
     game_over_delay_1 = game_over_delay_value_1;
     game_over_delay_2 = game_over_delay_value_2;
@@ -4861,7 +4323,6 @@ void GameWon()
       time = time_final;
       score = score_final;
 
-#if 1
       local_player->LevelSolved_CountingTime = time;
       local_player->LevelSolved_CountingScore = score;
 
@@ -4869,10 +4330,6 @@ void GameWon()
       game_panel_controls[GAME_PANEL_SCORE].value = score;
 
       DisplayGameControlValues();
-#else
-      DrawGameValue_Time(time);
-      DrawGameValue_Score(score);
-#endif
     }
 
     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
@@ -4889,24 +4346,14 @@ void GameWon()
        {
          int element = Feld[ExitX][ExitY];
 
-#if 0
-         if (element == EL_EM_EXIT_OPEN ||
-             element == EL_EM_STEEL_EXIT_OPEN)
-         {
-           Bang(ExitX, ExitY);
-         }
-         else
-#endif
-         {
-           Feld[ExitX][ExitY] =
-             (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
-              element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
-              element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
-              element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
-              EL_EM_STEEL_EXIT_CLOSING);
-
-           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
-         }
+         Feld[ExitX][ExitY] =
+           (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
+            element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
+            element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
+            element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
+            EL_EM_STEEL_EXIT_CLOSING);
+
+         PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
        }
 
        /* player disappears */
@@ -4946,7 +4393,6 @@ void GameWon()
     time  += time_count_steps * time_count_dir;
     score += time_count_steps * level.score[SC_TIME_BONUS];
 
-#if 1
     local_player->LevelSolved_CountingTime = time;
     local_player->LevelSolved_CountingScore = score;
 
@@ -4954,10 +4400,6 @@ void GameWon()
     game_panel_controls[GAME_PANEL_SCORE].value = score;
 
     DisplayGameControlValues();
-#else
-    DrawGameValue_Time(time);
-    DrawGameValue_Score(score);
-#endif
 
     if (time == time_final)
       StopSound(SND_GAME_LEVELTIME_BONUS);
@@ -4978,9 +4420,7 @@ void GameWon()
     return;
   }
 
-#if 1
   GameEnd();
-#endif
 }
 
 void GameEnd()
@@ -4990,43 +4430,30 @@ void GameEnd()
 
   local_player->LevelSolved_GameEnd = TRUE;
 
-  CloseDoor(DOOR_CLOSE_1);
+  if (!global.use_envelope_request)
+    CloseDoor(DOOR_CLOSE_1);
 
   if (local_player->LevelSolved_SaveTape)
   {
-#if 0
-    TapeStop();
-#endif
-
-#if 1
     SaveTapeChecked(tape.level_nr);    /* ask to save tape */
-#else
-    SaveTape(tape.level_nr);           /* ask to save tape */
-#endif
   }
 
+  CloseDoor(DOOR_CLOSE_ALL);
+
   if (level_editor_test_game)
   {
-    game_status = GAME_MODE_MAIN;
+    SetGameStatus(GAME_MODE_MAIN);
 
-#if 1
-    DrawAndFadeInMainMenu(REDRAW_FIELD);
-#else
     DrawMainMenu();
-#endif
 
     return;
   }
 
   if (!local_player->LevelSolved_SaveScore)
   {
-#if 1
-    FadeOut(REDRAW_FIELD);
-#endif
-
-    game_status = GAME_MODE_MAIN;
+    SetGameStatus(GAME_MODE_MAIN);
 
-    DrawAndFadeInMainMenu(REDRAW_FIELD);
+    DrawMainMenu();
 
     return;
   }
@@ -5038,12 +4465,13 @@ void GameEnd()
     SaveLevelSetup_SeriesInfo();
   }
 
-  if (level_nr < leveldir_current->last_level)
+  if (setup.increment_levels &&
+      level_nr < leveldir_current->last_level)
     raise_level = TRUE;                        /* advance to next level */
 
   if ((hi_pos = NewHiScore()) >= 0) 
   {
-    game_status = GAME_MODE_SCORES;
+    SetGameStatus(GAME_MODE_SCORES);
 
     DrawHallOfFame(hi_pos);
 
@@ -5055,11 +4483,7 @@ void GameEnd()
   }
   else
   {
-#if 1
-    FadeOut(REDRAW_FIELD);
-#endif
-
-    game_status = GAME_MODE_MAIN;
+    SetGameStatus(GAME_MODE_MAIN);
 
     if (raise_level)
     {
@@ -5067,7 +4491,7 @@ void GameEnd()
       TapeErase();
     }
 
-    DrawAndFadeInMainMenu(REDRAW_FIELD);
+    DrawMainMenu();
   }
 }
 
@@ -5075,6 +4499,7 @@ int NewHiScore()
 {
   int k, l;
   int position = -1;
+  boolean one_score_entry_per_name = !program.many_scores_per_name;
 
   LoadScore(level_nr);
 
@@ -5092,13 +4517,15 @@ int NewHiScore()
       {
        int m = MAX_SCORE_ENTRIES - 1;
 
-#ifdef ONE_PER_NAME
-       for (l = k; l < MAX_SCORE_ENTRIES; l++)
-         if (strEqual(setup.player_name, highscore[l].Name))
-           m = l;
-       if (m == k)     /* player's new highscore overwrites his old one */
-         goto put_into_list;
-#endif
+       if (one_score_entry_per_name)
+       {
+         for (l = k; l < MAX_SCORE_ENTRIES; l++)
+           if (strEqual(setup.player_name, highscore[l].Name))
+             m = l;
+
+         if (m == k)   /* player's new highscore overwrites his old one */
+           goto put_into_list;
+       }
 
        for (l = m; l > k; l--)
        {
@@ -5107,22 +4534,19 @@ int NewHiScore()
        }
       }
 
-#ifdef ONE_PER_NAME
       put_into_list:
-#endif
+
       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
       highscore[k].Score = local_player->score_final; 
       position = k;
+
       break;
     }
-
-#ifdef ONE_PER_NAME
-    else if (!strncmp(setup.player_name, highscore[k].Name,
+    else if (one_score_entry_per_name &&
+            !strncmp(setup.player_name, highscore[k].Name,
                      MAX_PLAYER_NAME_LEN))
       break;   /* player already there with a higher score */
-#endif
-
   }
 
   if (position >= 0) 
@@ -5162,11 +4586,6 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
 {
   if (player->GfxAction != action || player->GfxDir != dir)
   {
-#if 0
-    printf("Player frame reset! (%d => %d, %d => %d)\n",
-          player->GfxAction, action, player->GfxDir, dir);
-#endif
-
     player->GfxAction = action;
     player->GfxDir = dir;
     player->Frame = 0;
@@ -5174,12 +4593,10 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
   }
 }
 
-#if USE_GFX_RESET_GFX_ANIMATION
-static void ResetGfxFrame(int x, int y, boolean redraw)
+static void ResetGfxFrame(int x, int y)
 {
   int element = Feld[x][y];
   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-  int last_gfx_frame = GfxFrame[x][y];
 
   if (graphic_info[graphic].anim_global_sync)
     GfxFrame[x][y] = FrameCounter;
@@ -5189,11 +4606,7 @@ static void ResetGfxFrame(int x, int y, boolean redraw)
     GfxFrame[x][y] = element_info[element].collect_score;
   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
     GfxFrame[x][y] = ChangeDelay[x][y];
-
-  if (redraw && GfxFrame[x][y] != last_gfx_frame)
-    DrawLevelGraphicAnimation(x, y, graphic);
 }
-#endif
 
 static void ResetGfxAnimation(int x, int y)
 {
@@ -5201,9 +4614,7 @@ static void ResetGfxAnimation(int x, int y)
   GfxDir[x][y] = MovDir[x][y];
   GfxFrame[x][y] = 0;
 
-#if USE_GFX_RESET_GFX_ANIMATION
-  ResetGfxFrame(x, y, FALSE);
-#endif
+  ResetGfxFrame(x, y);
 }
 
 static void ResetRandomAnimationValue(int x, int y)
@@ -5219,51 +4630,24 @@ void InitMovingField(int x, int y, int direction)
   int newx = x + dx;
   int newy = y + dy;
   boolean is_moving_before, is_moving_after;
-#if 0
-  boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
-#endif
 
   /* check if element was/is moving or being moved before/after mode change */
-#if 1
-#if 1
   is_moving_before = (WasJustMoving[x][y] != 0);
-#else
-  /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
-  is_moving_before = WasJustMoving[x][y];
-#endif
-#else
-  is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
-#endif
   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
 
   /* reset animation only for moving elements which change direction of moving
      or which just started or stopped moving
      (else CEs with property "can move" / "not moving" are reset each frame) */
-#if USE_GFX_RESET_ONLY_WHEN_MOVING
-#if 1
   if (is_moving_before != is_moving_after ||
       direction != MovDir[x][y])
     ResetGfxAnimation(x, y);
-#else
-  if ((is_moving_before || is_moving_after) && !continues_moving)
-    ResetGfxAnimation(x, y);
-#endif
-#else
-  if (!continues_moving)
-    ResetGfxAnimation(x, y);
-#endif
 
   MovDir[x][y] = direction;
   GfxDir[x][y] = direction;
 
-#if USE_GFX_RESET_ONLY_WHEN_MOVING
   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
                     direction == MV_DOWN && CAN_FALL(element) ?
                     ACTION_FALLING : ACTION_MOVING);
-#else
-  GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
-                    ACTION_FALLING : ACTION_MOVING);
-#endif
 
   /* this is needed for CEs with property "can move" / "not moving" */
 
@@ -5274,9 +4658,7 @@ void InitMovingField(int x, int y, int direction)
 
     MovDir[newx][newy] = MovDir[x][y];
 
-#if USE_NEW_CUSTOM_VALUE
     CustomValue[newx][newy] = CustomValue[x][y];
-#endif
 
     GfxFrame[newx][newy] = GfxFrame[x][y];
     GfxRandom[newx][newy] = GfxRandom[x][y];
@@ -5359,26 +4741,16 @@ static void RemoveField(int x, int y)
   MovDir[x][y] = 0;
   MovDelay[x][y] = 0;
 
-#if USE_NEW_CUSTOM_VALUE
   CustomValue[x][y] = 0;
-#endif
 
   AmoebaNr[x][y] = 0;
   ChangeDelay[x][y] = 0;
   ChangePage[x][y] = -1;
   Pushed[x][y] = FALSE;
 
-#if 0
-  ExplodeField[x][y] = EX_TYPE_NONE;
-#endif
-
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MV_NONE;
-#if 0
-  /* !!! this would prevent the removed tile from being redrawn !!! */
-  GfxRedraw[x][y] = GFX_REDRAW_NONE;
-#endif
 }
 
 void RemoveMovingField(int x, int y)
@@ -5535,204 +4907,92 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy)
 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
                        boolean center_screen, boolean quick_relocation)
 {
+  unsigned int frame_delay_value_old = GetVideoFrameDelay();
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
   boolean no_delay = (tape.warp_forward);
   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+  int new_scroll_x, new_scroll_y;
 
-  if (quick_relocation)
+  if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
   {
-    if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
-    {
-      if (!level.shifted_relocation || center_screen)
-      {
-       /* quick relocation (without scrolling), with centering of screen */
+    /* case 1: quick relocation inside visible screen (without scrolling) */
 
-       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-                   x > SBX_Right + MIDPOSX ? SBX_Right :
-                   x - MIDPOSX);
+    RedrawPlayfield();
 
-       scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                   y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                   y - MIDPOSY);
-      }
-      else
-      {
-       /* quick relocation (without scrolling), but do not center screen */
+    return;
+  }
 
-       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
-                              old_x - MIDPOSX);
+  if (!level.shifted_relocation || center_screen)
+  {
+    /* relocation _with_ centering of screen */
 
-       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                              old_y - MIDPOSY);
+    new_scroll_x = SCROLL_POSITION_X(x);
+    new_scroll_y = SCROLL_POSITION_Y(y);
+  }
+  else
+  {
+    /* relocation _without_ centering of screen */
 
-       int offset_x = x + (scroll_x - center_scroll_x);
-       int offset_y = y + (scroll_y - center_scroll_y);
+    int center_scroll_x = SCROLL_POSITION_X(old_x);
+    int center_scroll_y = SCROLL_POSITION_Y(old_y);
+    int offset_x = x + (scroll_x - center_scroll_x);
+    int offset_y = y + (scroll_y - center_scroll_y);
 
-       scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-                   offset_x - MIDPOSX);
+    /* for new screen position, apply previous offset to center position */
+    new_scroll_x = SCROLL_POSITION_X(offset_x);
+    new_scroll_y = SCROLL_POSITION_Y(offset_y);
+  }
 
-       scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                   offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                   offset_y - MIDPOSY);
-      }
-    }
-    else
-    {
-#if 1
-      if (!level.shifted_relocation || center_screen)
-      {
-       /* quick relocation (without scrolling), with centering of screen */
+  if (quick_relocation)
+  {
+    /* case 2: quick relocation (redraw without visible scrolling) */
 
-       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-                   x > SBX_Right + MIDPOSX ? SBX_Right :
-                   x - MIDPOSX);
+    scroll_x = new_scroll_x;
+    scroll_y = new_scroll_y;
 
-       scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                   y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                   y - MIDPOSY);
-      }
-      else
-      {
-       /* quick relocation (without scrolling), but do not center screen */
+    RedrawPlayfield();
+
+    return;
+  }
 
-       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
-                              old_x - MIDPOSX);
+  /* case 3: visible relocation (with scrolling to new position) */
 
-       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                              old_y - MIDPOSY);
+  ScrollScreen(NULL, SCROLL_GO_ON);    /* scroll last frame to full tile */
 
-       int offset_x = x + (scroll_x - center_scroll_x);
-       int offset_y = y + (scroll_y - center_scroll_y);
+  SetVideoFrameDelay(wait_delay_value);
 
-       scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-                   offset_x - MIDPOSX);
+  while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
+  {
+    int dx = 0, dy = 0;
+    int fx = FX, fy = FY;
 
-       scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                   offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                   offset_y - MIDPOSY);
-      }
-#else
-      /* quick relocation (without scrolling), inside visible screen area */
+    dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
+    dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
 
-      int offset = game.scroll_delay_value;
+    if (dx == 0 && dy == 0)            /* no scrolling needed at all */
+      break;
 
-      if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
-         (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
-       scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
+    scroll_x -= dx;
+    scroll_y -= dy;
 
-      if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
-         (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
-       scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
+    fx += dx * TILEX / 2;
+    fy += dy * TILEY / 2;
 
-      /* don't scroll over playfield boundaries */
-      if (scroll_x < SBX_Left || scroll_x > SBX_Right)
-       scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+    ScrollLevel(dx, dy);
+    DrawAllPlayers();
 
-      /* don't scroll over playfield boundaries */
-      if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
-       scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
-#endif
-    }
+    /* scroll in two steps of half tile size to make things smoother */
+    BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
 
-    RedrawPlayfield(TRUE, 0,0,0,0);
+    /* scroll second step to align at full tile size */
+    BlitScreenToBitmap(window);
   }
-  else
-  {
-#if 1
-    int scroll_xx, scroll_yy;
-
-    if (!level.shifted_relocation || center_screen)
-    {
-      /* visible relocation (with scrolling), with centering of screen */
-
-      scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-                  x > SBX_Right + MIDPOSX ? SBX_Right :
-                  x - MIDPOSX);
-
-      scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                  y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                  y - MIDPOSY);
-    }
-    else
-    {
-      /* visible relocation (with scrolling), but do not center screen */
-
-      int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
-                            old_x - MIDPOSX);
-
-      int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                            old_y - MIDPOSY);
-
-      int offset_x = x + (scroll_x - center_scroll_x);
-      int offset_y = y + (scroll_y - center_scroll_y);
-
-      scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                  offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-                  offset_x - MIDPOSX);
-
-      scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                  offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                  offset_y - MIDPOSY);
-    }
-
-#else
-
-    /* visible relocation (with scrolling), with centering of screen */
-
-    int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-                    x > SBX_Right + MIDPOSX ? SBX_Right :
-                    x - MIDPOSX);
-
-    int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                    y - MIDPOSY);
-#endif
-
-    ScrollScreen(NULL, SCROLL_GO_ON);  /* scroll last frame to full tile */
-
-    while (scroll_x != scroll_xx || scroll_y != scroll_yy)
-    {
-      int dx = 0, dy = 0;
-      int fx = FX, fy = FY;
-
-      dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
-      dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
-
-      if (dx == 0 && dy == 0)          /* no scrolling needed at all */
-       break;
-
-      scroll_x -= dx;
-      scroll_y -= dy;
-
-      fx += dx * TILEX / 2;
-      fy += dy * TILEY / 2;
-
-      ScrollLevel(dx, dy);
-      DrawAllPlayers();
 
-      /* scroll in two steps of half tile size to make things smoother */
-      BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
-      FlushDisplay();
-      Delay(wait_delay_value);
-
-      /* scroll second step to align at full tile size */
-      BackToFront();
-      Delay(wait_delay_value);
-    }
+  DrawAllPlayers();
+  BackToFront();
 
-    DrawAllPlayers();
-    BackToFront();
-    Delay(wait_delay_value);
-  }
+  SetVideoFrameDelay(frame_delay_value_old);
 }
 
 void RelocatePlayer(int jx, int jy, int el_player_raw)
@@ -5782,8 +5042,7 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
 
       DrawPlayer(player);
 
-      BackToFront();
-      Delay(wait_delay_value);
+      BackToFront_WithFrameDelay(wait_delay_value);
     }
 
     DrawPlayer(player);                /* needed here only to cleanup last field */
@@ -5811,12 +5070,6 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   if (!ELEM_IS_PLAYER(element))        /* player may be set on walkable element */
   {
     Feld[jx][jy] = element;    /* restore previously existing element */
-#if 0
-    /* !!! do not initialize already initialized element a second time !!! */
-    /* (this causes at least problems with "element creation" CE trigger for
-       already existing elements, and existing Sokoban fields counted twice) */
-    InitField(jx, jy, FALSE);
-#endif
   }
 
   /* only visually relocate centered player */
@@ -5833,7 +5086,6 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
                                      player->index_bit, enter_side);
 
-#if 1
   if (player->is_switching)
   {
     /* ensure that relocation while still switching an element does not cause
@@ -5846,7 +5098,6 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
     player->switch_x += jx - old_jx;
     player->switch_y += jy - old_jy;
   }
-#endif
 }
 
 void Explode(int ex, int ey, int phase, int mode)
@@ -5869,23 +5120,6 @@ void Explode(int ex, int ey, int phase, int mode)
     int center_element = Feld[ex][ey];
     int artwork_element, explosion_element;    /* set these values later */
 
-#if 0
-    /* --- This is only really needed (and now handled) in "Impact()". --- */
-    /* do not explode moving elements that left the explode field in time */
-    if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
-       center_element == EL_EMPTY &&
-       (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
-      return;
-#endif
-
-#if 0
-    /* !!! at this place, the center element may be EL_BLOCKED !!! */
-    if (mode == EX_TYPE_NORMAL ||
-       mode == EX_TYPE_CENTER ||
-       mode == EX_TYPE_CROSS)
-      PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
-#endif
-
     /* remove things displayed in background while burning dynamite */
     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
       Back[ex][ey] = 0;
@@ -5915,12 +5149,10 @@ void Explode(int ex, int ey, int phase, int mode)
       }
     }
 
-#if 1
     if (mode == EX_TYPE_NORMAL ||
        mode == EX_TYPE_CENTER ||
        mode == EX_TYPE_CROSS)
       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
-#endif
 
     last_phase = element_info[explosion_element].explosion_delay + 1;
 
@@ -6057,31 +5289,9 @@ void Explode(int ex, int ey, int phase, int mode)
 
   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
 
-#ifdef DEBUG
-
-  /* activate this even in non-DEBUG version until cause for crash in
-     getGraphicAnimationFrame() (see below) is found and eliminated */
-
-#endif
-#if 1
-
-#if 1
   /* this can happen if the player leaves an explosion just in time */
   if (GfxElement[x][y] == EL_UNDEFINED)
     GfxElement[x][y] = EL_EMPTY;
-#else
-  if (GfxElement[x][y] == EL_UNDEFINED)
-  {
-    printf("\n\n");
-    printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
-    printf("Explode(): This should never happen!\n");
-    printf("\n\n");
-
-    GfxElement[x][y] = EL_EMPTY;
-  }
-#endif
-
-#endif
 
   border_element = Store2[x][y];
   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
@@ -6153,9 +5363,7 @@ void Explode(int ex, int ey, int phase, int mode)
     ChangeDelay[x][y] = 0;
     ChangePage[x][y] = -1;
 
-#if USE_NEW_CUSTOM_VALUE
     CustomValue[x][y] = 0;
-#endif
 
     InitField_WithBug2(x, y, FALSE);
 
@@ -6255,12 +5463,7 @@ void Bang(int x, int y)
   {
     struct PlayerInfo *player = PLAYERINFO(x, y);
 
-#if USE_FIX_CE_ACTION_WITH_PLAYER
     element = Feld[x][y] = player->initial_element;
-#else
-    element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
-                           player->element_nr);
-#endif
 
     if (level.use_explosion_element[player->index_nr])
     {
@@ -6298,10 +5501,6 @@ void Bang(int x, int y)
       break;
 
     case EL_DC_LANDMINE:
-#if 0
-    case EL_EM_EXIT_OPEN:
-    case EL_EM_STEEL_EXIT_OPEN:
-#endif
       explosion_type = EX_TYPE_CENTER;
       break;
 
@@ -6525,20 +5724,6 @@ static void ToggleSwitchgateSwitch(int x, int y)
   {
     int element = Feld[xx][yy];
 
-#if !USE_BOTH_SWITCHGATE_SWITCHES
-    if (element == EL_SWITCHGATE_SWITCH_UP ||
-       element == EL_SWITCHGATE_SWITCH_DOWN)
-    {
-      Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      TEST_DrawLevelField(xx, yy);
-    }
-    else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
-            element == EL_DC_SWITCHGATE_SWITCH_DOWN)
-    {
-      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      TEST_DrawLevelField(xx, yy);
-    }
-#else
     if (element == EL_SWITCHGATE_SWITCH_UP)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
@@ -6559,7 +5744,6 @@ static void ToggleSwitchgateSwitch(int x, int y)
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
       TEST_DrawLevelField(xx, yy);
     }
-#endif
     else if (element == EL_SWITCHGATE_OPEN ||
             element == EL_SWITCHGATE_OPENING)
     {
@@ -6792,12 +5976,8 @@ static void ActivateTimegateSwitch(int x, int y)
 
   }
 
-#if 1
   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
                EL_DC_TIMEGATE_SWITCH_ACTIVE);
-#else
-  Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
-#endif
 }
 
 void Impact(int x, int y)
@@ -7023,10 +6203,6 @@ void Impact(int x, int y)
        }
        else
        {
-#if 0
-         TestIfElementSmashesCustomElement(x, y, MV_DOWN);
-#endif
-
          CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
 
          CheckElementChangeBySide(x, y + 1, smashed, element,
@@ -7328,7 +6504,6 @@ inline static void TurnRoundExt(int x, int y)
   }
   else if (element == EL_SPRING)
   {
-#if USE_NEW_SPRING_BUMPER
     if (MovDir[x][y] & MV_HORIZONTAL)
     {
       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
@@ -7344,12 +6519,6 @@ inline static void TurnRoundExt(int x, int y)
               SPRING_CAN_ENTER_FIELD(element, x, y + 1))
        MovDir[x][y] = MV_NONE;
     }
-#else
-    if (MovDir[x][y] & MV_HORIZONTAL &&
-       (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
-        SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
-      MovDir[x][y] = MV_NONE;
-#endif
 
     MovDelay[x][y] = 0;
   }
@@ -7891,7 +7060,7 @@ static void TurnRound(int x, int y)
   if (MovDelay[x][y])
     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
 
-  ResetGfxFrame(x, y, FALSE);
+  ResetGfxFrame(x, y);
 }
 
 static boolean JustBeingPushed(int x, int y)
@@ -8209,13 +7378,8 @@ void StartMoving(int x, int y)
       Store[x][y] = EL_ACID;
     }
     else if (
-#if USE_FIX_IMPACT_COLLISION
             (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
              CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
-#else
-            (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
-             CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
-#endif
             (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
              CAN_FALL(element) && WasJustFalling[x][y] &&
              (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
@@ -8275,7 +7439,6 @@ void StartMoving(int x, int y)
       boolean can_fall_both = (can_fall_left && can_fall_right);
       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
 
-#if USE_NEW_ALL_SLIPPERY
       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
       {
        if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
@@ -8290,37 +7453,7 @@ void StartMoving(int x, int y)
        can_fall_any  = (can_fall_left || can_fall_right);
        can_fall_both = FALSE;
       }
-#else
-      if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
-      {
-       if (slippery_type == SLIPPERY_ONLY_LEFT)
-         can_fall_right = FALSE;
-       else if (slippery_type == SLIPPERY_ONLY_RIGHT)
-         can_fall_left = FALSE;
-       else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
-         can_fall_right = FALSE;
-       else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
-         can_fall_left = FALSE;
-
-       can_fall_any  = (can_fall_left || can_fall_right);
-       can_fall_both = (can_fall_left && can_fall_right);
-      }
-#endif
-
-#if USE_NEW_ALL_SLIPPERY
-#else
-#if USE_NEW_SP_SLIPPERY
-      /* !!! better use the same properties as for custom elements here !!! */
-      else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
-              can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
-      {
-       can_fall_right = FALSE;         /* slip down on left side */
-       can_fall_both = FALSE;
-      }
-#endif
-#endif
 
-#if USE_NEW_ALL_SLIPPERY
       if (can_fall_both)
       {
        if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
@@ -8330,18 +7463,6 @@ void StartMoving(int x, int y)
 
        can_fall_both = FALSE;
       }
-#else
-      if (can_fall_both)
-      {
-       if (game.emulation == EMU_BOULDERDASH ||
-           element == EL_BD_ROCK || element == EL_BD_DIAMOND)
-         can_fall_right = FALSE;       /* slip down on left side */
-       else
-         can_fall_left = !(can_fall_right = RND(2));
-
-       can_fall_both = FALSE;
-      }
-#endif
 
       if (can_fall_any)
       {
@@ -8350,11 +7471,7 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
       }
     }
-#if 0
-    else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
-#else
     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
-#endif
     {
       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
@@ -8382,26 +7499,11 @@ void StartMoving(int x, int y)
   }
 
   /* not "else if" because of elements that can fall and move (EL_SPRING) */
-#if 0
-  if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
-#else
   if (CAN_MOVE(element) && !started_moving)
-#endif
   {
     int move_pattern = element_info[element].move_pattern;
     int newx, newy;
 
-#if 0
-#if DEBUG
-    if (MovDir[x][y] == MV_NONE)
-    {
-      printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
-            x, y, element, element_info[element].token_name);
-      printf("StartMoving(): This should never happen!\n");
-    }
-#endif
-#endif
-
     Moving2Blocked(x, y, &newx, &newy);
 
     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
@@ -8494,20 +7596,10 @@ void StartMoving(int x, int y)
          {
            int flamed = MovingOrBlocked2Element(xx, yy);
 
-           /* !!! */
-#if 0
-           if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
-             Bang(xx, yy);
-           else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
-             RemoveMovingField(xx, yy);
-           else
-             RemoveField(xx, yy);
-#else
            if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
              Bang(xx, yy);
            else
              RemoveMovingField(xx, yy);
-#endif
 
            ChangeDelay[xx][yy] = 0;
 
@@ -8703,78 +7795,14 @@ void StartMoving(int x, int y)
       }
       else if (!IS_FREE(newx, newy))
       {
-#if 0
-       if (IS_PLAYER(x, y))
-         DrawPlayerField(x, y);
-       else
-         TEST_DrawLevelField(x, y);
-#endif
-
        return;
       }
     }
     else if (IS_CUSTOM_ELEMENT(element) &&
             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
     {
-#if 1
       if (!DigFieldByCE(newx, newy, element))
        return;
-#else
-      int new_element = Feld[newx][newy];
-
-      if (!IS_FREE(newx, newy))
-      {
-       int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
-                     IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
-                     ACTION_BREAKING);
-
-       /* no element can dig solid indestructible elements */
-       if (IS_INDESTRUCTIBLE(new_element) &&
-           !IS_DIGGABLE(new_element) &&
-           !IS_COLLECTIBLE(new_element))
-         return;
-
-       if (AmoebaNr[newx][newy] &&
-           (new_element == EL_AMOEBA_FULL ||
-            new_element == EL_BD_AMOEBA ||
-            new_element == EL_AMOEBA_GROWING))
-       {
-         AmoebaCnt[AmoebaNr[newx][newy]]--;
-         AmoebaCnt2[AmoebaNr[newx][newy]]--;
-       }
-
-       if (IS_MOVING(newx, newy))
-         RemoveMovingField(newx, newy);
-       else
-       {
-         RemoveField(newx, newy);
-         TEST_DrawLevelField(newx, newy);
-       }
-
-       /* if digged element was about to explode, prevent the explosion */
-       ExplodeField[newx][newy] = EX_TYPE_NONE;
-
-       PlayLevelSoundAction(x, y, action);
-      }
-
-      Store[newx][newy] = EL_EMPTY;
-
-#if 1
-      /* this makes it possible to leave the removed element again */
-      if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
-       Store[newx][newy] = new_element;
-#else
-      if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
-      {
-       int move_leave_element = element_info[element].move_leave_element;
-
-       /* this makes it possible to leave the removed element again */
-       Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
-                            new_element : move_leave_element);
-      }
-#endif
-
-#endif
 
       if (move_pattern & MV_MAZE_RUNNER_STYLE)
       {
@@ -8822,25 +7850,11 @@ void StartMoving(int x, int y)
 
          MovDelay[x][y] = 50;
 
-         /* !!! */
-#if 0
-         RemoveField(newx, newy);
-#endif
          Feld[newx][newy] = EL_FLAMES;
          if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
-         {
-#if 0
-           RemoveField(newx1, newy1);
-#endif
            Feld[newx1][newy1] = EL_FLAMES;
-         }
          if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
-         {
-#if 0
-           RemoveField(newx2, newy2);
-#endif
            Feld[newx2][newy2] = EL_FLAMES;
-         }
 
          return;
        }
@@ -8870,18 +7884,10 @@ void StartMoving(int x, int y)
          AmoebaCnt[AmoebaNr[newx][newy]]--;
       }
 
-#if 0
-      /* !!! test !!! */
-      if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
-      {
-       RemoveMovingField(newx, newy);
-      }
-#else
       if (IS_MOVING(newx, newy))
       {
        RemoveMovingField(newx, newy);
       }
-#endif
       else
       {
        Feld[newx][newy] = EL_EMPTY;
@@ -8934,21 +7940,6 @@ void StartMoving(int x, int y)
 
       TurnRound(x, y);
 
-#if 0
-      /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
-      if (move_pattern & MV_ANY_DIRECTION &&
-         move_pattern == MovDir[x][y])
-      {
-       int blocking_element =
-         (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
-
-       CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
-                                MovDir[x][y]);
-
-       element = Feld[x][y];   /* element might have changed */
-      }
-#endif
-
       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
        DrawLevelElementAnimation(x, y, element);
 
@@ -8988,17 +7979,6 @@ void ContinueMoving(int x, int y)
 
   if (ABS(MovPos[x][y]) < TILEX)
   {
-#if 0
-    int ee = Feld[x][y];
-    int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-    int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
-
-    printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
-          x, y, ABS(MovPos[x][y]),
-          ee, gg, ff,
-          GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
-#endif
-
     TEST_DrawLevelField(x, y);
 
     return;    /* element is still moving */
@@ -9054,9 +8034,7 @@ void ContinueMoving(int x, int y)
       Feld[x][y] = EL_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
 
-#if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
-#endif
   }
   else if (element == EL_BD_MAGIC_WALL_FILLING)
   {
@@ -9072,9 +8050,7 @@ void ContinueMoving(int x, int y)
       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
 
-#if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
-#endif
   }
   else if (element == EL_DC_MAGIC_WALL_FILLING)
   {
@@ -9090,9 +8066,7 @@ void ContinueMoving(int x, int y)
       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
 
-#if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
-#endif
   }
   else if (element == EL_AMOEBA_DROPPING)
   {
@@ -9126,18 +8100,14 @@ void ContinueMoving(int x, int y)
     ChangeEvent[newx][newy] = ChangeEvent[x][y];
   }
 
-#if USE_NEW_CUSTOM_VALUE
   CustomValue[newx][newy] = CustomValue[x][y];
-#endif
 
   ChangeDelay[x][y] = 0;
   ChangePage[x][y] = -1;
   ChangeCount[x][y] = 0;
   ChangeEvent[x][y] = -1;
 
-#if USE_NEW_CUSTOM_VALUE
   CustomValue[x][y] = 0;
-#endif
 
   /* copy animation control values to new field */
   GfxFrame[newx][newy]  = GfxFrame[x][y];
@@ -9148,34 +8118,15 @@ void ContinueMoving(int x, int y)
   Pushed[x][y] = Pushed[newx][newy] = FALSE;
 
   /* some elements can leave other elements behind after moving */
-#if 1
   if (ei->move_leave_element != EL_EMPTY &&
       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
-#else
-  if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
-      (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
-      (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
-#endif
   {
     int move_leave_element = ei->move_leave_element;
 
-#if 1
-#if 1
     /* this makes it possible to leave the removed element again */
     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
-#else
-    /* this makes it possible to leave the removed element again */
-    if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
-      move_leave_element = stored;
-#endif
-#else
-    /* this makes it possible to leave the removed element again */
-    if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
-        ei->move_leave_element == EL_TRIGGER_ELEMENT)
-      move_leave_element = stored;
-#endif
 
     Feld[x][y] = move_leave_element;
 
@@ -9230,10 +8181,8 @@ void ContinueMoving(int x, int y)
     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
 
-#if USE_FIX_IMPACT_COLLISION
     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
-#endif
   }
 
   if (DONT_TOUCH(element))     /* object may be nasty to player or others */
@@ -9276,29 +8225,6 @@ void ContinueMoving(int x, int y)
   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
 
   TestIfElementTouchesCustomElement(x, y);     /* empty or new element */
-
-#if 0
-  if (ChangePage[newx][newy] != -1)            /* delayed change */
-  {
-    int page = ChangePage[newx][newy];
-    struct ElementChangeInfo *change = &ei->change_page[page];
-
-    ChangePage[newx][newy] = -1;
-
-    if (change->can_change)
-    {
-      if (ChangeElement(newx, newy, element, page))
-      {
-        if (change->post_change_function)
-          change->post_change_function(newx, newy);
-      }
-    }
-
-    if (change->has_action)
-      ExecuteCustomElementAction(newx, newy, element, page);
-  }
-#endif
-
   TestIfElementHitsCustomElement(newx, newy, direction);
   TestIfPlayerTouchesCustomElement(newx, newy);
   TestIfElementTouchesCustomElement(newx, newy);
@@ -9819,11 +8745,7 @@ static void RunTimegateWheel(int x, int y)
 
 static void InitMagicBallDelay(int x, int y)
 {
-#if 1
   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
-#else
-  ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
-#endif
 }
 
 static void ActivateMagicBall(int bx, int by)
@@ -10436,11 +9358,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      EL_EMPTY);
   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
 
-#if 0
-  if (action_arg_element_raw == EL_GROUP_START)
-    printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
-#endif
-
   int action_arg_direction =
     (action_arg >= CA_ARG_DIRECTION_LEFT &&
      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
@@ -10468,11 +9385,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
      action_type == CA_SET_LEVEL_TIME ? level.time :
      action_type == CA_SET_LEVEL_SCORE ? 0 :
-#if USE_NEW_CUSTOM_VALUE
      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
-#else
-     action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
-#endif
      action_type == CA_SET_CE_SCORE ? 0 :
      0);
 
@@ -10484,11 +9397,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
-#if USE_NEW_CUSTOM_VALUE
      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
-#else
-     action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
-#endif
      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
@@ -10518,17 +9427,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                            action_mode, action_arg_number,
                            action_arg_number_min, action_arg_number_max);
 
-#if 1
   int trigger_player_bits =
     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
      change->actual_trigger_player_bits : change->trigger_player);
-#else
-  int trigger_player_bits =
-    (change->actual_trigger_player >= EL_PLAYER_1 &&
-     change->actual_trigger_player <= EL_PLAYER_4 ?
-     (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
-     PLAYER_BITS_ANY);
-#endif
 
   int action_arg_player_bits =
     (action_arg >= CA_ARG_PLAYER_1 &&
@@ -10572,13 +9473,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       {
        TimeLeft = action_arg_number_new;
 
-#if 1
        game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
 
        DisplayGameControlValues();
-#else
-       DrawGameValue_Time(TimeLeft);
-#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
@@ -10592,13 +9489,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->score = action_arg_number_new;
 
-#if 1
       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
 
       DisplayGameControlValues();
-#else
-      DrawGameValue_Score(local_player->score);
-#endif
 
       break;
     }
@@ -10607,27 +9500,15 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->gems_still_needed = action_arg_number_new;
 
-#if 1
-      game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+      game.snapshot.collected_item = TRUE;
 
-      DisplayGameControlValues();
-#else
-      DrawGameValue_Emeralds(local_player->gems_still_needed);
-#endif
+      game_panel_controls[GAME_PANEL_GEMS].value =
+       local_player->gems_still_needed;
 
-      break;
-    }
+      DisplayGameControlValues();
 
-#if !USE_PLAYER_GRAVITY
-    case CA_SET_LEVEL_GRAVITY:
-    {
-      game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
-                     action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
-                     action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
-                     game.gravity);
       break;
     }
-#endif
 
     case CA_SET_LEVEL_WIND:
     {
@@ -10638,27 +9519,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_LEVEL_RANDOM_SEED:
     {
-#if 1
       /* ensure that setting a new random seed while playing is predictable */
       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
-#else
-      InitRND(action_arg_number_new);
-#endif
-
-#if 0
-      printf("::: %d -> %d\n", action_arg_number_new, RND(10));
-#endif
-
-#if 0
-      {
-       int i;
-
-       printf("::: ");
-       for (i = 0; i < 9; i++)
-         printf("%d, ", RND(2));
-       printf("\n");
-      }
-#endif
 
       break;
     }
@@ -10717,10 +9579,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_PLAYER_SPEED:
     {
-#if 0
-      printf("::: trigger_player_bits == %d\n", trigger_player_bits);
-#endif
-
       for (i = 0; i < MAX_PLAYERS; i++)
       {
        if (trigger_player_bits & (1 << i))
@@ -10784,7 +9642,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
-#if USE_PLAYER_GRAVITY
     case CA_SET_PLAYER_GRAVITY:
     {
       for (i = 0; i < MAX_PLAYERS; i++)
@@ -10801,7 +9658,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
       break;
     }
-#endif
 
     case CA_SET_PLAYER_ARTWORK:
     {
@@ -10816,10 +9672,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
              (level.use_artwork_element[i] ? level.artwork_element[i] :
               stored_player[i].element_nr);
 
-#if USE_GFX_RESET_PLAYER_ARTWORK
          if (stored_player[i].artwork_element != artwork_element)
            stored_player[i].Frame = 0;
-#endif
 
          stored_player[i].artwork_element = artwork_element;
 
@@ -10940,7 +9794,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_CE_VALUE:
     {
-#if USE_NEW_CUSTOM_VALUE
       int last_ce_value = CustomValue[x][y];
 
       CustomValue[x][y] = action_arg_number_new;
@@ -10956,14 +9809,12 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
          CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
        }
       }
-#endif
 
       break;
     }
 
     case CA_SET_CE_SCORE:
     {
-#if USE_NEW_CUSTOM_VALUE
       int last_ce_score = ei->collect_score;
 
       ei->collect_score = action_arg_number_new;
@@ -10999,7 +9850,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
          }
        }
       }
-#endif
 
       break;
     }
@@ -11055,32 +9905,13 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
   int old_element = Feld[x][y];
   int new_element = GetElementFromGroupElement(element);
   int previous_move_direction = MovDir[x][y];
-#if USE_NEW_CUSTOM_VALUE
   int last_ce_value = CustomValue[x][y];
-#endif
   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
   boolean add_player_onto_element = (new_element_is_player &&
-#if USE_CODE_THAT_BREAKS_SNAKE_BITE
-                                    /* this breaks SnakeBite when a snake is
-                                       halfway through a door that closes */
-                                    /* NOW FIXED AT LEVEL INIT IN files.c */
                                     new_element != EL_SOKOBAN_FIELD_PLAYER &&
-#endif
                                     IS_WALKABLE(old_element));
 
-#if 0
-  /* check if element under the player changes from accessible to unaccessible
-     (needed for special case of dropping element which then changes) */
-  if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
-      IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
-  {
-    Bang(x, y);
-
-    return;
-  }
-#endif
-
   if (!add_player_onto_element)
   {
     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
@@ -11090,27 +9921,18 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
 
     Feld[x][y] = new_element;
 
-#if !USE_GFX_RESET_GFX_ANIMATION
-    ResetGfxAnimation(x, y);
-    ResetRandomAnimationValue(x, y);
-#endif
-
     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
       MovDir[x][y] = previous_move_direction;
 
-#if USE_NEW_CUSTOM_VALUE
     if (element_info[new_element].use_last_ce_value)
       CustomValue[x][y] = last_ce_value;
-#endif
 
     InitField_WithBug1(x, y, FALSE);
 
     new_element = Feld[x][y];  /* element may have changed */
 
-#if USE_GFX_RESET_GFX_ANIMATION
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
-#endif
 
     TEST_DrawLevelField(x, y);
 
@@ -11118,11 +9940,9 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
   }
 
-#if 1
   /* check if element under the player changes from accessible to unaccessible
      (needed for special case of dropping element which then changes) */
   /* (must be checked after creating new element for walkable group elements) */
-#if USE_FIX_KILLED_BY_NON_WALKABLE
   if (IS_PLAYER(x, y) && !player_explosion_protected &&
       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
   {
@@ -11130,16 +9950,6 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
 
     return;
   }
-#else
-  if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
-      IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
-  {
-    Bang(x, y);
-
-    return;
-  }
-#endif
-#endif
 
   /* "ChangeCount" not set yet to allow "entered by player" change one time */
   if (new_element_is_player)
@@ -11162,7 +9972,6 @@ static void CreateElementFromChange(int x, int y, int element)
 {
   element = GET_VALID_RUNTIME_ELEMENT(element);
 
-#if USE_STOP_CHANGED_ELEMENTS
   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
   {
     int old_element = Feld[x][y];
@@ -11173,7 +9982,6 @@ static void CreateElementFromChange(int x, int y, int element)
        (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
       Stop[x][y] = TRUE;
   }
-#endif
 
   CreateFieldExt(x, y, element, TRUE);
 }
@@ -11349,8 +10157,6 @@ static boolean ChangeElement(int x, int y, int element, int page)
   return TRUE;
 }
 
-#if USE_NEW_DELAYED_ACTION
-
 static void HandleElementChange(int x, int y, int page)
 {
   int element = MovingOrBlocked2Element(x, y);
@@ -11373,11 +10179,6 @@ static void HandleElementChange(int x, int y, int page)
   /* this can happen with classic bombs on walkable, changing elements */
   if (!CAN_CHANGE_OR_HAS_ACTION(element))
   {
-#if 0
-    if (!CAN_CHANGE(Back[x][y]))       /* prevent permanent repetition */
-      ChangeDelay[x][y] = 0;
-#endif
-
     return;
   }
 
@@ -11387,19 +10188,56 @@ static void HandleElementChange(int x, int y, int page)
 
     if (change->can_change)
     {
-#if 1
       /* !!! not clear why graphic animation should be reset at all here !!! */
       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
-#if USE_GFX_RESET_WHEN_NOT_MOVING
+      /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
+
+      /*
+       GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
+
+       When using an animation frame delay of 1 (this only happens with
+       "sp_zonk.moving.left/right" in the classic graphics), the default
+       (non-moving) animation shows wrong animation frames (while the
+       moving animation, like "sp_zonk.moving.left/right", is correct,
+       so this graphical bug never shows up with the classic graphics).
+       For an animation with 4 frames, this causes wrong frames 0,0,1,2
+       be drawn instead of the correct frames 0,1,2,3. This is caused by
+       "GfxFrame[][]" being reset *twice* (in two successive frames) after
+       an element change: First when the change delay ("ChangeDelay[][]")
+       counter has reached zero after decrementing, then a second time in
+       the next frame (after "GfxFrame[][]" was already incremented) when
+       "ChangeDelay[][]" is reset to the initial delay value again.
+
+       This causes frame 0 to be drawn twice, while the last frame won't
+       be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
+
+       As some animations may already be cleverly designed around this bug
+       (at least the "Snake Bite" snake tail animation does this), it cannot
+       simply be fixed here without breaking such existing animations.
+       Unfortunately, it cannot easily be detected if a graphics set was
+       designed "before" or "after" the bug was fixed. As a workaround,
+       a new graphics set option "game.graphics_engine_version" was added
+       to be able to specify the game's major release version for which the
+       graphics set was designed, which can then be used to decide if the
+       bugfix should be used (version 4 and above) or not (version 3 or
+       below, or if no version was specified at all, as with old sets).
+
+       (The wrong/fixed animation frames can be tested with the test level set
+       "test_gfxframe" and level "000", which contains a specially prepared
+       custom element at level position (x/y) == (11/9) which uses the zonk
+       animation mentioned above. Using "game.graphics_engine_version: 4"
+       fixes the wrong animation frames, showing the correct frames 0,1,2,3.
+       This can also be seen from the debug output for this test element.)
+      */
+
       /* when a custom element is about to change (for example by change delay),
         do not reset graphic animation when the custom element is moving */
-      if (!IS_MOVING(x, y))
-#endif
+      if (game.graphics_engine_version < 4 &&
+         !IS_MOVING(x, y))
       {
        ResetGfxAnimation(x, y);
        ResetRandomAnimationValue(x, y);
       }
-#endif
 
       if (change->pre_change_function)
        change->pre_change_function(x, y);
@@ -11439,14 +10277,12 @@ static void HandleElementChange(int x, int y, int page)
       return;
     }
 
-#if 1
     /* special case: set new level random seed before changing element */
     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
       handle_action_before_change = TRUE;
 
     if (change->has_action && handle_action_before_change)
       ExecuteCustomElementAction(x, y, element, page);
-#endif
 
     if (change->can_change)
     {
@@ -11462,87 +10298,6 @@ static void HandleElementChange(int x, int y, int page)
   }
 }
 
-#else
-
-static void HandleElementChange(int x, int y, int page)
-{
-  int element = MovingOrBlocked2Element(x, y);
-  struct ElementInfo *ei = &element_info[element];
-  struct ElementChangeInfo *change = &ei->change_page[page];
-
-#ifdef DEBUG
-  if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
-  {
-    printf("\n\n");
-    printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
-          x, y, element, element_info[element].token_name);
-    printf("HandleElementChange(): This should never happen!\n");
-    printf("\n\n");
-  }
-#endif
-
-  /* this can happen with classic bombs on walkable, changing elements */
-  if (!CAN_CHANGE(element))
-  {
-#if 0
-    if (!CAN_CHANGE(Back[x][y]))       /* prevent permanent repetition */
-      ChangeDelay[x][y] = 0;
-#endif
-
-    return;
-  }
-
-  if (ChangeDelay[x][y] == 0)          /* initialize element change */
-  {
-    ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
-
-    ResetGfxAnimation(x, y);
-    ResetRandomAnimationValue(x, y);
-
-    if (change->pre_change_function)
-      change->pre_change_function(x, y);
-  }
-
-  ChangeDelay[x][y]--;
-
-  if (ChangeDelay[x][y] != 0)          /* continue element change */
-  {
-    int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-
-    if (IS_ANIMATED(graphic))
-      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-
-    if (change->change_function)
-      change->change_function(x, y);
-  }
-  else                                 /* finish element change */
-  {
-    if (ChangePage[x][y] != -1)                /* remember page from delayed change */
-    {
-      page = ChangePage[x][y];
-      ChangePage[x][y] = -1;
-
-      change = &ei->change_page[page];
-    }
-
-    if (IS_MOVING(x, y))               /* never change a running system ;-) */
-    {
-      ChangeDelay[x][y] = 1;           /* try change after next move step */
-      ChangePage[x][y] = page;         /* remember page to use for change */
-
-      return;
-    }
-
-    if (ChangeElement(x, y, element, page))
-    {
-      if (change->post_change_function)
-       change->post_change_function(x, y);
-    }
-  }
-}
-
-#endif
-
 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
                                              int trigger_element,
                                              int trigger_event,
@@ -11557,12 +10312,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
   if (!(trigger_events[trigger_element][trigger_event]))
     return FALSE;
 
-#if 0
-  printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
-        trigger_event, recursion_loop_depth, recursion_loop_detected,
-        recursion_loop_element, EL_NAME(recursion_loop_element));
-#endif
-
   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -11593,11 +10342,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
        change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
        change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
 
-#if 0
-       printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
-              element, EL_NAME(element), p);
-#endif
-
        if ((change->can_change && !change_done) || change->has_action)
        {
          int x, y;
@@ -11608,7 +10352,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
            {
              if (change->can_change && !change_done)
              {
-#if USE_FIX_NO_ACTION_AFTER_CHANGE
                /* if element already changed in this frame, not only prevent
                   another element change (checked in ChangeElement()), but
                   also prevent additional element actions for this element */
@@ -11616,22 +10359,14 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
                if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
                    !level.use_action_after_change_bug)
                  continue;
-#endif
-
-#if 0
-               printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
-                      element, EL_NAME(element), p);
-#endif
 
                ChangeDelay[x][y] = 1;
                ChangeEvent[x][y] = trigger_event;
 
                HandleElementChange(x, y, p);
              }
-#if USE_NEW_DELAYED_ACTION
              else if (change->has_action)
              {
-#if USE_FIX_NO_ACTION_AFTER_CHANGE
                /* if element already changed in this frame, not only prevent
                   another element change (checked in ChangeElement()), but
                   also prevent additional element actions for this element */
@@ -11639,24 +10374,10 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
                if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
                    !level.use_action_after_change_bug)
                  continue;
-#endif
-
-
-#if 0
-               printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
-                      element, EL_NAME(element), p);
-#endif
 
                ExecuteCustomElementAction(x, y, element, p);
                PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
              }
-#else
-             if (change->has_action)
-             {
-               ExecuteCustomElementAction(x, y, element, p);
-               PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
-             }
-#endif
            }
          }
 
@@ -11664,12 +10385,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
          {
            change_done = TRUE;
            change_done_any = TRUE;
-
-#if 0
-           printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
-                  element, EL_NAME(element), p);
-#endif
-
          }
        }
       }
@@ -11701,11 +10416,6 @@ static boolean CheckElementChangeExt(int x, int y,
     element = Feld[x][y];
   }
 
-#if 0
-  /* check if element has already changed */
-  if (Feld[x][y] != element)
-    return FALSE;
-#else
   /* check if element has already changed or is about to change after moving */
   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
        Feld[x][y] != element) ||
@@ -11714,20 +10424,9 @@ static boolean CheckElementChangeExt(int x, int y,
        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
        ChangePage[x][y] != -1)))
     return FALSE;
-#endif
-
-#if 0
-  printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
-        trigger_event, recursion_loop_depth, recursion_loop_detected,
-        recursion_loop_element, EL_NAME(recursion_loop_element));
-#endif
 
   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
 
-#if 0
-  printf("::: X: trigger_player_bits == %d\n", trigger_player);
-#endif
-
   for (p = 0; p < element_info[element].num_change_pages; p++)
   {
     struct ElementChangeInfo *change = &element_info[element].change_page[p];
@@ -11740,10 +10439,7 @@ static boolean CheckElementChangeExt(int x, int y,
       (trigger_event == CE_TOUCHING_X ||
        trigger_event == CE_HITTING_X ||
        trigger_event == CE_HIT_BY_X ||
-#if 1
-       /* this one was forgotten until 3.2.3 */
-       trigger_event == CE_DIGGING_X);
-#endif
+       trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
 
     if (change->can_change_or_has_action &&
        change->has_event[trigger_event] &&
@@ -11792,19 +10488,11 @@ static boolean CheckElementChangeExt(int x, int y,
 
        change_done = TRUE;
       }
-#if USE_NEW_DELAYED_ACTION
       else if (change->has_action)
       {
        ExecuteCustomElementAction(x, y, element, p);
        PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
       }
-#else
-      if (change->has_action)
-      {
-       ExecuteCustomElementAction(x, y, element, p);
-       PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
-      }
-#endif
     }
   }
 
@@ -11995,18 +10683,49 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
   }
 }
 
+static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
+{
+  if ((!player->is_moving  && player->was_moving) ||
+      (player->MovPos == 0 && player->was_moving) ||
+      (player->is_snapping && !player->was_snapping) ||
+      (player->is_dropping && !player->was_dropping))
+  {
+    if (!CheckSaveEngineSnapshotToList())
+      return;
+
+    player->was_moving = FALSE;
+    player->was_snapping = TRUE;
+    player->was_dropping = TRUE;
+  }
+  else
+  {
+    if (player->is_moving)
+      player->was_moving = TRUE;
+
+    if (!player->is_snapping)
+      player->was_snapping = FALSE;
+
+    if (!player->is_dropping)
+      player->was_dropping = FALSE;
+  }
+}
+
 static void CheckSingleStepMode(struct PlayerInfo *player)
 {
   if (tape.single_step && tape.recording && !tape.pausing)
   {
     /* as it is called "single step mode", just return to pause mode when the
        player stopped moving after one tile (or never starts moving at all) */
-    if (!player->is_moving && !player->is_pushing)
+    if (!player->is_moving &&
+       !player->is_pushing &&
+       !player->is_dropping_pressed)
     {
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
       SnapField(player, 0, 0);                 /* stop snapping */
     }
   }
+
+  CheckSaveEngineSnapshot(player);
 }
 
 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
@@ -12132,18 +10851,11 @@ static void CheckLevelTime()
        if (TimeLeft <= 10 && setup.time_limit)
          PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
-#if 1
        /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
           is reset from other values in UpdateGameDoorValues() -- FIX THIS */
 
        game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
 
-       /* (already called by UpdateAndDisplayGameControlValues() below) */
-       // DisplayGameControlValues();
-#else
-       DrawGameValue_Time(TimeLeft);
-#endif
-
        if (!TimeLeft && setup.time_limit)
        {
          if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
@@ -12153,18 +10865,10 @@ static void CheckLevelTime()
              KillPlayer(&stored_player[i]);
        }
       }
-#if 1
       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
       {
        game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
-
-       /* (already called by UpdateAndDisplayGameControlValues() below) */
-       // DisplayGameControlValues();
       }
-#else
-      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
-       DrawGameValue_Time(TimePlayed);
-#endif
 
       level.native_em_level->lev->time =
        (game.no_time_limit ? TimePlayed : TimeLeft);
@@ -12174,12 +10878,10 @@ static void CheckLevelTime()
       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
   }
 
-#if 1
+  if (tape.recording || tape.playing)
+    DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
+
   UpdateAndDisplayGameControlValues();
-#else
-  UpdateGameDoorValues();
-  DrawGameDoorValues();
-#endif
 }
 
 void AdvanceFrameAndPlayerCounters(int player_nr)
@@ -12200,7 +10902,6 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
     if (!advance_player_counters)      /* not all players may be affected */
       continue;
 
-#if USE_NEW_PLAYER_ANIM
     if (move_frames == 0)      /* less than one move per game frame */
     {
       int stepsize = TILEX / move_delay_value;
@@ -12211,7 +10912,6 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
       if (count % delay == 0)
        move_frames = 1;
     }
-#endif
 
     stored_player[i].Frame += move_frames;
 
@@ -12253,9 +10953,11 @@ void StartGameActions(boolean init_network_game, boolean record_tape,
   InitGame();
 }
 
-void GameActions()
+void GameActionsExt()
 {
+#if 0
   static unsigned int game_frame_delay = 0;
+#endif
   unsigned int game_frame_delay_value;
   byte *recorded_player_action;
   byte summarized_player_action = 0;
@@ -12333,9 +11035,22 @@ void GameActions()
   if (tape.playing && tape.warp_forward && !tape.pausing)
     game_frame_delay_value = 0;
 
+  SetVideoFrameDelay(game_frame_delay_value);
+
+#if 0
+#if 0
+  /* ---------- main game synchronization point ---------- */
+
+  int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+
+  printf("::: skip == %d\n", skip);
+
+#else
   /* ---------- main game synchronization point ---------- */
 
   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+#endif
+#endif
 
   if (network_playing && !network_player_action_received)
   {
@@ -12366,11 +11081,9 @@ void GameActions()
   /* when playing tape, read previously recorded player input from tape data */
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
-#if 1
   /* TapePlayAction() may return NULL when toggling to "pause before death" */
   if (tape.pausing)
     return;
-#endif
 
   if (tape.set_centered_player)
   {
@@ -12382,13 +11095,8 @@ void GameActions()
   {
     summarized_player_action |= stored_player[i].action;
 
-#if 1
     if (!network_playing && (game.team_mode || tape.playing))
       stored_player[i].effective_action = stored_player[i].action;
-#else
-    if (!network_playing)
-      stored_player[i].effective_action = stored_player[i].action;
-#endif
   }
 
 #if defined(NETWORK_AVALIABLE)
@@ -12396,8 +11104,11 @@ void GameActions()
     SendToServer_MovePlayer(summarized_player_action);
 #endif
 
+  // summarize all actions at local players mapped input device position
+  // (this allows using different input devices in single player mode)
   if (!options.network && !game.team_mode)
-    local_player->effective_action = summarized_player_action;
+    stored_player[map_player_action[local_player->index_nr]].effective_action =
+      summarized_player_action;
 
   if (tape.recording &&
       setup.team_mode &&
@@ -12417,19 +11128,13 @@ void GameActions()
   {
     tape_action[i] = stored_player[i].effective_action;
 
-#if 1
     /* (this may happen in the RND game engine if a player was not present on
        the playfield on level start, but appeared later from a custom element */
-    if (tape.recording &&
-       setup.team_mode &&
+    if (setup.team_mode &&
+       tape.recording &&
        tape_action[i] &&
        !tape.player_participates[i])
       tape.player_participates[i] = TRUE;
-#else
-    /* (this can only happen in the R'n'D game engine) */
-    if (tape.recording && tape_action[i] && !tape.player_participates[i])
-      tape.player_participates[i] = TRUE;    /* player just appeared from CE */
-#endif
   }
 
   /* only record actions from input devices, but not programmed actions */
@@ -12437,9 +11142,9 @@ void GameActions()
     TapeRecordAction(tape_action);
 
 #if USE_NEW_PLAYER_ASSIGNMENTS
-#if 1
-  if (game.team_mode)
-#endif
+  // !!! also map player actions in single player mode !!!
+  // if (game.team_mode)
+  if (1)
   {
     byte mapped_action[MAX_PLAYERS];
 
@@ -12473,40 +11178,20 @@ void GameActions()
 #endif
 #endif
 
-#if 0
-  printf("::: summarized_player_action == %d\n",
-        local_player->effective_action);
-#endif
-
-
-
-
-#if 0
-#if DEBUG_INIT_PLAYER
-    if (options.debug)
-    {
-      printf("Player status (final):\n");
-
-      for (i = 0; i < MAX_PLAYERS; i++)
-      {
-       struct PlayerInfo *player = &stored_player[i];
-
-       printf("- player %d: present == %d, connected == %d, active == %d",
-              i + 1,
-              player->present,
-              player->connected,
-              player->active);
-
-       if (local_player == player)
-         printf(" (local player)");
-
-       printf("\n");
-      }
-    }
-#endif
-#endif
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    // allow engine snapshot in case of changed movement attempt
+    if ((game.snapshot.last_action[i] & KEY_MOTION) !=
+       (stored_player[i].effective_action & KEY_MOTION))
+      game.snapshot.changed_action = TRUE;
 
+    // allow engine snapshot in case of snapping/dropping attempt
+    if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
+       (stored_player[i].effective_action & KEY_BUTTON) != 0)
+      game.snapshot.changed_action = TRUE;
 
+    game.snapshot.last_action[i] = stored_player[i].effective_action;
+  }
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
@@ -12518,10 +11203,53 @@ void GameActions()
   }
   else
   {
-    GameActions_RND();
+    GameActions_RND_Main();
+  }
+
+  BlitScreenToBitmap(backbuffer);
+
+  CheckLevelTime();
+
+  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
+
+  if (options.debug)                   /* calculate frames per second */
+  {
+    static unsigned int fps_counter = 0;
+    static int fps_frames = 0;
+    unsigned int fps_delay_ms = Counter() - fps_counter;
+
+    fps_frames++;
+
+    if (fps_delay_ms >= 500)   /* calculate fps every 0.5 seconds */
+    {
+      global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
+
+      fps_frames = 0;
+      fps_counter = Counter();
+    }
+
+    redraw_mask |= REDRAW_FPS;
   }
 }
 
+static void GameActions_CheckSaveEngineSnapshot()
+{
+  if (!game.snapshot.save_snapshot)
+    return;
+
+  // clear flag for saving snapshot _before_ saving snapshot
+  game.snapshot.save_snapshot = FALSE;
+
+  SaveEngineSnapshotToList();
+}
+
+void GameActions()
+{
+  GameActionsExt();
+
+  GameActions_CheckSaveEngineSnapshot();
+}
+
 void GameActions_EM_Main()
 {
   byte effective_action[MAX_PLAYERS];
@@ -12532,10 +11260,6 @@ void GameActions_EM_Main()
     effective_action[i] = stored_player[i].effective_action;
 
   GameActions_EM(effective_action, warp_mode);
-
-  CheckLevelTime();
-
-  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
 }
 
 void GameActions_SP_Main()
@@ -12549,19 +11273,27 @@ void GameActions_SP_Main()
 
   GameActions_SP(effective_action, warp_mode);
 
-  CheckLevelTime();
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    if (stored_player[i].force_dropping)
+      stored_player[i].action |= KEY_BUTTON_DROP;
 
-  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
+    stored_player[i].force_dropping = FALSE;
+  }
+}
+
+void GameActions_RND_Main()
+{
+  GameActions_RND();
 }
 
 void GameActions_RND()
 {
   int magic_wall_x = 0, magic_wall_y = 0;
-  int i, x, y, element, graphic;
+  int i, x, y, element, graphic, last_gfx_frame;
 
   InitPlayfieldScanModeVars();
 
-#if USE_ONE_MORE_CHANGE_PER_FRAME
   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
   {
     SCAN_PLAYFIELD(x, y)
@@ -12570,7 +11302,6 @@ void GameActions_RND()
       ChangeEvent[x][y] = -1;
     }
   }
-#endif
 
   if (game.set_centered_player)
   {
@@ -12670,10 +11401,6 @@ void GameActions_RND()
     }
   }
 
-#if 0
-  debug_print_timestamp(0, "start main loop profiling");
-#endif
-
   SCAN_PLAYFIELD(x, y)
   {
     ChangeCount[x][y] = 0;
@@ -12687,7 +11414,6 @@ void GameActions_RND()
        RemoveField(x, y);
     }
 
-#if USE_NEW_SNAP_DELAY
     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
     {
       MovDelay[x][y]--;
@@ -12699,7 +11425,6 @@ void GameActions_RND()
        TestIfElementTouchesCustomElement(x, y);        /* for empty space */
       }
     }
-#endif
 
 #if DEBUG
     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
@@ -12748,69 +11473,16 @@ void GameActions_RND()
 #endif
   }
 
-#if 0
-  debug_print_timestamp(0, "- time for pre-main loop:");
-#endif
-
-#if 0  // -------------------- !!! TEST ONLY !!! --------------------
   SCAN_PLAYFIELD(x, y)
   {
     element = Feld[x][y];
     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+    last_gfx_frame = GfxFrame[x][y];
 
-#if 1
-    {
-#if 1
-      int element2 = element;
-      int graphic2 = graphic;
-#else
-      int element2 = Feld[x][y];
-      int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
-#endif
-      int last_gfx_frame = GfxFrame[x][y];
+    ResetGfxFrame(x, y);
 
-      if (graphic_info[graphic2].anim_global_sync)
-       GfxFrame[x][y] = FrameCounter;
-      else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
-       GfxFrame[x][y] = CustomValue[x][y];
-      else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
-       GfxFrame[x][y] = element_info[element2].collect_score;
-      else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
-       GfxFrame[x][y] = ChangeDelay[x][y];
-
-      if (redraw && GfxFrame[x][y] != last_gfx_frame)
-       DrawLevelGraphicAnimation(x, y, graphic2);
-    }
-#else
-    ResetGfxFrame(x, y, TRUE);
-#endif
-
-#if 1
-    if (ANIM_MODE(graphic) == ANIM_RANDOM &&
-       IS_NEXT_FRAME(GfxFrame[x][y], graphic))
-      ResetRandomAnimationValue(x, y);
-#endif
-
-#if 1
-    SetRandomAnimationValue(x, y);
-#endif
-
-#if 1
-    PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
-#endif
-  }
-#endif // -------------------- !!! TEST ONLY !!! --------------------
-
-#if 0
-  debug_print_timestamp(0, "- time for TEST loop:     -->");
-#endif
-
-  SCAN_PLAYFIELD(x, y)
-  {
-    element = Feld[x][y];
-    graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-
-    ResetGfxFrame(x, y, TRUE);
+    if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
+      DrawLevelGraphicAnimation(x, y, graphic);
 
     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
        IS_NEXT_FRAME(GfxFrame[x][y], graphic))
@@ -12834,22 +11506,12 @@ void GameActions_RND()
     {
       int page = element_info[element].event_page_nr[CE_DELAY];
 
-#if 1
       HandleElementChange(x, y, page);
-#else
-      if (CAN_CHANGE(element))
-       HandleElementChange(x, y, page);
-
-      if (HAS_ACTION(element))
-       ExecuteCustomElementAction(x, y, element, page);
-#endif
 
       element = Feld[x][y];
       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
     }
 
-#if 0  // ---------------------------------------------------------------------
-
     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
     {
       StartMoving(x, y);
@@ -12865,143 +11527,12 @@ void GameActions_RND()
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
        TEST_DrawTwinkleOnField(x, y);
     }
-    else if (IS_MOVING(x, y))
-      ContinueMoving(x, y);
-    else
-    {
-      switch (element)
-      {
-        case EL_ACID:
-        case EL_EXIT_OPEN:
-        case EL_EM_EXIT_OPEN:
-        case EL_SP_EXIT_OPEN:
-        case EL_STEEL_EXIT_OPEN:
-        case EL_EM_STEEL_EXIT_OPEN:
-        case EL_SP_TERMINAL:
-        case EL_SP_TERMINAL_ACTIVE:
-        case EL_EXTRA_TIME:
-        case EL_SHIELD_NORMAL:
-        case EL_SHIELD_DEADLY:
-         if (IS_ANIMATED(graphic))
-           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-         break;
-
-        case EL_DYNAMITE_ACTIVE:
-        case EL_EM_DYNAMITE_ACTIVE:
-        case EL_DYNABOMB_PLAYER_1_ACTIVE:
-        case EL_DYNABOMB_PLAYER_2_ACTIVE:
-        case EL_DYNABOMB_PLAYER_3_ACTIVE:
-        case EL_DYNABOMB_PLAYER_4_ACTIVE:
-        case EL_SP_DISK_RED_ACTIVE:
-         CheckDynamite(x, y);
-         break;
-
-        case EL_AMOEBA_GROWING:
-         AmoebeWaechst(x, y);
-         break;
-
-        case EL_AMOEBA_SHRINKING:
-         AmoebaDisappearing(x, y);
-         break;
-
-#if !USE_NEW_AMOEBA_CODE
-        case EL_AMOEBA_WET:
-        case EL_AMOEBA_DRY:
-        case EL_AMOEBA_FULL:
-        case EL_BD_AMOEBA:
-        case EL_EMC_DRIPPER:
-         AmoebeAbleger(x, y);
-         break;
-#endif
-
-        case EL_GAME_OF_LIFE:
-        case EL_BIOMAZE:
-         Life(x, y);
-         break;
-
-        case EL_EXIT_CLOSED:
-         CheckExit(x, y);
-         break;
-
-        case EL_EM_EXIT_CLOSED:
-         CheckExitEM(x, y);
-         break;
-
-        case EL_STEEL_EXIT_CLOSED:
-         CheckExitSteel(x, y);
-         break;
-
-        case EL_EM_STEEL_EXIT_CLOSED:
-         CheckExitSteelEM(x, y);
-         break;
-
-        case EL_SP_EXIT_CLOSED:
-         CheckExitSP(x, y);
-         break;
-
-        case EL_EXPANDABLE_WALL_GROWING:
-        case EL_EXPANDABLE_STEELWALL_GROWING:
-         MauerWaechst(x, y);
-         break;
-
-        case EL_EXPANDABLE_WALL:
-        case EL_EXPANDABLE_WALL_HORIZONTAL:
-        case EL_EXPANDABLE_WALL_VERTICAL:
-        case EL_EXPANDABLE_WALL_ANY:
-        case EL_BD_EXPANDABLE_WALL:
-         MauerAbleger(x, y);
-         break;
-
-        case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
-        case EL_EXPANDABLE_STEELWALL_VERTICAL:
-        case EL_EXPANDABLE_STEELWALL_ANY:
-         MauerAblegerStahl(x, y);
-         break;
-
-        case EL_FLAMES:
-         CheckForDragon(x, y);
-         break;
-
-        case EL_EXPLOSION:
-         break;
-
-        case EL_ELEMENT_SNAPPING:
-        case EL_DIAGONAL_SHRINKING:
-        case EL_DIAGONAL_GROWING:
-       {
-         graphic =
-           el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
-
-         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-         break;
-       }
-
-        default:
-         if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
-           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-         break;
-      }
-    }
-
-#else  // ---------------------------------------------------------------------
-
-    if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
+    else if (element == EL_ACID)
     {
-      StartMoving(x, y);
-
-      element = Feld[x][y];
-      graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-
-      if (IS_ANIMATED(graphic) &&
-         !IS_MOVING(x, y) &&
-         !Stop[x][y])
+      if (!Stop[x][y])
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-
-      if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       TEST_DrawTwinkleOnField(x, y);
     }
-    else if ((element == EL_ACID ||
-             element == EL_EXIT_OPEN ||
+    else if ((element == EL_EXIT_OPEN ||
              element == EL_EM_EXIT_OPEN ||
              element == EL_SP_EXIT_OPEN ||
              element == EL_STEEL_EXIT_OPEN ||
@@ -13067,8 +11598,6 @@ void GameActions_RND()
     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
-#endif // ---------------------------------------------------------------------
-
     if (IS_BELT_ACTIVE(element))
       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
 
@@ -13094,10 +11623,6 @@ void GameActions_RND()
     }
   }
 
-#if 0
-  debug_print_timestamp(0, "- time for MAIN loop:     -->");
-#endif
-
 #if USE_NEW_AMOEBA_CODE
   /* new experimental amoeba growth stuff */
   if (!(FrameCounter % 8))
@@ -13130,27 +11655,22 @@ void GameActions_RND()
   }
 #endif
 
-#if 0
-  if (game.explosions_delayed)
-#endif
-  {
-    game.explosions_delayed = FALSE;
+  game.explosions_delayed = FALSE;
 
-    SCAN_PLAYFIELD(x, y)
-    {
-      element = Feld[x][y];
-
-      if (ExplodeField[x][y])
-       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
-      else if (element == EL_EXPLOSION)
-       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
+  SCAN_PLAYFIELD(x, y)
+  {
+    element = Feld[x][y];
 
-      ExplodeField[x][y] = EX_TYPE_NONE;
-    }
+    if (ExplodeField[x][y])
+      Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
+    else if (element == EL_EXPLOSION)
+      Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
 
-    game.explosions_delayed = TRUE;
+    ExplodeField[x][y] = EX_TYPE_NONE;
   }
 
+  game.explosions_delayed = TRUE;
+
   if (game.magic_wall_active)
   {
     if (!(game.magic_wall_time_left % 4))
@@ -13252,12 +11772,7 @@ void GameActions_RND()
 #if USE_DELAYED_GFX_REDRAW
   SCAN_PLAYFIELD(x, y)
   {
-#if 1
     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
-#else
-    if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
-       GfxRedraw[x][y] != GFX_REDRAW_NONE)
-#endif
     {
       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
         !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
@@ -13279,32 +11794,9 @@ void GameActions_RND()
   }
 #endif
 
-  CheckLevelTime();
-
   DrawAllPlayers();
   PlayAllPlayersSound();
 
-  if (options.debug)                   /* calculate frames per second */
-  {
-    static unsigned int fps_counter = 0;
-    static int fps_frames = 0;
-    unsigned int fps_delay_ms = Counter() - fps_counter;
-
-    fps_frames++;
-
-    if (fps_delay_ms >= 500)   /* calculate fps every 0.5 seconds */
-    {
-      global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
-
-      fps_frames = 0;
-      fps_counter = Counter();
-    }
-
-    redraw_mask |= REDRAW_FPS;
-  }
-
-  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
-
   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
   {
     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
@@ -13312,11 +11804,6 @@ void GameActions_RND()
     local_player->show_envelope = 0;
   }
 
-#if 0
-  debug_print_timestamp(0, "stop main loop profiling ");
-  printf("----------------------------------------------------------\n");
-#endif
-
   /* use random number generator in every frame to make it less predictable */
   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
     RND(1);
@@ -13363,101 +11850,16 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
-#if 0
-  /* (directly solved in BlitBitmap() now) */
-  static Bitmap *bitmap_db_field2 = NULL;
-  int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
+  int scroll_offset = 2 * TILEX_VAR;
   int x, y;
-#else
-  int x, y;
-#endif
-
-#if 0
-  /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
-  /* only horizontal XOR vertical scroll direction allowed */
-  if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
-    return;
-#endif
-
-#if 0
-  /* (directly solved in BlitBitmap() now) */
-  if (bitmap_db_field2 == NULL)
-    bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
-
-  /* needed when blitting directly to same bitmap -- should not be needed with
-     recent SDL libraries, but apparently does not work in 1.2.11 directly */
-  BlitBitmap(drawto_field, bitmap_db_field2,
-            FX + TILEX * (dx == -1) - softscroll_offset,
-            FY + TILEY * (dy == -1) - softscroll_offset,
-            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
-            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
-            FX + TILEX * (dx == 1) - softscroll_offset,
-            FY + TILEY * (dy == 1) - softscroll_offset);
-  BlitBitmap(bitmap_db_field2, drawto_field,
-            FX + TILEX * (dx == 1) - softscroll_offset,
-            FY + TILEY * (dy == 1) - softscroll_offset,
-            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
-            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
-            FX + TILEX * (dx == 1) - softscroll_offset,
-            FY + TILEY * (dy == 1) - softscroll_offset);
-
-#else
-
-#if 0
-  /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
-  int xsize = (BX2 - BX1 + 1);
-  int ysize = (BY2 - BY1 + 1);
-  int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
-  int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
-  int step  = (start < end ? +1 : -1);
-
-  for (i = start; i != end; i += step)
-  {
-    BlitBitmap(drawto_field, drawto_field,
-              FX + TILEX * (dx != 0 ? i + step : 0),
-              FY + TILEY * (dy != 0 ? i + step : 0),
-              TILEX * (dx != 0 ? 1 : xsize),
-              TILEY * (dy != 0 ? 1 : ysize),
-              FX + TILEX * (dx != 0 ? i : 0),
-              FY + TILEY * (dy != 0 ? i : 0));
-  }
-
-#else
-
-#if NEW_TILESIZE
-#if NEW_SCROLL
-  int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
-#else
-  int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
-#endif
-#else
-#if NEW_SCROLL
-  int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
-#else
-  int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
-#endif
-#endif
 
-#if NEW_TILESIZE
   BlitBitmap(drawto_field, drawto_field,
-            FX + TILEX_VAR * (dx == -1) - softscroll_offset,
-            FY + TILEY_VAR * (dy == -1) - softscroll_offset,
-            SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
-            SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
-            FX + TILEX_VAR * (dx == 1) - softscroll_offset,
-            FY + TILEY_VAR * (dy == 1) - softscroll_offset);
-#else
-  BlitBitmap(drawto_field, drawto_field,
-            FX + TILEX * (dx == -1) - softscroll_offset,
-            FY + TILEY * (dy == -1) - softscroll_offset,
-            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
-            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
-            FX + TILEX * (dx == 1) - softscroll_offset,
-            FY + TILEY * (dy == 1) - softscroll_offset);
-#endif
-
-#endif
-#endif
+            FX + TILEX_VAR * (dx == -1) - scroll_offset,
+            FY + TILEY_VAR * (dy == -1) - scroll_offset,
+            SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
+            SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
+            FX + TILEX_VAR * (dx == 1) - scroll_offset,
+            FY + TILEY_VAR * (dy == 1) - scroll_offset);
 
   if (dx != 0)
   {
@@ -13520,11 +11922,7 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
 
 static void CheckGravityMovement(struct PlayerInfo *player)
 {
-#if USE_PLAYER_GRAVITY
   if (player->gravity && !player->programmed_action)
-#else
-  if (game.gravity && !player->programmed_action)
-#endif
   {
     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
@@ -13546,11 +11944,7 @@ static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
 {
   return CheckGravityMovement(player);
 
-#if USE_PLAYER_GRAVITY
   if (player->gravity && !player->programmed_action)
-#else
-  if (game.gravity && !player->programmed_action)
-#endif
   {
     int jx = player->jx, jy = player->jy;
     boolean field_under_player_is_free =
@@ -13577,9 +11971,6 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 {
   int jx = player->jx, jy = player->jy;
   int new_jx = jx + dx, new_jy = jy + dy;
-#if !USE_FIXED_DONT_RUN_INTO
-  int element;
-#endif
   int can_move;
   boolean player_can_move = !player->cannot_move;
 
@@ -13606,36 +11997,9 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
     }
   }
 
-#if 1
   if (!options.network && game.centered_player_nr == -1 &&
       !AllPlayersInSight(player, new_jx, new_jy))
     return MP_NO_ACTION;
-#else
-  if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
-    return MP_NO_ACTION;
-#endif
-
-#if !USE_FIXED_DONT_RUN_INTO
-  element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
-
-  /* (moved to DigField()) */
-  if (player_can_move && DONT_RUN_INTO(element))
-  {
-    if (element == EL_ACID && dx == 0 && dy == 1)
-    {
-      SplashAcid(new_jx, new_jy);
-      Feld[jx][jy] = EL_PLAYER_1;
-      InitMovingField(jx, jy, MV_DOWN);
-      Store[jx][jy] = EL_ACID;
-      ContinueMoving(jx, jy);
-      BuryPlayer(player);
-    }
-    else
-      TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
-
-    return MP_MOVING;
-  }
-#endif
 
   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
   if (can_move != MP_MOVING)
@@ -13665,9 +12029,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 
   PlayerVisit[jx][jy] = FrameCounter;
 
-#if USE_UFAST_PLAYER_EXIT_BUGFIX
   player->is_moving = TRUE;
-#endif
 
 #if 1
   /* should better be called in MovePlayer(), but this breaks some tapes */
@@ -13734,7 +12096,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       AdvanceFrameAndPlayerCounters(player->index_nr);
 
       DrawAllPlayers();
-      BackToFront();
+      BackToFront_WithFrameDelay(0);
     }
 
     player->move_delay_value = original_move_delay_value;
@@ -13753,7 +12115,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
   }
 
-#if USE_FIXED_BORDER_RUNNING_GFX
   if (!moved && !player->is_active)
   {
     player->is_moving = FALSE;
@@ -13762,19 +12123,13 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->is_snapping = FALSE;
     player->is_pushing = FALSE;
   }
-#endif
 
   jx = player->jx;
   jy = player->jy;
 
-#if 1
   if (moved & MP_MOVING && !ScreenMovPos &&
       (player->index_nr == game.centered_player_nr ||
        game.centered_player_nr == -1))
-#else
-  if (moved & MP_MOVING && !ScreenMovPos &&
-      (player == local_player || !options.network))
-#endif
   {
     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
     int offset = game.scroll_delay_value;
@@ -13829,7 +12184,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
     {
-#if 1
       if (!options.network && game.centered_player_nr == -1 &&
          !AllPlayersInVisibleScreen())
       {
@@ -13837,14 +12191,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
        scroll_y = old_scroll_y;
       }
       else
-#else
-      if (!options.network && !AllPlayersInVisibleScreen())
-      {
-       scroll_x = old_scroll_x;
-       scroll_y = old_scroll_y;
-      }
-      else
-#endif
       {
        ScrollScreen(player, SCROLL_INIT);
        ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
@@ -13912,16 +12258,11 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
   int last_jx = player->last_jx, last_jy = player->last_jy;
   int move_stepsize = TILEX / player->move_delay_value;
 
-#if USE_NEW_PLAYER_SPEED
   if (!player->active)
     return;
 
   if (player->MovPos == 0 && mode == SCROLL_GO_ON)     /* player not moving */
     return;
-#else
-  if (!player->active || player->MovPos == 0)
-    return;
-#endif
 
   if (mode == SCROLL_INIT)
   {
@@ -13940,13 +12281,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        last_field_block_delay += player->move_delay_value;
 
        /* when blocking enabled, prevent moving up despite gravity */
-#if USE_PLAYER_GRAVITY
        if (player->gravity && player->MovDir == MV_UP)
          block_delay_adjustment = -1;
-#else
-       if (game.gravity && player->MovDir == MV_UP)
-         block_delay_adjustment = -1;
-#endif
       }
 
       /* add block delay adjustment (also possible when not blocking) */
@@ -13956,17 +12292,12 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
     }
 
-#if USE_NEW_PLAYER_SPEED
     if (player->MovPos != 0)   /* player has not yet reached destination */
       return;
-#else
-    return;
-#endif
   }
   else if (!FrameReached(&player->actual_frame_counter, 1))
     return;
 
-#if USE_NEW_PLAYER_SPEED
   if (player->MovPos != 0)
   {
     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
@@ -13976,14 +12307,6 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     if (player->MovPos == 0)
       CheckGravityMovement(player);
   }
-#else
-  player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
-  player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
-
-  /* before DrawPlayer() to draw correct player graphic for this case */
-  if (player->MovPos == 0)
-    CheckGravityMovement(player);
-#endif
 
   if (player->MovPos == 0)     /* player reached destination field */
   {
@@ -14006,14 +12329,10 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
     if (Feld[jx][jy] == EL_EXIT_OPEN ||
        Feld[jx][jy] == EL_EM_EXIT_OPEN ||
-#if 1
        Feld[jx][jy] == EL_EM_EXIT_OPENING ||
-#endif
        Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
        Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
-#if 1
        Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
-#endif
        Feld[jx][jy] == EL_SP_EXIT_OPEN ||
        Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
     {
@@ -14052,13 +12371,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                          CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
 
-#if USE_FIX_CE_ACTION_WITH_PLAYER
       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
                                        CE_MOVE_OF_X, move_direction);
-#else
-      CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
-                                       CE_MOVE_OF_X, move_direction);
-#endif
     }
 
     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
@@ -14088,34 +12402,28 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        if (TimeLeft <= 10 && setup.time_limit)
          PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
-#if 1
        game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
 
        DisplayGameControlValues();
-#else
-       DrawGameValue_Time(TimeLeft);
-#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
            KillPlayer(&stored_player[i]);
       }
-#if 1
       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
       {
        game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
 
        DisplayGameControlValues();
       }
-#else
-      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
-       DrawGameValue_Time(TimePlayed);
-#endif
     }
 
     if (tape.single_step && tape.recording && !tape.pausing &&
        !player->programmed_action)
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+    if (!player->programmed_action)
+      CheckSaveEngineSnapshot(player);
   }
 }
 
@@ -14204,7 +12512,6 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, border_side);
 
-#if USE_FIX_CE_ACTION_WITH_PLAYER
       {
        /* use player element that is initially defined in the level playfield,
           not the player element that corresponds to the runtime player number
@@ -14215,7 +12522,6 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
        CheckElementChangeBySide(xx, yy, border_element, player_element,
                                 CE_TOUCHING_X, border_side);
       }
-#endif
     }
     else if (IS_PLAYER(xx, yy))                /* player found at border element */
     {
@@ -14233,7 +12539,6 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, center_side);
 
-#if USE_FIX_CE_ACTION_WITH_PLAYER
       {
        /* use player element that is initially defined in the level playfield,
           not the player element that corresponds to the runtime player number
@@ -14244,15 +12549,12 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
        CheckElementChangeBySide(x, y, center_element, player_element,
                                 CE_TOUCHING_X, center_side);
       }
-#endif
 
       break;
     }
   }
 }
 
-#if USE_ELEMENT_TOUCHING_BUGFIX
-
 void TestIfElementTouchesCustomElement(int x, int y)
 {
   static int xy[4][2] =
@@ -14338,7 +12640,6 @@ void TestIfElementTouchesCustomElement(int x, int y)
        CheckElementChangeBySide(x, y, center_element, border_element,
                                 CE_TOUCHING_X, border_side);
 
-#if USE_FIX_CE_ACTION_WITH_PLAYER
     if (IS_PLAYER(xx, yy))
     {
       /* use player element that is initially defined in the level playfield,
@@ -14350,74 +12651,9 @@ void TestIfElementTouchesCustomElement(int x, int y)
       CheckElementChangeBySide(x, y, center_element, player_element,
                               CE_TOUCHING_X, border_side);
     }
-#endif
-  }
-}
-
-#else
-
-void TestIfElementTouchesCustomElement_OLD(int x, int y)
-{
-  static int xy[4][2] =
-  {
-    { 0, -1 },
-    { -1, 0 },
-    { +1, 0 },
-    { 0, +1 }
-  };
-  static int trigger_sides[4][2] =
-  {
-    /* center side     border side */
-    { CH_SIDE_TOP,     CH_SIDE_BOTTOM  },      /* check top    */
-    { CH_SIDE_LEFT,    CH_SIDE_RIGHT   },      /* check left   */
-    { CH_SIDE_RIGHT,   CH_SIDE_LEFT    },      /* check right  */
-    { CH_SIDE_BOTTOM,  CH_SIDE_TOP     }       /* check bottom */
-  };
-  static int touch_dir[4] =
-  {
-    MV_LEFT | MV_RIGHT,
-    MV_UP   | MV_DOWN,
-    MV_UP   | MV_DOWN,
-    MV_LEFT | MV_RIGHT
-  };
-  boolean change_center_element = FALSE;
-  int center_element = Feld[x][y];     /* should always be non-moving! */
-  int i;
-
-  for (i = 0; i < NUM_DIRECTIONS; i++)
-  {
-    int xx = x + xy[i][0];
-    int yy = y + xy[i][1];
-    int center_side = trigger_sides[i][0];
-    int border_side = trigger_sides[i][1];
-    int border_element;
-
-    if (!IN_LEV_FIELD(xx, yy))
-      continue;
-
-    if (game.engine_version < VERSION_IDENT(3,0,7,0))
-      border_element = Feld[xx][yy];   /* may be moving! */
-    else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
-      border_element = Feld[xx][yy];
-    else if (MovDir[xx][yy] & touch_dir[i])    /* elements are touching */
-      border_element = MovingOrBlocked2Element(xx, yy);
-    else
-      continue;                        /* center and border element do not touch */
-
-    /* check for change of center element (but change it only once) */
-    if (!change_center_element)
-      change_center_element =
-       CheckElementChangeBySide(x, y, center_element, border_element,
-                                CE_TOUCHING_X, border_side);
-
-    /* check for change of border element */
-    CheckElementChangeBySide(xx, yy, border_element, center_element,
-                            CE_TOUCHING_X, center_side);
   }
 }
 
-#endif
-
 void TestIfElementHitsCustomElement(int x, int y, int direction)
 {
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
@@ -14454,7 +12690,6 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
                               CE_HIT_BY_SOMETHING, opposite_direction);
 
-#if USE_FIX_CE_ACTION_WITH_PLAYER
       if (IS_PLAYER(hitx, hity))
       {
        /* use player element that is initially defined in the level playfield,
@@ -14466,7 +12701,6 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
        CheckElementChangeBySide(x, y, hitting_element, player_element,
                                 CE_HITTING_X, touched_side);
       }
-#endif
     }
   }
 
@@ -14475,69 +12709,6 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
                           CE_HITTING_SOMETHING, direction);
 }
 
-#if 0
-void TestIfElementSmashesCustomElement(int x, int y, int direction)
-{
-  int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
-  int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
-  int hitx = x + dx, hity = y + dy;
-  int hitting_element = Feld[x][y];
-  int touched_element;
-#if 0
-  boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
-                       !IS_FREE(hitx, hity) &&
-                       (!IS_MOVING(hitx, hity) ||
-                        MovDir[hitx][hity] != direction ||
-                        ABS(MovPos[hitx][hity]) <= TILEY / 2));
-#endif
-
-  if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
-    return;
-
-#if 0
-  if (IN_LEV_FIELD(hitx, hity) && !object_hit)
-    return;
-#endif
-
-  touched_element = (IN_LEV_FIELD(hitx, hity) ?
-                    MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
-
-  CheckElementChangeBySide(x, y, hitting_element, touched_element,
-                          EP_CAN_SMASH_EVERYTHING, direction);
-
-  if (IN_LEV_FIELD(hitx, hity))
-  {
-    int opposite_direction = MV_DIR_OPPOSITE(direction);
-    int hitting_side = direction;
-    int touched_side = opposite_direction;
-#if 0
-    int touched_element = MovingOrBlocked2Element(hitx, hity);
-#endif
-#if 1
-    boolean object_hit = (!IS_MOVING(hitx, hity) ||
-                         MovDir[hitx][hity] != direction ||
-                         ABS(MovPos[hitx][hity]) <= TILEY / 2);
-
-    object_hit = TRUE;
-#endif
-
-    if (object_hit)
-    {
-      int i;
-
-      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
-                              CE_SMASHED_BY_SOMETHING, opposite_direction);
-
-      CheckElementChangeBySide(x, y, hitting_element, touched_element,
-                              CE_OTHER_IS_SMASHING, touched_side);
-
-      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
-                              CE_OTHER_GETS_SMASHED, hitting_side);
-    }
-  }
-}
-#endif
-
 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 {
   int i, kill_x = -1, kill_y = -1;
@@ -14869,19 +13040,10 @@ void KillPlayer(struct PlayerInfo *player)
         player->killed, player->active, player->reanimated);
 #endif
 
-#if USE_PLAYER_REANIMATION
-#if 1
   if (player->reanimated)      /* killed player may have been reanimated */
     player->killed = player->reanimated = FALSE;
   else
     BuryPlayer(player);
-#else
-  if (player->killed)          /* player may have been reanimated */
-    BuryPlayer(player);
-#endif
-#else
-  BuryPlayer(player);
-#endif
 }
 
 static void KillPlayerUnlessEnemyProtected(int x, int y)
@@ -14935,7 +13097,6 @@ void RemovePlayer(struct PlayerInfo *player)
   ExitY = ZY = jy;
 }
 
-#if USE_NEW_SNAP_DELAY
 static void setFieldForSnapping(int x, int y, int element, int direction)
 {
   struct ElementInfo *ei = &element_info[element];
@@ -14954,7 +13115,6 @@ static void setFieldForSnapping(int x, int y, int element, int direction)
   GfxDir[x][y] = direction;
   GfxFrame[x][y] = -1;
 }
-#endif
 
 /*
   =============================================================================
@@ -15011,11 +13171,7 @@ static int DigField(struct PlayerInfo *player,
   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
   int dig_side = MV_DIR_OPPOSITE(move_direction);
   int old_element = Feld[jx][jy];
-#if USE_FIXED_DONT_RUN_INTO
   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
-#else
-  int element;
-#endif
   int collect_count;
 
   if (is_player)               /* function can also be called by EL_PENGUIN */
@@ -15038,11 +13194,6 @@ static int DigField(struct PlayerInfo *player,
     }
   }
 
-#if !USE_FIXED_DONT_RUN_INTO
-  if (IS_MOVING(x, y) || IS_PLAYER(x, y))
-    return MP_NO_ACTION;
-#endif
-
   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
 
@@ -15057,7 +13208,6 @@ static int DigField(struct PlayerInfo *player,
   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
     return MP_NO_ACTION;       /* field has no opening in this direction */
 
-#if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
   {
     SplashAcid(x, y);
@@ -15070,25 +13220,16 @@ static int DigField(struct PlayerInfo *player,
 
     return MP_DONT_RUN_INTO;
   }
-#endif
 
-#if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
   {
     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
 
     return MP_DONT_RUN_INTO;
   }
-#endif
 
-#if USE_FIXED_DONT_RUN_INTO
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
     return MP_NO_ACTION;
-#endif
-
-#if !USE_FIXED_DONT_RUN_INTO
-  element = Feld[x][y];
-#endif
 
   collect_count = element_info[element].collect_count_initial;
 
@@ -15115,17 +13256,10 @@ static int DigField(struct PlayerInfo *player,
     return MP_NO_ACTION;
   }
 
-#if USE_PLAYER_GRAVITY
   if (player->gravity && is_player && !player->is_auto_moving &&
       canFallDown(player) && move_direction != MV_DOWN &&
       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
     return MP_NO_ACTION;       /* player cannot walk here due to gravity */
-#else
-  if (game.gravity && is_player && !player->is_auto_moving &&
-      canFallDown(player) && move_direction != MV_DOWN &&
-      !canMoveToValidFieldWithGravity(jx, jy, move_direction))
-    return MP_NO_ACTION;       /* player cannot walk here due to gravity */
-#endif
 
   if (player_can_move &&
       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
@@ -15150,14 +13284,10 @@ static int DigField(struct PlayerInfo *player,
     }
     else if (element == EL_EXIT_OPEN ||
             element == EL_EM_EXIT_OPEN ||
-#if 1
             element == EL_EM_EXIT_OPENING ||
-#endif
             element == EL_STEEL_EXIT_OPEN ||
             element == EL_EM_STEEL_EXIT_OPEN ||
-#if 1
             element == EL_EM_STEEL_EXIT_OPENING ||
-#endif
             element == EL_SP_EXIT_OPEN ||
             element == EL_SP_EXIT_OPENING)
     {
@@ -15228,29 +13358,17 @@ static int DigField(struct PlayerInfo *player,
          element == EL_SP_GRAVITY_PORT_RIGHT ||
          element == EL_SP_GRAVITY_PORT_UP ||
          element == EL_SP_GRAVITY_PORT_DOWN)
-#if USE_PLAYER_GRAVITY
        player->gravity = !player->gravity;
-#else
-       game.gravity = !game.gravity;
-#endif
       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
               element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
               element == EL_SP_GRAVITY_ON_PORT_UP ||
               element == EL_SP_GRAVITY_ON_PORT_DOWN)
-#if USE_PLAYER_GRAVITY
        player->gravity = TRUE;
-#else
-       game.gravity = TRUE;
-#endif
       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
               element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
               element == EL_SP_GRAVITY_OFF_PORT_UP ||
               element == EL_SP_GRAVITY_OFF_PORT_DOWN)
-#if USE_PLAYER_GRAVITY
        player->gravity = FALSE;
-#else
-       game.gravity = FALSE;
-#endif
     }
 
     /* automatically move to the next field with double speed */
@@ -15282,14 +13400,10 @@ static int DigField(struct PlayerInfo *player,
 
     if (mode == DF_SNAP)
     {
-#if USE_NEW_SNAP_DELAY
       if (level.block_snap_field)
        setFieldForSnapping(x, y, element, move_direction);
       else
        TestIfElementTouchesCustomElement(x, y);        /* for empty space */
-#else
-      TestIfElementTouchesCustomElement(x, y);         /* for empty space */
-#endif
 
       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
                                          player->index_bit, dig_side);
@@ -15313,13 +13427,9 @@ static int DigField(struct PlayerInfo *player,
     {
       TimeLeft += level.extra_time;
 
-#if 1
       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
 
       DisplayGameControlValues();
-#else
-      DrawGameValue_Time(TimeLeft);
-#endif
     }
     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
     {
@@ -15398,13 +13508,11 @@ static int DigField(struct PlayerInfo *player,
       if (local_player->gems_still_needed < 0)
        local_player->gems_still_needed = 0;
 
-#if 1
+      game.snapshot.collected_item = TRUE;
+
       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
 
       DisplayGameControlValues();
-#else
-      DrawGameValue_Emeralds(local_player->gems_still_needed);
-#endif
     }
 
     RaiseScoreElement(element);
@@ -15416,14 +13524,10 @@ static int DigField(struct PlayerInfo *player,
 
     if (mode == DF_SNAP)
     {
-#if USE_NEW_SNAP_DELAY
       if (level.block_snap_field)
        setFieldForSnapping(x, y, element, move_direction);
       else
        TestIfElementTouchesCustomElement(x, y);        /* for empty space */
-#else
-      TestIfElementTouchesCustomElement(x, y);         /* for empty space */
-#endif
 
       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
                                          player->index_bit, dig_side);
@@ -15535,13 +13639,8 @@ static int DigField(struct PlayerInfo *player,
        PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
                                    ACTION_FILLING);
 
-#if 1
       if (local_player->sokobanfields_still_needed == 0 &&
          (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
-#else
-      if (local_player->sokobanfields_still_needed == 0 &&
-         game.emulation == EMU_SOKOBAN)
-#endif
       {
        PlayerWins(player);
 
@@ -15609,9 +13708,16 @@ static int DigField(struct PlayerInfo *player,
       SCAN_PLAYFIELD(xx, yy)
       {
        if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
+       {
          Bang(xx, yy);
+       }
        else if (Feld[xx][yy] == EL_SP_TERMINAL)
+       {
          Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
+
+         ResetGfxAnimation(xx, yy);
+         TEST_DrawLevelField(xx, yy);
+       }
       }
     }
     else if (IS_BELT_SWITCH(element))
@@ -15666,13 +13772,9 @@ static int DigField(struct PlayerInfo *player,
        TimeLeft += level.time_orb_time;
        game.no_time_limit = FALSE;
 
-#if 1
        game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
 
        DisplayGameControlValues();
-#else
-       DrawGameValue_Time(TimeLeft);
-#endif
       }
 
       ResetGfxAnimation(x, y);
@@ -15799,20 +13901,9 @@ static boolean DigFieldByCE(int x, int y, int digging_element)
 
   Store[x][y] = EL_EMPTY;
 
-#if 1
   /* this makes it possible to leave the removed element again */
   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
     Store[x][y] = element;
-#else
-  if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
-  {
-    int move_leave_element = element_info[digging_element].move_leave_element;
-
-    /* this makes it possible to leave the removed element again */
-    Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
-                  element : move_leave_element);
-  }
-#endif
 
   return TRUE;
 }
@@ -15854,14 +13945,9 @@ static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
     return FALSE;
   }
 
-#if USE_NEW_CONTINUOUS_SNAPPING
   /* prevent snapping with already pressed snap key when not allowed */
   if (player->is_snapping && !can_continue_snapping)
     return FALSE;
-#else
-  if (player->is_snapping)
-    return FALSE;
-#endif
 
   player->MovDir = snap_direction;
 
@@ -15903,19 +13989,7 @@ static boolean DropElement(struct PlayerInfo *player)
   int dropx = player->jx, dropy = player->jy;
   int drop_direction = player->MovDir;
   int drop_side = drop_direction;
-#if 1
   int drop_element = get_next_dropped_element(player);
-#else
-  int drop_element = (player->inventory_size > 0 ?
-                     player->inventory_element[player->inventory_size - 1] :
-                     player->inventory_infinite_element != EL_UNDEFINED ?
-                     player->inventory_infinite_element :
-                     player->dynabombs_left > 0 ?
-                     EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
-                     EL_UNDEFINED);
-#endif
-
-  player->is_dropping_pressed = TRUE;
 
   /* do not drop an element on top of another element; when holding drop key
      pressed without moving, dropped element must move away before the next
@@ -15944,6 +14018,9 @@ static boolean DropElement(struct PlayerInfo *player)
   if (new_element == EL_UNDEFINED)
     return FALSE;
 
+  /* only set if player has anything that can be dropped */
+  player->is_dropping_pressed = TRUE;
+
   /* check if drop key was pressed long enough for EM style dynamite */
   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
     return FALSE;
@@ -16019,28 +14096,13 @@ static boolean DropElement(struct PlayerInfo *player)
   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
   {
-#if 0
-    int move_direction;
-    int nextx, nexty;
-#endif
-
     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
       MovDir[dropx][dropy] = drop_direction;
 
-#if 0
-    move_direction = MovDir[dropx][dropy];
-    nextx = dropx + GET_DX_FROM_DIR(move_direction);
-    nexty = dropy + GET_DY_FROM_DIR(move_direction);
-#endif
-
     ChangeCount[dropx][dropy] = 0;     /* allow at least one more change */
 
-#if USE_FIX_IMPACT_COLLISION
     /* do not cause impact style collision by dropping elements that can fall */
     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
-#else
-    CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
-#endif
   }
 
   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
@@ -16341,10 +14403,6 @@ void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
   int x = xx - offset;
   int y = yy - offset;
 
-#if 0
-  printf("::: %d -> %d\n", element_sp, action_sp);
-#endif
-
   PlayLevelSoundElementAction(x, y, element, action);
 }
 
@@ -16352,13 +14410,9 @@ void RaiseScore(int value)
 {
   local_player->score += value;
 
-#if 1
   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
 
   DisplayGameControlValues();
-#else
-  DrawGameValue_Score(local_player->score);
-#endif
 }
 
 void RaiseScoreElement(int element)
@@ -16445,6 +14499,10 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
 {
   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
   {
+    /* closing door required in case of envelope style request dialogs */
+    if (!skip_request)
+      CloseDoor(DOOR_CLOSE_1);
+
 #if defined(NETWORK_AVALIABLE)
     if (options.network)
       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
@@ -16452,37 +14510,11 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
 #endif
     {
       if (quick_quit)
-      {
-#if 1
-
-#if 1
        FadeSkipNextFadeIn();
-#else
-       fading = fading_none;
-#endif
 
-#else
-       OpenDoor(DOOR_CLOSE_1);
-#endif
-
-       game_status = GAME_MODE_MAIN;
-
-#if 1
-       DrawAndFadeInMainMenu(REDRAW_FIELD);
-#else
-       DrawMainMenu();
-#endif
-      }
-      else
-      {
-#if 0
-       FadeOut(REDRAW_FIELD);
-#endif
+      SetGameStatus(GAME_MODE_MAIN);
 
-       game_status = GAME_MODE_MAIN;
-
-       DrawAndFadeInMainMenu(REDRAW_FIELD);
-      }
+      DrawMainMenu();
     }
   }
   else         /* continue playing the game */
@@ -16515,18 +14547,7 @@ unsigned int InitEngineRandom_RND(int seed)
 {
   game.num_random_calls = 0;
 
-#if 0
-  unsigned int rnd_seed = InitEngineRandom(seed);
-
-  printf("::: START RND: %d\n", rnd_seed);
-
-  return rnd_seed;
-#else
-
   return InitEngineRandom(seed);
-
-#endif
-
 }
 
 unsigned int RND(int max)
@@ -16648,97 +14669,106 @@ static void LoadEngineSnapshotValues_RND()
   }
 }
 
-void SaveEngineSnapshot()
+void FreeEngineSnapshotSingle()
 {
-  /* do not save snapshots from editor */
-  if (level_editor_test_game)
-    return;
+  FreeSnapshotSingle();
 
-  /* free previous snapshot buffers, if needed */
-  FreeEngineSnapshotBuffers();
+  setString(&snapshot_level_identifier, NULL);
+  snapshot_level_nr = -1;
+}
+
+void FreeEngineSnapshotList()
+{
+  FreeSnapshotList();
+}
+
+ListNode *SaveEngineSnapshotBuffers()
+{
+  ListNode *buffers = NULL;
 
   /* copy some special values to a structure better suited for the snapshot */
 
-  SaveEngineSnapshotValues_RND();
-  SaveEngineSnapshotValues_EM();
-  SaveEngineSnapshotValues_SP();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    SaveEngineSnapshotValues_RND();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    SaveEngineSnapshotValues_EM();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    SaveEngineSnapshotValues_SP(&buffers);
 
   /* save values stored in special snapshot structure */
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
 
   /* save further RND engine values */
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
-
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
-
-  /* save level identification information */
-
-  setString(&snapshot_level_identifier, leveldir_current->identifier);
-  snapshot_level_nr = level_nr;
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
 
 #if 0
   ListNode *node = engine_snapshot_list_rnd;
@@ -16753,69 +14783,155 @@ void SaveEngineSnapshot()
 
   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
 #endif
+
+  return buffers;
+}
+
+void SaveEngineSnapshotSingle()
+{
+  ListNode *buffers = SaveEngineSnapshotBuffers();
+
+  /* finally save all snapshot buffers to single snapshot */
+  SaveSnapshotSingle(buffers);
+
+  /* save level identification information */
+  setString(&snapshot_level_identifier, leveldir_current->identifier);
+  snapshot_level_nr = level_nr;
 }
 
-void LoadEngineSnapshot()
+boolean CheckSaveEngineSnapshotToList()
 {
-  /* restore generically stored snapshot buffers */
+  boolean save_snapshot =
+    ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
+     (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
+      game.snapshot.changed_action) ||
+     (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
+      game.snapshot.collected_item));
 
-  LoadEngineSnapshotBuffers();
+  game.snapshot.changed_action = FALSE;
+  game.snapshot.collected_item = FALSE;
+  game.snapshot.save_snapshot = save_snapshot;
 
+  return save_snapshot;
+}
+
+void SaveEngineSnapshotToList()
+{
+  if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
+      tape.quick_resume)
+    return;
+
+  ListNode *buffers = SaveEngineSnapshotBuffers();
+
+  /* finally save all snapshot buffers to snapshot list */
+  SaveSnapshotToList(buffers);
+}
+
+void SaveEngineSnapshotToListInitial()
+{
+  FreeEngineSnapshotList();
+
+  SaveEngineSnapshotToList();
+}
+
+void LoadEngineSnapshotValues()
+{
   /* restore special values from snapshot structure */
 
-  LoadEngineSnapshotValues_RND();
-  LoadEngineSnapshotValues_EM();
-  LoadEngineSnapshotValues_SP();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    LoadEngineSnapshotValues_RND();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    LoadEngineSnapshotValues_EM();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    LoadEngineSnapshotValues_SP();
+}
+
+void LoadEngineSnapshotSingle()
+{
+  LoadSnapshotSingle();
+
+  LoadEngineSnapshotValues();
+}
+
+void LoadEngineSnapshot_Undo(int steps)
+{
+  LoadSnapshotFromList_Older(steps);
+
+  LoadEngineSnapshotValues();
+}
+
+void LoadEngineSnapshot_Redo(int steps)
+{
+  LoadSnapshotFromList_Newer(steps);
+
+  LoadEngineSnapshotValues();
 }
 
-boolean CheckEngineSnapshot()
+boolean CheckEngineSnapshotSingle()
 {
   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
          snapshot_level_nr == level_nr);
 }
 
+boolean CheckEngineSnapshotList()
+{
+  return CheckSnapshotList();
+}
+
 
 /* ---------- new game button stuff ---------------------------------------- */
 
 static struct
 {
   int graphic;
-  struct Rect *pos;
+  struct XY *pos;
   int gadget_id;
   char *infotext;
 } gamebutton_info[NUM_GAME_BUTTONS] =
 {
   {
-    IMG_GAME_BUTTON_GFX_STOP,          &game.button.stop,
+    IMG_GFX_GAME_BUTTON_STOP,          &game.button.stop,
     GAME_CTRL_ID_STOP,                 "stop game"
   },
   {
-    IMG_GAME_BUTTON_GFX_PAUSE,         &game.button.pause,
+    IMG_GFX_GAME_BUTTON_PAUSE,         &game.button.pause,
     GAME_CTRL_ID_PAUSE,                        "pause game"
   },
   {
-    IMG_GAME_BUTTON_GFX_PLAY,          &game.button.play,
+    IMG_GFX_GAME_BUTTON_PLAY,          &game.button.play,
     GAME_CTRL_ID_PLAY,                 "play game"
   },
   {
-    IMG_GAME_BUTTON_GFX_SOUND_MUSIC,   &game.button.sound_music,
-    SOUND_CTRL_ID_MUSIC,               "background music on/off"
+    IMG_GFX_GAME_BUTTON_UNDO,          &game.button.undo,
+    GAME_CTRL_ID_UNDO,                 "undo step"
   },
   {
-    IMG_GAME_BUTTON_GFX_SOUND_LOOPS,   &game.button.sound_loops,
-    SOUND_CTRL_ID_LOOPS,               "sound loops on/off"
+    IMG_GFX_GAME_BUTTON_REDO,          &game.button.redo,
+    GAME_CTRL_ID_REDO,                 "redo step"
   },
   {
-    IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,  &game.button.sound_simple,
-    SOUND_CTRL_ID_SIMPLE,              "normal sounds on/off"
+    IMG_GFX_GAME_BUTTON_SAVE,          &game.button.save,
+    GAME_CTRL_ID_SAVE,                 "save game"
   },
   {
-    IMG_GAME_BUTTON_GFX_SAVE,          &game.button.save,
-    GAME_CTRL_ID_SAVE,                 "save game"
+    IMG_GFX_GAME_BUTTON_PAUSE2,                &game.button.pause2,
+    GAME_CTRL_ID_PAUSE2,               "pause game"
   },
   {
-    IMG_GAME_BUTTON_GFX_LOAD,          &game.button.load,
+    IMG_GFX_GAME_BUTTON_LOAD,          &game.button.load,
     GAME_CTRL_ID_LOAD,                 "load game"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_SOUND_MUSIC,   &game.button.sound_music,
+    SOUND_CTRL_ID_MUSIC,               "background music on/off"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_SOUND_LOOPS,   &game.button.sound_loops,
+    SOUND_CTRL_ID_LOOPS,               "sound loops on/off"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,  &game.button.sound_simple,
+    SOUND_CTRL_ID_SIMPLE,              "normal sounds on/off"
   }
 };
 
@@ -16826,11 +14942,13 @@ void CreateGameButtons()
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
   {
     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
-    struct Rect *pos = gamebutton_info[i].pos;
+    struct XY *pos = gamebutton_info[i].pos;
     struct GadgetInfo *gi;
     int button_type;
     boolean checked;
     unsigned int event_mask;
+    int base_x = (tape.show_game_buttons ? VX : DX);
+    int base_y = (tape.show_game_buttons ? VY : DY);
     int gd_x   = gfx->src_x;
     int gd_y   = gfx->src_y;
     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
@@ -16849,7 +14967,6 @@ void CreateGameButtons()
     }
 
     if (id == GAME_CTRL_ID_STOP ||
-       id == GAME_CTRL_ID_PAUSE ||
        id == GAME_CTRL_ID_PLAY ||
        id == GAME_CTRL_ID_SAVE ||
        id == GAME_CTRL_ID_LOAD)
@@ -16858,6 +14975,13 @@ void CreateGameButtons()
       checked = FALSE;
       event_mask = GD_EVENT_RELEASED;
     }
+    else if (id == GAME_CTRL_ID_UNDO ||
+            id == GAME_CTRL_ID_REDO)
+    {
+      button_type = GD_TYPE_NORMAL_BUTTON;
+      checked = FALSE;
+      event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
+    }
     else
     {
       button_type = GD_TYPE_CHECK_BUTTON;
@@ -16870,8 +14994,8 @@ void CreateGameButtons()
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_INFO_TEXT, gamebutton_info[i].infotext,
-                     GDI_X, DX + GDI_ACTIVE_POS(pos->x),
-                     GDI_Y, DY + GDI_ACTIVE_POS(pos->y),
+                     GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
+                     GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
                      GDI_WIDTH, gfx->width,
                      GDI_HEIGHT, gfx->height,
                      GDI_TYPE, button_type,
@@ -16901,12 +15025,80 @@ void FreeGameButtons()
     FreeGadget(game_gadget[i]);
 }
 
+static void UnmapGameButtonsAtSamePosition(int id)
+{
+  int i;
+
+  for (i = 0; i < NUM_GAME_BUTTONS; i++)
+    if (i != id &&
+       gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
+       gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
+      UnmapGadget(game_gadget[i]);
+}
+
+static void UnmapGameButtonsAtSamePosition_All()
+{
+  if (setup.show_snapshot_buttons)
+  {
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
+  }
+  else
+  {
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
+  }
+}
+
+static void MapGameButtonsAtSamePosition(int id)
+{
+  int i;
+
+  for (i = 0; i < NUM_GAME_BUTTONS; i++)
+    if (i != id &&
+       gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
+       gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
+      MapGadget(game_gadget[i]);
+
+  UnmapGameButtonsAtSamePosition_All();
+}
+
+void MapUndoRedoButtons()
+{
+  UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
+  UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
+
+  MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
+  MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+
+  ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
+}
+
+void UnmapUndoRedoButtons()
+{
+  UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
+  UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+
+  MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
+  MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
+
+  ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
+}
+
 void MapGameButtons()
 {
   int i;
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
-    MapGadget(game_gadget[i]);
+    if (i != GAME_CTRL_ID_UNDO &&
+       i != GAME_CTRL_ID_REDO)
+      MapGadget(game_gadget[i]);
+
+  UnmapGameButtonsAtSamePosition_All();
+
+  RedrawGameButtons();
 }
 
 void UnmapGameButtons()
@@ -16923,24 +15115,75 @@ void RedrawGameButtons()
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
     RedrawGadget(game_gadget[i]);
+
+  // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
+  redraw_mask &= ~REDRAW_ALL;
+}
+
+void GameUndoRedoExt()
+{
+  ClearPlayerAction();
+
+  tape.pausing = TRUE;
+
+  RedrawPlayfield();
+  UpdateAndDisplayGameControlValues();
+
+  DrawCompleteVideoDisplay();
+  DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
+  DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
+  DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
+
+  BackToFront();
+}
+
+void GameUndo(int steps)
+{
+  if (!CheckEngineSnapshotList())
+    return;
+
+  LoadEngineSnapshot_Undo(steps);
+
+  GameUndoRedoExt();
+}
+
+void GameRedo(int steps)
+{
+  if (!CheckEngineSnapshotList())
+    return;
+
+  LoadEngineSnapshot_Redo(steps);
+
+  GameUndoRedoExt();
 }
 
-static void HandleGameButtonsExt(int id)
+static void HandleGameButtonsExt(int id, int button)
 {
-  if (game_status != GAME_MODE_PLAYING)
+  static boolean game_undo_executed = FALSE;
+  int steps = BUTTON_STEPSIZE(button);
+  boolean handle_game_buttons =
+    (game_status == GAME_MODE_PLAYING ||
+     (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
+
+  if (!handle_game_buttons)
     return;
 
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
+      if (game_status == GAME_MODE_MAIN)
+       break;
+
       if (tape.playing)
        TapeStop();
       else
        RequestQuitGame(TRUE);
+
       break;
 
     case GAME_CTRL_ID_PAUSE:
-      if (options.network)
+    case GAME_CTRL_ID_PAUSE2:
+      if (options.network && game_status == GAME_MODE_PLAYING)
       {
 #if defined(NETWORK_AVALIABLE)
        if (tape.pausing)
@@ -16951,23 +15194,54 @@ static void HandleGameButtonsExt(int id)
       }
       else
        TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
+      game_undo_executed = FALSE;
+
       break;
 
     case GAME_CTRL_ID_PLAY:
-      if (tape.pausing)
+      if (game_status == GAME_MODE_MAIN)
+      {
+        StartGameActions(options.network, setup.autorecord, level.random_seed);
+      }
+      else if (tape.pausing)
       {
 #if defined(NETWORK_AVALIABLE)
        if (options.network)
          SendToServer_ContinuePlaying();
        else
 #endif
-       {
-         tape.pausing = FALSE;
-         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
-       }
+         TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
       }
       break;
 
+    case GAME_CTRL_ID_UNDO:
+      // Important: When using "save snapshot when collecting an item" mode,
+      // load last (current) snapshot for first "undo" after pressing "pause"
+      // (else the last-but-one snapshot would be loaded, because the snapshot
+      // pointer already points to the last snapshot when pressing "pause",
+      // which is fine for "every step/move" mode, but not for "every collect")
+      if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
+         !game_undo_executed)
+       steps--;
+
+      game_undo_executed = TRUE;
+
+      GameUndo(steps);
+      break;
+
+    case GAME_CTRL_ID_REDO:
+      GameRedo(steps);
+      break;
+
+    case GAME_CTRL_ID_SAVE:
+      TapeQuickSave();
+      break;
+
+    case GAME_CTRL_ID_LOAD:
+      TapeQuickLoad();
+      break;
+
     case SOUND_CTRL_ID_MUSIC:
       if (setup.sound_music)
       { 
@@ -17007,14 +15281,6 @@ static void HandleGameButtonsExt(int id)
       }
       break;
 
-    case GAME_CTRL_ID_SAVE:
-      TapeQuickSave();
-      break;
-
-    case GAME_CTRL_ID_LOAD:
-      TapeQuickLoad();
-      break;
-
     default:
       break;
   }
@@ -17022,24 +15288,16 @@ static void HandleGameButtonsExt(int id)
 
 static void HandleGameButtons(struct GadgetInfo *gi)
 {
-  HandleGameButtonsExt(gi->custom_id);
+  HandleGameButtonsExt(gi->custom_id, gi->event.button);
 }
 
 void HandleSoundButtonKeys(Key key)
 {
-#if 1
+
   if (key == setup.shortcut.sound_simple)
     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
   else if (key == setup.shortcut.sound_loops)
     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
   else if (key == setup.shortcut.sound_music)
     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
-#else
-  if (key == setup.shortcut.sound_simple)
-    HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
-  else if (key == setup.shortcut.sound_loops)
-    HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
-  else if (key == setup.shortcut.sound_music)
-    HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
-#endif
 }