added creating engine snapshots when using mouse click events
[rocksndiamonds.git] / src / game.c
index 441793cef025f261b769d8f6dab212a4b23825dc..8b77ddef674f65d232e0c968c5e4c5ef9529870f 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
+//                 https://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"
 
-/* EXPERIMENTAL STUFF */
-#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)
+// DEBUG SETTINGS
+#define DEBUG_INIT_PLAYER      1
+#define DEBUG_PLAYER_ACTIONS   0
 
-#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_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
+// EXPERIMENTAL STUFF
+#define USE_NEW_AMOEBA_CODE    FALSE
 
-#define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 1)
+// EXPERIMENTAL STUFF
+#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)                              \
        GfxRedraw[x][y] |= GFX_REDRAW_TILE
-#define TEST_DrawLevelFieldCrumbledSand(x, y)                  \
+#define TEST_DrawLevelFieldCrumbled(x, y)                      \
        GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
-#define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)                \
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y)            \
        GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
 #define TEST_DrawTwinkleOnField(x, y)                          \
        GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
 #else
 #define TEST_DrawLevelField(x, y)                              \
             DrawLevelField(x, y)
-#define TEST_DrawLevelFieldCrumbledSand(x, y)                  \
-            DrawLevelFieldCrumbledSand(x, y)
-#define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)                \
-            DrawLevelFieldCrumbledSandNeighbours(x, y)
+#define TEST_DrawLevelFieldCrumbled(x, y)                      \
+            DrawLevelFieldCrumbled(x, y)
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y)            \
+            DrawLevelFieldCrumbledNeighbours(x, y)
 #define TEST_DrawTwinkleOnField(x, y)                          \
             DrawTwinkleOnField(x, y)
 #endif
 
 
-/* for DigField() */
+// for DigField()
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
 #define DF_SNAP                        2
 
-/* for MovePlayer() */
+// for MovePlayer()
 #define MP_NO_ACTION           0
 #define MP_MOVING              1
 #define MP_ACTION              2
 #define MP_DONT_RUN_INTO       (MP_MOVING | MP_ACTION)
 
-/* for ScrollPlayer() */
+// for ScrollPlayer()
 #define SCROLL_INIT            0
 #define SCROLL_GO_ON           1
 
-/* for Bang()/Explode() */
+// for Bang()/Explode()
 #define EX_PHASE_START         0
 #define EX_TYPE_NONE           0
 #define EX_TYPE_NORMAL         (1 << 0)
 #define EX_TYPE_DYNA           (1 << 4)
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
-#define PANEL_OFF()            (local_player->LevelSolved_PanelOff)
+#define PANEL_OFF()            (game.panel.active == FALSE)
 #define        PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
 #define PANEL_XPOS(p)          (DX + ALIGNED_TEXT_XPOS(p))
 #define PANEL_YPOS(p)          (DY + ALIGNED_TEXT_YPOS(p))
 
-/* special positions in the game control window (relative to control window) */
-#define XX_LEVEL1              (PANEL_XPOS(game.panel.level))
-#define XX_LEVEL2              (PANEL_XPOS(game.panel.level) - 1)
-#define XX_LEVEL               (PANEL_XPOS(game.panel.level))
-#define YY_LEVEL               (PANEL_YPOS(game.panel.level))
-#define XX_EMERALDS            (PANEL_XPOS(game.panel.gems))
-#define YY_EMERALDS            (PANEL_YPOS(game.panel.gems))
-#define XX_DYNAMITE            (PANEL_XPOS(game.panel.inventory))
-#define YY_DYNAMITE            (PANEL_YPOS(game.panel.inventory))
-#define XX_KEYS                        (PANEL_XPOS(game.panel.keys))
-#define YY_KEYS                        (PANEL_YPOS(game.panel.keys))
-#define XX_SCORE               (PANEL_XPOS(game.panel.score))
-#define YY_SCORE               (PANEL_YPOS(game.panel.score))
-#define XX_TIME1               (PANEL_XPOS(game.panel.time))
-#define XX_TIME2               (PANEL_XPOS(game.panel.time) + 1)
-#define XX_TIME                        (PANEL_XPOS(game.panel.time))
-#define YY_TIME                        (PANEL_YPOS(game.panel.time))
-
-/* special positions in the game control window (relative to main window) */
-#define DX_LEVEL1              (DX + XX_LEVEL1)
-#define DX_LEVEL2              (DX + XX_LEVEL2)
-#define DX_LEVEL               (DX + XX_LEVEL)
-#define DY_LEVEL               (DY + YY_LEVEL)
-#define DX_EMERALDS            (DX + XX_EMERALDS)
-#define DY_EMERALDS            (DY + YY_EMERALDS)
-#define DX_DYNAMITE            (DX + XX_DYNAMITE)
-#define DY_DYNAMITE            (DY + YY_DYNAMITE)
-#define DX_KEYS                        (DX + XX_KEYS)
-#define DY_KEYS                        (DY + YY_KEYS)
-#define DX_SCORE               (DX + XX_SCORE)
-#define DY_SCORE               (DY + YY_SCORE)
-#define DX_TIME1               (DX + XX_TIME1)
-#define DX_TIME2               (DX + XX_TIME2)
-#define DX_TIME                        (DX + XX_TIME)
-#define DY_TIME                        (DY + YY_TIME)
-
-#if 1
-/* game panel display and control definitions */
-
+// game panel display and control definitions
 #define GAME_PANEL_LEVEL_NUMBER                        0
 #define GAME_PANEL_GEMS                                1
 #define GAME_PANEL_INVENTORY_COUNT             2
 #define GAME_PANEL_TIME_HH                     32
 #define GAME_PANEL_TIME_MM                     33
 #define GAME_PANEL_TIME_SS                     34
-#define GAME_PANEL_SHIELD_NORMAL               35
-#define GAME_PANEL_SHIELD_NORMAL_TIME          36
-#define GAME_PANEL_SHIELD_DEADLY               37
-#define GAME_PANEL_SHIELD_DEADLY_TIME          38
-#define GAME_PANEL_EXIT                                39
-#define GAME_PANEL_EMC_MAGIC_BALL              40
-#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       41
-#define GAME_PANEL_LIGHT_SWITCH                        42
-#define GAME_PANEL_LIGHT_SWITCH_TIME           43
-#define GAME_PANEL_TIMEGATE_SWITCH             44
-#define GAME_PANEL_TIMEGATE_SWITCH_TIME                45
-#define GAME_PANEL_SWITCHGATE_SWITCH           46
-#define GAME_PANEL_EMC_LENSES                  47
-#define GAME_PANEL_EMC_LENSES_TIME             48
-#define GAME_PANEL_EMC_MAGNIFIER               49
-#define GAME_PANEL_EMC_MAGNIFIER_TIME          50
-#define GAME_PANEL_BALLOON_SWITCH              51
-#define GAME_PANEL_DYNABOMB_NUMBER             52
-#define GAME_PANEL_DYNABOMB_SIZE               53
-#define GAME_PANEL_DYNABOMB_POWER              54
-#define GAME_PANEL_PENGUINS                    55
-#define GAME_PANEL_SOKOBAN_OBJECTS             56
-#define GAME_PANEL_SOKOBAN_FIELDS              57
-#define GAME_PANEL_ROBOT_WHEEL                 58
-#define GAME_PANEL_CONVEYOR_BELT_1             59
-#define GAME_PANEL_CONVEYOR_BELT_2             60
-#define GAME_PANEL_CONVEYOR_BELT_3             61
-#define GAME_PANEL_CONVEYOR_BELT_4             62
-#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      63
-#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      64
-#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      65
-#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      66
-#define GAME_PANEL_MAGIC_WALL                  67
-#define GAME_PANEL_MAGIC_WALL_TIME             68
-#define GAME_PANEL_GRAVITY_STATE               69
-#define GAME_PANEL_GRAPHIC_1                   70
-#define GAME_PANEL_GRAPHIC_2                   71
-#define GAME_PANEL_GRAPHIC_3                   72
-#define GAME_PANEL_GRAPHIC_4                   73
-#define GAME_PANEL_GRAPHIC_5                   74
-#define GAME_PANEL_GRAPHIC_6                   75
-#define GAME_PANEL_GRAPHIC_7                   76
-#define GAME_PANEL_GRAPHIC_8                   77
-#define GAME_PANEL_ELEMENT_1                   78
-#define GAME_PANEL_ELEMENT_2                   79
-#define GAME_PANEL_ELEMENT_3                   80
-#define GAME_PANEL_ELEMENT_4                   81
-#define GAME_PANEL_ELEMENT_5                   82
-#define GAME_PANEL_ELEMENT_6                   83
-#define GAME_PANEL_ELEMENT_7                   84
-#define GAME_PANEL_ELEMENT_8                   85
-#define GAME_PANEL_ELEMENT_COUNT_1             86
-#define GAME_PANEL_ELEMENT_COUNT_2             87
-#define GAME_PANEL_ELEMENT_COUNT_3             88
-#define GAME_PANEL_ELEMENT_COUNT_4             89
-#define GAME_PANEL_ELEMENT_COUNT_5             90
-#define GAME_PANEL_ELEMENT_COUNT_6             91
-#define GAME_PANEL_ELEMENT_COUNT_7             92
-#define GAME_PANEL_ELEMENT_COUNT_8             93
-#define GAME_PANEL_CE_SCORE_1                  94
-#define GAME_PANEL_CE_SCORE_2                  95
-#define GAME_PANEL_CE_SCORE_3                  96
-#define GAME_PANEL_CE_SCORE_4                  97
-#define GAME_PANEL_CE_SCORE_5                  98
-#define GAME_PANEL_CE_SCORE_6                  99
-#define GAME_PANEL_CE_SCORE_7                  100
-#define GAME_PANEL_CE_SCORE_8                  101
-#define GAME_PANEL_CE_SCORE_1_ELEMENT          102
-#define GAME_PANEL_CE_SCORE_2_ELEMENT          103
-#define GAME_PANEL_CE_SCORE_3_ELEMENT          104
-#define GAME_PANEL_CE_SCORE_4_ELEMENT          105
-#define GAME_PANEL_CE_SCORE_5_ELEMENT          106
-#define GAME_PANEL_CE_SCORE_6_ELEMENT          107
-#define GAME_PANEL_CE_SCORE_7_ELEMENT          108
-#define GAME_PANEL_CE_SCORE_8_ELEMENT          109
-#define GAME_PANEL_PLAYER_NAME                 110
-#define GAME_PANEL_LEVEL_NAME                  111
-#define GAME_PANEL_LEVEL_AUTHOR                        112
-
-#define NUM_GAME_PANEL_CONTROLS                        113
+#define GAME_PANEL_TIME_ANIM                   35
+#define GAME_PANEL_HEALTH                      36
+#define GAME_PANEL_HEALTH_ANIM                 37
+#define GAME_PANEL_FRAME                       38
+#define GAME_PANEL_SHIELD_NORMAL               39
+#define GAME_PANEL_SHIELD_NORMAL_TIME          40
+#define GAME_PANEL_SHIELD_DEADLY               41
+#define GAME_PANEL_SHIELD_DEADLY_TIME          42
+#define GAME_PANEL_EXIT                                43
+#define GAME_PANEL_EMC_MAGIC_BALL              44
+#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       45
+#define GAME_PANEL_LIGHT_SWITCH                        46
+#define GAME_PANEL_LIGHT_SWITCH_TIME           47
+#define GAME_PANEL_TIMEGATE_SWITCH             48
+#define GAME_PANEL_TIMEGATE_SWITCH_TIME                49
+#define GAME_PANEL_SWITCHGATE_SWITCH           50
+#define GAME_PANEL_EMC_LENSES                  51
+#define GAME_PANEL_EMC_LENSES_TIME             52
+#define GAME_PANEL_EMC_MAGNIFIER               53
+#define GAME_PANEL_EMC_MAGNIFIER_TIME          54
+#define GAME_PANEL_BALLOON_SWITCH              55
+#define GAME_PANEL_DYNABOMB_NUMBER             56
+#define GAME_PANEL_DYNABOMB_SIZE               57
+#define GAME_PANEL_DYNABOMB_POWER              58
+#define GAME_PANEL_PENGUINS                    59
+#define GAME_PANEL_SOKOBAN_OBJECTS             60
+#define GAME_PANEL_SOKOBAN_FIELDS              61
+#define GAME_PANEL_ROBOT_WHEEL                 62
+#define GAME_PANEL_CONVEYOR_BELT_1             63
+#define GAME_PANEL_CONVEYOR_BELT_2             64
+#define GAME_PANEL_CONVEYOR_BELT_3             65
+#define GAME_PANEL_CONVEYOR_BELT_4             66
+#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      67
+#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      68
+#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      69
+#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      70
+#define GAME_PANEL_MAGIC_WALL                  71
+#define GAME_PANEL_MAGIC_WALL_TIME             72
+#define GAME_PANEL_GRAVITY_STATE               73
+#define GAME_PANEL_GRAPHIC_1                   74
+#define GAME_PANEL_GRAPHIC_2                   75
+#define GAME_PANEL_GRAPHIC_3                   76
+#define GAME_PANEL_GRAPHIC_4                   77
+#define GAME_PANEL_GRAPHIC_5                   78
+#define GAME_PANEL_GRAPHIC_6                   79
+#define GAME_PANEL_GRAPHIC_7                   80
+#define GAME_PANEL_GRAPHIC_8                   81
+#define GAME_PANEL_ELEMENT_1                   82
+#define GAME_PANEL_ELEMENT_2                   83
+#define GAME_PANEL_ELEMENT_3                   84
+#define GAME_PANEL_ELEMENT_4                   85
+#define GAME_PANEL_ELEMENT_5                   86
+#define GAME_PANEL_ELEMENT_6                   87
+#define GAME_PANEL_ELEMENT_7                   88
+#define GAME_PANEL_ELEMENT_8                   89
+#define GAME_PANEL_ELEMENT_COUNT_1             90
+#define GAME_PANEL_ELEMENT_COUNT_2             91
+#define GAME_PANEL_ELEMENT_COUNT_3             92
+#define GAME_PANEL_ELEMENT_COUNT_4             93
+#define GAME_PANEL_ELEMENT_COUNT_5             94
+#define GAME_PANEL_ELEMENT_COUNT_6             95
+#define GAME_PANEL_ELEMENT_COUNT_7             96
+#define GAME_PANEL_ELEMENT_COUNT_8             97
+#define GAME_PANEL_CE_SCORE_1                  98
+#define GAME_PANEL_CE_SCORE_2                  99
+#define GAME_PANEL_CE_SCORE_3                  100
+#define GAME_PANEL_CE_SCORE_4                  101
+#define GAME_PANEL_CE_SCORE_5                  102
+#define GAME_PANEL_CE_SCORE_6                  103
+#define GAME_PANEL_CE_SCORE_7                  104
+#define GAME_PANEL_CE_SCORE_8                  105
+#define GAME_PANEL_CE_SCORE_1_ELEMENT          106
+#define GAME_PANEL_CE_SCORE_2_ELEMENT          107
+#define GAME_PANEL_CE_SCORE_3_ELEMENT          108
+#define GAME_PANEL_CE_SCORE_4_ELEMENT          109
+#define GAME_PANEL_CE_SCORE_5_ELEMENT          110
+#define GAME_PANEL_CE_SCORE_6_ELEMENT          111
+#define GAME_PANEL_CE_SCORE_7_ELEMENT          112
+#define GAME_PANEL_CE_SCORE_8_ELEMENT          113
+#define GAME_PANEL_PLAYER_NAME                 114
+#define GAME_PANEL_LEVEL_NAME                  115
+#define GAME_PANEL_LEVEL_AUTHOR                        116
+
+#define NUM_GAME_PANEL_CONTROLS                        117
 
 struct GamePanelOrderInfo
 {
@@ -284,6 +222,8 @@ struct GamePanelControlInfo
   struct TextPosInfo *pos;
   int type;
 
+  int graphic, graphic_active;
+
   int value, last_value;
   int frame, last_frame;
   int gfx_frame;
@@ -467,6 +407,32 @@ static struct GamePanelControlInfo game_panel_controls[] =
     &game.panel.time_ss,
     TYPE_INTEGER,
   },
+  {
+    GAME_PANEL_TIME_ANIM,
+    &game.panel.time_anim,
+    TYPE_GRAPHIC,
+
+    IMG_GFX_GAME_PANEL_TIME_ANIM,
+    IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
+  },
+  {
+    GAME_PANEL_HEALTH,
+    &game.panel.health,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_HEALTH_ANIM,
+    &game.panel.health_anim,
+    TYPE_GRAPHIC,
+
+    IMG_GFX_GAME_PANEL_HEALTH_ANIM,
+    IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
+  },
+  {
+    GAME_PANEL_FRAME,
+    &game.panel.frame,
+    TYPE_INTEGER,
+  },
   {
     GAME_PANEL_SHIELD_NORMAL,
     &game.panel.shield_normal,
@@ -864,20 +830,18 @@ static struct GamePanelControlInfo game_panel_controls[] =
     -1,
   }
 };
-#endif
-
 
-/* values for delayed check of falling and moving elements and for collision */
+// values for delayed check of falling and moving elements and for collision
 #define CHECK_DELAY_MOVING     3
 #define CHECK_DELAY_FALLING    CHECK_DELAY_MOVING
 #define CHECK_DELAY_COLLISION  2
 #define CHECK_DELAY_IMPACT     CHECK_DELAY_COLLISION
 
-/* values for initial player move delay (initial delay counter value) */
+// values for initial player move delay (initial delay counter value)
 #define INITIAL_MOVE_DELAY_OFF -1
 #define INITIAL_MOVE_DELAY_ON  0
 
-/* values for player movement speed (which is in fact a delay value) */
+// values for player movement speed (which is in fact a delay value)
 #define MOVE_DELAY_MIN_SPEED   32
 #define MOVE_DELAY_NORMAL_SPEED        8
 #define MOVE_DELAY_HIGH_SPEED  4
@@ -889,7 +853,15 @@ 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 other actions */
+// 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)
 #define MOVE_STEPSIZE_MAX      (TILEX)
@@ -948,20 +920,20 @@ static struct GamePanelControlInfo game_panel_controls[] =
 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)             \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
                                        (CAN_MOVE_INTO_ACID(e) &&       \
-                                        Feld[x][y] == EL_ACID) ||      \
+                                        Tile[x][y] == EL_ACID) ||      \
                                        (condition)))
 
 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)             \
                (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
                                        (CAN_MOVE_INTO_ACID(e) &&       \
-                                        Feld[x][y] == EL_ACID) ||      \
+                                        Tile[x][y] == EL_ACID) ||      \
                                        (condition)))
 
 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)             \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
                                        (condition) ||                  \
                                        (CAN_MOVE_INTO_ACID(e) &&       \
-                                        Feld[x][y] == EL_ACID) ||      \
+                                        Tile[x][y] == EL_ACID) ||      \
                                        (DONT_COLLIDE_WITH(e) &&        \
                                         IS_PLAYER(x, y) &&             \
                                         !PLAYER_ENEMY_PROTECTED(x, y))))
@@ -973,33 +945,33 @@ static struct GamePanelControlInfo game_panel_controls[] =
        ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
 
 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                               \
-       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
+       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
 
 #define ANDROID_CAN_CLONE_FIELD(x, y)                                  \
-       (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
+       (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
                                CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
 
 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                 \
        ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
 
 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                        \
-       ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
+       ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
 
 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                           \
-       ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
+       ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
 
 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                        \
-       ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
+       ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
 
 #define PIG_CAN_ENTER_FIELD(e, x, y)                                   \
-       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
+       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
 
 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                               \
-       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
-                                                Feld[x][y] == EL_EM_EXIT_OPEN || \
-                                                Feld[x][y] == EL_STEEL_EXIT_OPEN || \
-                                                Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
-                                                IS_FOOD_PENGUIN(Feld[x][y])))
+       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
+                                                Tile[x][y] == EL_EM_EXIT_OPEN || \
+                                                Tile[x][y] == EL_STEEL_EXIT_OPEN || \
+                                                Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
+                                                IS_FOOD_PENGUIN(Tile[x][y])))
 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                        \
        ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
 
@@ -1010,14 +982,14 @@ static struct GamePanelControlInfo game_panel_controls[] =
        ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
 
 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                               \
-       (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
-                               Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
+       (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
+                               Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
 
 #define MOVE_ENTER_EL(e)       (element_info[e].move_enter_element)
 
 #define CE_ENTER_FIELD_COND(e, x, y)                                   \
                (!IS_PLAYER(x, y) &&                                    \
-                IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
+                IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
 
 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
        ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
@@ -1030,18 +1002,33 @@ static struct GamePanelControlInfo game_panel_controls[] =
 #define IS_PASSABLE_FROM(e, d)         (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
 #define IS_ACCESSIBLE_FROM(e, d)       (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
 
-/* game button identifiers */
+#define MM_HEALTH(x)           (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
+
+// game button identifiers
 #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 NUM_GAME_BUTTONS               6
-
-
-/* forward declaration for internal use */
+#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 GAME_CTRL_ID_PANEL_STOP                8
+#define GAME_CTRL_ID_PANEL_PAUSE       9
+#define GAME_CTRL_ID_PANEL_PLAY                10
+#define GAME_CTRL_ID_TOUCH_STOP                11
+#define GAME_CTRL_ID_TOUCH_PAUSE       12
+#define SOUND_CTRL_ID_MUSIC            13
+#define SOUND_CTRL_ID_LOOPS            14
+#define SOUND_CTRL_ID_SIMPLE           15
+#define SOUND_CTRL_ID_PANEL_MUSIC      16
+#define SOUND_CTRL_ID_PANEL_LOOPS      17
+#define SOUND_CTRL_ID_PANEL_SIMPLE     18
+
+#define NUM_GAME_BUTTONS               19
+
+
+// forward declaration for internal use
 
 static void CreateField(int, int, int);
 
@@ -1070,9 +1057,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);
@@ -1103,18 +1087,18 @@ static void PlayLevelSoundElementAction(int, int, int, int);
 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
 static void PlayLevelSoundActionIfLoop(int, int, int);
 static void StopLevelSoundActionIfLoop(int, int, int);
-static void PlayLevelMusic();
+static void PlayLevelMusic(void);
+static void FadeLevelSoundsAndMusic(void);
 
-static void MapGameButtons();
 static void HandleGameButtons(struct GadgetInfo *);
 
-int AmoebeNachbarNr(int, int);
-void AmoebeUmwandeln(int, int);
+int AmoebaNeighbourNr(int, int);
+void AmoebaToDiamond(int, int);
 void ContinueMoving(int, int);
 void Bang(int, int);
 void InitMovDir(int, int);
 void InitAmoebaNr(int, int);
-int NewHiScore(void);
+int NewHiScore(int);
 
 void TestIfGoodThingHitsBadThing(int, int, int);
 void TestIfBadThingHitsGoodThing(int, int, int);
@@ -1125,18 +1109,20 @@ void TestIfBadThingRunsIntoPlayer(int, int, int);
 void TestIfFriendTouchesBadThing(int, int);
 void TestIfBadThingTouchesFriend(int, int);
 void TestIfBadThingTouchesOtherBadThing(int, int);
+void TestIfGoodThingGetsHitByBadThing(int, int, int);
 
 void KillPlayer(struct PlayerInfo *);
 void BuryPlayer(struct PlayerInfo *);
 void RemovePlayer(struct PlayerInfo *);
+void ExitPlayer(struct PlayerInfo *);
 
 static int getInvisibleActiveFromInvisibleElement(int);
 static int getInvisibleFromInvisibleActiveElement(int);
 
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
-/* for detection of endless loops, caused by custom element programming */
-/* (using maximal playfield width x 10 is just a rough approximation) */
+// for detection of endless loops, caused by custom element programming
+// (using maximal playfield width x 10 is just a rough approximation)
 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH     (MAX_PLAYFIELD_WIDTH * 10)
 
 #define RECURSION_LOOP_DETECTION_START(e, rc)                          \
@@ -1162,13 +1148,15 @@ static int recursion_loop_depth;
 static boolean recursion_loop_detected;
 static boolean recursion_loop_element;
 
+static int map_player_action[MAX_PLAYERS];
+
 
-/* ------------------------------------------------------------------------- */
-/* definition of elements that automatically change to other elements after  */
-/* a specified time, eventually calling a function when changing             */
-/* ------------------------------------------------------------------------- */
+// ----------------------------------------------------------------------------
+// definition of elements that automatically change to other elements after
+// a specified time, eventually calling a function when changing
+// ----------------------------------------------------------------------------
 
-/* forward declaration for changer functions */
+// forward declaration for changer functions
 static void InitBuggyBase(int, int);
 static void WarnBuggyBase(int, int);
 
@@ -1256,11 +1244,7 @@ static struct ChangingElementInfo change_delay_list[] =
   },
   {
     EL_EM_EXIT_CLOSING,
-#if 1
     EL_EMPTY,
-#else
-    EL_EM_EXIT_CLOSED,
-#endif
     29,
     NULL,
     NULL,
@@ -1276,11 +1260,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,
@@ -1565,12 +1545,12 @@ static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
 
 #define IS_AUTO_CHANGING(e)    (element_info[e].has_change_event[CE_DELAY])
 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
-#define IS_CHANGING(x, y)      (IS_AUTO_CHANGING(Feld[x][y]) || \
+#define IS_CHANGING(x, y)      (IS_AUTO_CHANGING(Tile[x][y]) || \
                                 IS_JUST_CHANGING(x, y))
 
 #define CE_PAGE(e, ce)         (element_info[e].event_page[ce])
 
-/* static variables for playfield scan mode (scanning forward or backward) */
+// static variables for playfield scan mode (scanning forward or backward)
 static int playfield_scan_start_x = 0;
 static int playfield_scan_start_y = 0;
 static int playfield_scan_delta_x = 1;
@@ -1584,7 +1564,7 @@ static int playfield_scan_delta_y = 1;
                                     (x) += playfield_scan_delta_x)
 
 #ifdef DEBUG
-void DEBUG_SetMaximumDynamite()
+void DEBUG_SetMaximumDynamite(void)
 {
   int i;
 
@@ -1595,7 +1575,7 @@ void DEBUG_SetMaximumDynamite()
 }
 #endif
 
-static void InitPlayfieldScanModeVars()
+static void InitPlayfieldScanModeVars(void)
 {
   if (game.use_reverse_scan_direction)
   {
@@ -1628,7 +1608,7 @@ static int get_move_delay_from_stepsize(int move_stepsize)
   move_stepsize =
     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
 
-  /* make sure that stepsize value is always a power of 2 */
+  // make sure that stepsize value is always a power of 2
   move_stepsize = (1 << log_2(move_stepsize));
 
   return TILEX / move_stepsize;
@@ -1641,10 +1621,10 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
   int move_delay = get_move_delay_from_stepsize(move_stepsize);
   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
 
-  /* do no immediately change move delay -- the player might just be moving */
+  // do no immediately change move delay -- the player might just be moving
   player->move_delay_value_next = move_delay;
 
-  /* information if player can move must be set separately */
+  // information if player can move must be set separately
   player->cannot_move = cannot_move;
 
   if (init_game)
@@ -1658,7 +1638,7 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
   }
 }
 
-void GetPlayerConfig()
+void GetPlayerConfig(void)
 {
   GameFrameDelay = setup.game_frame_delay;
 
@@ -1677,7 +1657,6 @@ void GetPlayerConfig()
   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
 
   SetAudioMode(setup.sound);
-  InitJoysticks();
 }
 
 int GetElementFromGroupElement(int element)
@@ -1706,6 +1685,30 @@ int GetElementFromGroupElement(int element)
   return element;
 }
 
+static void IncrementSokobanFieldsNeeded(void)
+{
+  if (level.sb_fields_needed)
+    game.sokoban_fields_still_needed++;
+}
+
+static void IncrementSokobanObjectsNeeded(void)
+{
+  if (level.sb_objects_needed)
+    game.sokoban_objects_still_needed++;
+}
+
+static void DecrementSokobanFieldsNeeded(void)
+{
+  if (game.sokoban_fields_still_needed > 0)
+    game.sokoban_fields_still_needed--;
+}
+
+static void DecrementSokobanObjectsNeeded(void)
+{
+  if (game.sokoban_objects_still_needed > 0)
+    game.sokoban_objects_still_needed--;
+}
+
 static void InitPlayerField(int x, int y, int element, boolean init_game)
 {
   if (element == EL_SP_MURPHY)
@@ -1714,25 +1717,26 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
     {
       if (stored_player[0].present)
       {
-       Feld[x][y] = EL_SP_MURPHY_CLONE;
+       Tile[x][y] = EL_SP_MURPHY_CLONE;
 
        return;
       }
       else
       {
+       stored_player[0].initial_element = element;
        stored_player[0].use_murphy = TRUE;
 
        if (!level.use_artwork_element[0])
          stored_player[0].artwork_element = EL_SP_MURPHY;
       }
 
-      Feld[x][y] = EL_PLAYER_1;
+      Tile[x][y] = EL_PLAYER_1;
     }
   }
 
   if (init_game)
   {
-    struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
+    struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
     int jx = player->jx, jy = player->jy;
 
     player->present = TRUE;
@@ -1741,48 +1745,57 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
                                level.sp_block_last_field :
                                level.block_last_field);
 
-    /* ---------- initialize player's last field block delay --------------- */
+    // ---------- initialize player's last field block delay ------------------
 
-    /* always start with reliable default value (no adjustment needed) */
+    // always start with reliable default value (no adjustment needed)
     player->block_delay_adjustment = 0;
 
-    /* special case 1: in Supaplex, Murphy blocks last field one more frame */
+    // special case 1: in Supaplex, Murphy blocks last field one more frame
     if (player->block_last_field && element == EL_SP_MURPHY)
       player->block_delay_adjustment = 1;
 
-    /* special case 2: in game engines before 3.1.1, blocking was different */
+    // special case 2: in game engines before 3.1.1, blocking was different
     if (game.use_block_last_field_bug)
       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
 
-    if (!options.network || player->connected)
+    if (!network.enabled || player->connected_network)
     {
       player->active = TRUE;
 
-      /* remove potentially duplicate players */
-      if (StorePlayer[jx][jy] == Feld[x][y])
+      // remove potentially duplicate players
+      if (StorePlayer[jx][jy] == Tile[x][y])
        StorePlayer[jx][jy] = 0;
 
-      StorePlayer[x][y] = Feld[x][y];
+      StorePlayer[x][y] = Tile[x][y];
 
-      if (options.debug)
-      {
-       printf("Player %d activated.\n", player->element_nr);
-       printf("[Local player is %d and currently %s.]\n",
-              local_player->element_nr,
-              local_player->active ? "active" : "not active");
-      }
+#if DEBUG_INIT_PLAYER
+      Debug("game:init:player", "- player element %d activated",
+           player->element_nr);
+      Debug("game:init:player", "  (local player is %d and currently %s)",
+           local_player->element_nr,
+           local_player->active ? "active" : "not active");
     }
+#endif
 
-    Feld[x][y] = EL_EMPTY;
+    Tile[x][y] = EL_EMPTY;
 
     player->jx = player->last_jx = x;
     player->jy = player->last_jy = y;
   }
+
+  // always check if player was just killed and should be reanimated
+  {
+    int player_nr = GET_PLAYER_NR(element);
+    struct PlayerInfo *player = &stored_player[player_nr];
+
+    if (player->active && player->killed)
+      player->reanimated = TRUE; // if player was just killed, reanimate him
+  }
 }
 
 static void InitField(int x, int y, boolean init_game)
 {
-  int element = Feld[x][y];
+  int element = Tile[x][y];
 
   switch (element)
   {
@@ -1795,28 +1808,32 @@ static void InitField(int x, int y, boolean init_game)
       break;
 
     case EL_SOKOBAN_FIELD_PLAYER:
-      element = Feld[x][y] = EL_PLAYER_1;
+      element = Tile[x][y] = EL_PLAYER_1;
       InitField(x, y, init_game);
 
-      element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
+      element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
       InitField(x, y, init_game);
       break;
 
     case EL_SOKOBAN_FIELD_EMPTY:
-      local_player->sokobanfields_still_needed++;
+      IncrementSokobanFieldsNeeded();
+      break;
+
+    case EL_SOKOBAN_OBJECT:
+      IncrementSokobanObjectsNeeded();
       break;
 
     case EL_STONEBLOCK:
-      if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
-       Feld[x][y] = EL_ACID_POOL_TOPLEFT;
-      else if (x > 0 && Feld[x-1][y] == EL_ACID)
-       Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
-      else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
-       Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
-      else if (y > 0 && Feld[x][y-1] == EL_ACID)
-       Feld[x][y] = EL_ACID_POOL_BOTTOM;
-      else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
-       Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
+      if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
+       Tile[x][y] = EL_ACID_POOL_TOPLEFT;
+      else if (x > 0 && Tile[x-1][y] == EL_ACID)
+       Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
+      else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
+       Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
+      else if (y > 0 && Tile[x][y-1] == EL_ACID)
+       Tile[x][y] = EL_ACID_POOL_BOTTOM;
+      else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
+       Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
       break;
 
     case EL_BUG:
@@ -1858,6 +1875,8 @@ static void InitField(int x, int y, boolean init_game)
     case EL_MOLE_RIGHT:
     case EL_MOLE_UP:
     case EL_MOLE_DOWN:
+    case EL_SPRING_LEFT:
+    case EL_SPRING_RIGHT:
       InitMovDir(x, y);
       break;
 
@@ -1869,7 +1888,7 @@ static void InitField(int x, int y, boolean init_game)
     case EL_AMOEBA_DROP:
       if (y == lev_fieldy - 1)
       {
-       Feld[x][y] = EL_AMOEBA_GROWING;
+       Tile[x][y] = EL_AMOEBA_GROWING;
        Store[x][y] = EL_AMOEBA_WET;
       }
       break;
@@ -1888,11 +1907,11 @@ static void InitField(int x, int y, boolean init_game)
       break;
 
     case EL_LAMP:
-      local_player->lights_still_needed++;
+      game.lights_still_needed++;
       break;
 
     case EL_PENGUIN:
-      local_player->friends_still_needed++;
+      game.friends_still_needed++;
       break;
 
     case EL_PIG:
@@ -1914,34 +1933,22 @@ static void InitField(int x, int y, boolean init_game)
     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
       if (init_game)
       {
-       int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
-       int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
-       int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
+       int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
+       int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
+       int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
 
-       if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
+       if (game.belt_dir_nr[belt_nr] == 3)     // initial value
        {
          game.belt_dir[belt_nr] = belt_dir;
          game.belt_dir_nr[belt_nr] = belt_dir_nr;
        }
-       else    /* more than one switch -- set it like the first switch */
+       else    // more than one switch -- set it like the first switch
        {
-         Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
+         Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
        }
       }
       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;
@@ -1952,17 +1959,17 @@ static void InitField(int x, int y, boolean init_game)
     case EL_INVISIBLE_SAND:
       if (game.light_time_left > 0 ||
          game.lenses_time_left > 0)
-        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+        Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
       break;
 
     case EL_EMC_MAGIC_BALL:
-      if (game.ball_state)
-       Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
+      if (game.ball_active)
+       Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
       break;
 
     case EL_EMC_MAGIC_BALL_SWITCH:
-      if (game.ball_state)
-       Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
+      if (game.ball_active)
+       Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
       break;
 
     case EL_TRIGGER_PLAYER:
@@ -1989,8 +1996,8 @@ static void InitField(int x, int y, boolean init_game)
     case EL_NEXT_CE_6:
     case EL_NEXT_CE_7:
     case EL_NEXT_CE_8:
-      /* reference elements should not be used on the playfield */
-      Feld[x][y] = EL_EMPTY;
+      // reference elements should not be used on the playfield
+      Tile[x][y] = EL_EMPTY;
       break;
 
     default:
@@ -1999,14 +2006,12 @@ 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
+         CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
       }
       else if (IS_GROUP_ELEMENT(element))
       {
-       Feld[x][y] = GetElementFromGroupElement(element);
+       Tile[x][y] = GetElementFromGroupElement(element);
 
        InitField(x, y, init_game);
       }
@@ -2018,23 +2023,23 @@ 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)
+static void InitField_WithBug1(int x, int y, boolean init_game)
 {
   InitField(x, y, init_game);
 
-  /* not needed to call InitMovDir() -- already done by InitField()! */
+  // not needed to call InitMovDir() -- already done by InitField()!
   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
-      CAN_MOVE(Feld[x][y]))
+      CAN_MOVE(Tile[x][y]))
     InitMovDir(x, y);
 }
 
-static inline void InitField_WithBug2(int x, int y, boolean init_game)
+static void InitField_WithBug2(int x, int y, boolean init_game)
 {
-  int old_element = Feld[x][y];
+  int old_element = Tile[x][y];
 
   InitField(x, y, init_game);
 
-  /* not needed to call InitMovDir() -- already done by InitField()! */
+  // not needed to call InitMovDir() -- already done by InitField()!
   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
       CAN_MOVE(old_element) &&
       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
@@ -2049,8 +2054,6 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
   */
 }
 
-#if 1
-
 static int get_key_element_from_nr(int key_nr)
 {
   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
@@ -2073,8 +2076,8 @@ static int get_next_dropped_element(struct PlayerInfo *player)
 
 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
 {
-  /* pos >= 0: get element from bottom of the stack;
-     pos <  0: get element from top of the stack */
+  // pos >= 0: get element from bottom of the stack;
+  // pos <  0: get element from top of the stack
 
   if (pos < 0)
   {
@@ -2120,7 +2123,17 @@ static int compareGamePanelOrderInfo(const void *object1, const void *object2)
   return compare_result;
 }
 
-void InitGameControlValues()
+int getPlayerInventorySize(int player_nr)
+{
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    return game_em.ply[player_nr]->dynamite;
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    return game_sp.red_disk_count;
+  else
+    return stored_player[player_nr].inventory_size;
+}
+
+static void InitGameControlValues(void)
 {
   int i;
 
@@ -2134,16 +2147,17 @@ void InitGameControlValues()
 
     if (nr != i)
     {
-      Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
-      Error(ERR_EXIT, "this should not happen -- please debug");
+      Error("'game_panel_controls' structure corrupted at %d", i);
+
+      Fail("this should not happen -- please debug");
     }
 
-    /* force update of game controls after initialization */
+    // force update of game controls after initialization
     gpc->value = gpc->last_value = -1;
     gpc->frame = gpc->last_frame = -1;
     gpc->gfx_frame = -1;
 
-    /* determine panel value width for later calculation of alignment */
+    // determine panel value width for later calculation of alignment
     if (type == TYPE_INTEGER || type == TYPE_STRING)
     {
       pos->width = pos->size * getFontWidth(pos->font);
@@ -2155,22 +2169,22 @@ void InitGameControlValues()
       pos->height = pos->size;
     }
 
-    /* fill structure for game panel draw order */
+    // fill structure for game panel draw order
     gpo->nr = gpc->nr;
     gpo->sort_priority = pos->sort_priority;
   }
 
-  /* sort game panel controls according to sort_priority and control number */
+  // sort game panel controls according to sort_priority and control number
   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
        sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
 }
 
-void UpdatePlayfieldElementCount()
+static void UpdatePlayfieldElementCount(void)
 {
   boolean use_element_count = FALSE;
   int i, j, x, y;
 
-  /* first check if it is needed at all to calculate playfield element count */
+  // first check if it is needed at all to calculate playfield element count
   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
       use_element_count = TRUE;
@@ -2183,7 +2197,7 @@ void UpdatePlayfieldElementCount()
 
   SCAN_PLAYFIELD(x, y)
   {
-    element_info[Feld[x][y]].element_count++;
+    element_info[Tile[x][y]].element_count++;
   }
 
   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
@@ -2193,33 +2207,57 @@ void UpdatePlayfieldElementCount()
          element_info[j].element_count;
 }
 
-void UpdateGameControlValues()
+static void UpdateGameControlValues(void)
 {
   int i, k;
-  int time = (local_player->LevelSolved ?
-             local_player->LevelSolved_CountingTime :
+  int time = (game.LevelSolved ?
+             game.LevelSolved_CountingTime :
              level.game_engine_type == GAME_ENGINE_TYPE_EM ?
-             level.native_em_level->lev->time :
-             level.time == 0 ? TimePlayed : TimeLeft);
-  int score = (local_player->LevelSolved ?
-              local_player->LevelSolved_CountingScore :
+             game_em.lev->time :
+             level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+             game_sp.time_played :
+             level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+             game_mm.energy_left :
+             game.no_time_limit ? TimePlayed : TimeLeft);
+  int score = (game.LevelSolved ?
+              game.LevelSolved_CountingScore :
               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
-              level.native_em_level->lev->score :
-              local_player->score);
+              game_em.lev->score :
+              level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+              game_sp.score :
+              level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+              game_mm.score :
+              game.score);
   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
-             level.native_em_level->lev->required :
-             local_player->gems_still_needed);
+             game_em.lev->gems_needed :
+             level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+             game_sp.infotrons_still_needed :
+             level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+             game_mm.kettles_still_needed :
+             game.gems_still_needed);
   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
-                    level.native_em_level->lev->required > 0 :
-                    local_player->gems_still_needed > 0 ||
-                    local_player->sokobanfields_still_needed > 0 ||
-                    local_player->lights_still_needed > 0);
+                    game_em.lev->gems_needed > 0 :
+                    level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+                    game_sp.infotrons_still_needed > 0 :
+                    level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+                    game_mm.kettles_still_needed > 0 ||
+                    game_mm.lights_still_needed > 0 :
+                    game.gems_still_needed > 0 ||
+                    game.sokoban_fields_still_needed > 0 ||
+                    game.sokoban_objects_still_needed > 0 ||
+                    game.lights_still_needed > 0);
+  int health = (game.LevelSolved ?
+               game.LevelSolved_CountingHealth :
+               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+               MM_HEALTH(game_mm.laser_overload_value) :
+               game.health);
 
   UpdatePlayfieldElementCount();
 
-  /* update game panel control values */
+  // update game panel control values
 
-  game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
+  // used instead of "level_nr" (for network games)
+  game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
   game_panel_controls[GAME_PANEL_GEMS].value = gems;
 
   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
@@ -2232,11 +2270,15 @@ void UpdateGameControlValues()
   {
     for (i = 0; i < MAX_PLAYERS; i++)
     {
+      // only one player in Supaplex game engine
+      if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
+       break;
+
       for (k = 0; k < MAX_NUM_KEYS; k++)
       {
        if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
        {
-         if (level.native_em_level->ply[i]->keys & (1 << k))
+         if (game_em.ply[i]->keys & (1 << k))
            game_panel_controls[GAME_PANEL_KEY_1 + k].value =
              get_key_element_from_nr(k);
        }
@@ -2245,12 +2287,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
-       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 =
@@ -2268,7 +2306,7 @@ void UpdateGameControlValues()
     {
       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
       {
-       if (level.native_em_level->ply[player_nr]->keys & (1 << k))
+       if (game_em.ply[player_nr]->keys & (1 << k))
          game_panel_controls[GAME_PANEL_KEY_1 + k].value =
            get_key_element_from_nr(k);
       }
@@ -2277,12 +2315,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
-      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;
@@ -2308,6 +2342,16 @@ void UpdateGameControlValues()
   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
 
+  if (level.time == 0)
+    game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
+  else
+    game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
+
+  game_panel_controls[GAME_PANEL_HEALTH].value = health;
+  game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
+
+  game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
+
   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
      EL_EMPTY);
@@ -2323,9 +2367,9 @@ void UpdateGameControlValues()
     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
 
   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
-    (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
+    (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
-    (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
+    (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
      EL_EMC_MAGIC_BALL_SWITCH);
 
   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
@@ -2366,12 +2410,12 @@ void UpdateGameControlValues()
     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
 
   game_panel_controls[GAME_PANEL_PENGUINS].value =
-    local_player->friends_still_needed;
+    game.friends_still_needed;
 
   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
-    local_player->sokobanfields_still_needed;
+    game.sokoban_objects_still_needed;
   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
-    local_player->sokobanfields_still_needed;
+    game.sokoban_fields_still_needed;
 
   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
@@ -2390,12 +2434,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;
@@ -2425,7 +2465,7 @@ void UpdateGameControlValues()
   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
 
-  /* update game panel control frames */
+  // update game panel control frames
 
   for (i = 0; game_panel_controls[i].nr != -1; i++)
   {
@@ -2462,6 +2502,36 @@ void UpdateGameControlValues()
        gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
                                              gpc->gfx_frame);
 
+       if (ANIM_MODE(graphic) == ANIM_RANDOM)
+         gfx.anim_random_frame = last_anim_random_frame;
+      }
+    }
+    else if (gpc->type == TYPE_GRAPHIC)
+    {
+      if (gpc->graphic != IMG_UNDEFINED)
+      {
+       int last_anim_random_frame = gfx.anim_random_frame;
+       int graphic = gpc->graphic;
+
+       if (gpc->value != gpc->last_value)
+       {
+         gpc->gfx_frame = 0;
+         gpc->gfx_random = INIT_GFX_RANDOM();
+       }
+       else
+       {
+         gpc->gfx_frame++;
+
+         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
+           gpc->gfx_random = INIT_GFX_RANDOM();
+       }
+
+       if (ANIM_MODE(graphic) == ANIM_RANDOM)
+         gfx.anim_random_frame = gpc->gfx_random;
+
+       gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
+
        if (ANIM_MODE(graphic) == ANIM_RANDOM)
          gfx.anim_random_frame = last_anim_random_frame;
       }
@@ -2469,7 +2539,7 @@ void UpdateGameControlValues()
   }
 }
 
-void DisplayGameControlValues()
+static void DisplayGameControlValues(void)
 {
   boolean redraw_panel = FALSE;
   int i;
@@ -2491,41 +2561,26 @@ void DisplayGameControlValues()
   if (!redraw_panel)
     return;
 
-  /* copy default game door content to main double buffer */
-  BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-            DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+  // copy default game door content to main double buffer
 
-  /* redraw game control buttons */
-#if 1
+  // !!! CHECK AGAIN !!!
+  SetPanelBackground();
+  // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+  DrawBackground(DX, DY, DXSIZE, DYSIZE);
+
+  // redraw game control buttons
   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;
@@ -2534,18 +2589,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 ||
@@ -2553,7 +2599,7 @@ void DisplayGameControlValues()
       {
        boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
 
-       if (use_dynamic_size)           /* use dynamic number of digits */
+       if (use_dynamic_size)           // use dynamic number of digits
        {
          int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
          int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
@@ -2563,33 +2609,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 */
+      // correct text size if "digits" is zero or less
       if (size <= 0)
        size = strlen(int2str(value, size));
 
-      /* dynamically correct text alignment */
+      // dynamically correct text alignment
       pos->width = size * getFontWidth(font);
-#endif
 
       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
                  int2str(value, size), font, mask_mode);
@@ -2603,18 +2631,18 @@ 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;
        graphic = el2panelimg(value);
 
-       // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
+#if 0
+       Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
+             element, EL_NAME(element), size);
+#endif
 
-#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);
@@ -2623,41 +2651,100 @@ 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);
+    }
+    else if (type == TYPE_GRAPHIC)
+    {
+      int graphic        = gpc->graphic;
+      int graphic_active = gpc->graphic_active;
+      Bitmap *src_bitmap;
+      int src_x, src_y;
+      int width, height;
+      int dst_x = PANEL_XPOS(pos);
+      int dst_y = PANEL_YPOS(pos);
+      boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
+                     level.game_engine_type != GAME_ENGINE_TYPE_MM);
 
-       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
+      if (graphic != IMG_UNDEFINED && !skip)
       {
-       element = value;
-       graphic = el2panelimg(value);
+       if (pos->style == STYLE_REVERSE)
+         value = 100 - value;
 
-       getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
-      }
+       getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
 
-      width  = graphic_info[graphic].width  * size / TILESIZE;
-      height = graphic_info[graphic].height * size / TILESIZE;
+       if (pos->direction & MV_HORIZONTAL)
+       {
+         width  = graphic_info[graphic_active].width * value / 100;
+         height = graphic_info[graphic_active].height;
 
-      BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
-#endif
+         if (pos->direction == MV_LEFT)
+         {
+           src_x += graphic_info[graphic_active].width - width;
+           dst_x += graphic_info[graphic_active].width - width;
+         }
+       }
+       else
+       {
+         width  = graphic_info[graphic_active].width;
+         height = graphic_info[graphic_active].height * value / 100;
+
+         if (pos->direction == MV_UP)
+         {
+           src_y += graphic_info[graphic_active].height - height;
+           dst_y += graphic_info[graphic_active].height - height;
+         }
+       }
+
+       if (draw_masked)
+         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);
+
+       getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+
+       if (pos->direction & MV_HORIZONTAL)
+       {
+         if (pos->direction == MV_RIGHT)
+         {
+           src_x += width;
+           dst_x += width;
+         }
+         else
+         {
+           dst_x = PANEL_XPOS(pos);
+         }
+
+         width = graphic_info[graphic].width - width;
+       }
+       else
+       {
+         if (pos->direction == MV_DOWN)
+         {
+           src_y += height;
+           dst_y += height;
+         }
+         else
+         {
+           dst_y = PANEL_YPOS(pos);
+         }
+
+         height = graphic_info[graphic].height - height;
+       }
+
+       if (draw_masked)
+         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 (type == TYPE_STRING)
     {
@@ -2672,22 +2759,8 @@ void DisplayGameControlValues()
 
       if (nr == GAME_PANEL_GRAVITY_STATE)
       {
-       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
+       int font1 = pos->font;          // (used for normal state)
+       int font2 = pos->font_alt;      // (used for active state)
 
        font = (active ? font2 : font1);
       }
@@ -2696,16 +2769,14 @@ void DisplayGameControlValues()
       {
        char *s_cut;
 
-#if 1
        if (size <= 0)
        {
-         /* don't truncate output if "chars" is zero or less */
+         // don't truncate output if "chars" is zero or less
          size = strlen(s);
 
-         /* dynamically correct text alignment */
+         // dynamically correct text alignment
          pos->width = size * getFontWidth(font);
        }
-#endif
 
        s_cut = getStringCopyN(s, size);
 
@@ -2719,474 +2790,137 @@ void DisplayGameControlValues()
     redraw_mask |= REDRAW_DOOR_1;
   }
 
-  game_status = GAME_MODE_PLAYING;
+  SetGameStatus(GAME_MODE_PLAYING);
 }
 
-void UpdateAndDisplayGameControlValues()
+void UpdateAndDisplayGameControlValues(void)
 {
-  if (tape.warp_forward)
+  if (tape.deactivate_display)
     return;
 
   UpdateGameControlValues();
   DisplayGameControlValues();
 }
 
-void DrawGameValue_Emeralds(int value)
+#if 0
+static void UpdateGameDoorValues(void)
 {
-  struct TextPosInfo *pos = &game.panel.gems;
-#if 1
-  int font_nr = pos->font;
-#else
-  int font_nr = FONT_TEXT_2;
-#endif
-  int font_width = getFontWidth(font_nr);
-  int chars = pos->size;
-
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
+  UpdateGameControlValues();
+}
 #endif
 
-  if (PANEL_DEACTIVATED(pos))
-    return;
+void DrawGameDoorValues(void)
+{
+  DisplayGameControlValues();
+}
 
-  pos->width = chars * font_width;
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
-}
+// ============================================================================
+// InitGameEngine()
+// ----------------------------------------------------------------------------
+// initialize game engine due to level / tape version number
+// ============================================================================
 
-void DrawGameValue_Dynamite(int value)
+static void InitGameEngine(void)
 {
-  struct TextPosInfo *pos = &game.panel.inventory_count;
-#if 1
-  int font_nr = pos->font;
-#else
-  int font_nr = FONT_TEXT_2;
-#endif
-  int font_width = getFontWidth(font_nr);
-  int chars = pos->size;
+  int i, j, k, l, x, y;
 
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
+  // set game engine from tape file when re-playing, else from level file
+  game.engine_version = (tape.playing ? tape.engine_version :
+                        level.game_version);
 
-  if (PANEL_DEACTIVATED(pos))
-    return;
+  // set single or multi-player game mode (needed for re-playing tapes)
+  game.team_mode = setup.team_mode;
 
-  pos->width = chars * font_width;
+  if (tape.playing)
+  {
+    int num_players = 0;
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
-}
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (tape.player_participates[i])
+       num_players++;
 
-void DrawGameValue_Score(int value)
-{
-  struct TextPosInfo *pos = &game.panel.score;
-#if 1
-  int font_nr = pos->font;
-#else
-  int font_nr = FONT_TEXT_2;
-#endif
-  int font_width = getFontWidth(font_nr);
-  int chars = pos->size;
+    // multi-player tapes contain input data for more than one player
+    game.team_mode = (num_players > 1);
+  }
 
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
+#if 0
+  Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
+       level.game_version);
+  Debug("game:init:level", "          tape.file_version   == %06d",
+       tape.file_version);
+  Debug("game:init:level", "          tape.game_version   == %06d",
+       tape.game_version);
+  Debug("game:init:level", "          tape.engine_version == %06d",
+       tape.engine_version);
+  Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
+       game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
+#endif
+
+  // --------------------------------------------------------------------------
+  // set flags for bugs and changes according to active game engine version
+  // --------------------------------------------------------------------------
 
-  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;
-#if 1
-  int font1_nr = pos->font;
-  int font2_nr = pos->font_alt;
-#else
-  int font1_nr = FONT_TEXT_2;
-  int font2_nr = FONT_TEXT_1;
-#endif
-  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;
-#if 1
-  int font1_nr = pos->font;
-  int font2_nr = pos->font_alt;
-#else
-  int font1_nr = FONT_TEXT_2;
-  int font2_nr = FONT_TEXT_1;
-#endif
-  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])
-{
-#if 0
-  struct TextPosInfo *pos = &game.panel.keys;
-#endif
-#if 0
-  int base_key_graphic = EL_KEY_1;
-#endif
-  int i;
-
-#if 1
-  return;      /* !!! USE NEW STUFF !!! */
-#endif
-
-#if 0
-  if (PANEL_DEACTIVATED(pos))
-    return;
-#endif
-
-#if 0
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    base_key_graphic = EL_EM_KEY_1;
-#endif
-
-#if 0
-  pos->width = 4 * MINI_TILEX;
-#endif
-
-#if 1
-  for (i = 0; i < MAX_NUM_KEYS; i++)
-#else
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
-#endif
-  {
-#if 1
-    struct TextPosInfo *pos = &game.panel.key[i];
-#endif
-    int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
-    int src_y = DOOR_GFX_PAGEY1 + 123;
-#if 1
-    int dst_x = PANEL_XPOS(pos);
-    int dst_y = PANEL_YPOS(pos);
-#else
-    int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
-    int dst_y = PANEL_YPOS(pos);
-#endif
-
-#if 1
-    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);
-#endif
-
-#if 1
-    if (PANEL_DEACTIVATED(pos))
-      continue;
-#endif
-
-#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])
-    {
-#if 0
-      int graphic = el2edimg(base_key_graphic + i);
-#endif
-      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 1
-    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);
-#else
-    if (key[i])
-      DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
-    else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
-                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
-#endif
-#endif
-  }
-}
-
-#else
-
-void DrawGameValue_Emeralds(int value)
-{
-  int font_nr = FONT_TEXT_2;
-  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.gems))
-    return;
-
-  DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
-}
-
-void DrawGameValue_Dynamite(int value)
-{
-  int font_nr = FONT_TEXT_2;
-  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.inventory_count))
-    return;
-
-  DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
-}
-
-void DrawGameValue_Score(int value)
-{
-  int font_nr = FONT_TEXT_2;
-  int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.score))
-    return;
-
-  DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
-}
-
-void DrawGameValue_Time(int value)
-{
-  int font1_nr = FONT_TEXT_2;
-#if 1
-  int font2_nr = FONT_TEXT_1;
-#else
-  int font2_nr = FONT_LEVEL_NUMBER;
-#endif
-  int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
-  int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.time))
-    return;
-
-  /* clear background if value just changed its size */
-  if (value == 999 || value == 1000)
-    ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
-
-  if (value < 1000)
-    DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
-  else
-    DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
-}
-
-void DrawGameValue_Level(int value)
-{
-  int font1_nr = FONT_TEXT_2;
-#if 1
-  int font2_nr = FONT_TEXT_1;
-#else
-  int font2_nr = FONT_LEVEL_NUMBER;
-#endif
-
-  if (PANEL_DEACTIVATED(game.panel.level))
-    return;
-
-  if (level_nr < 100)
-    DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
-  else
-    DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
-}
-
-void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
-{
-  int base_key_graphic = EL_KEY_1;
-  int i;
-
-  if (PANEL_DEACTIVATED(game.panel.keys))
-    return;
-
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    base_key_graphic = EL_EM_KEY_1;
-
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
-  {
-    int x = XX_KEYS + i * MINI_TILEX;
-    int y = YY_KEYS;
-
-    if (key[i])
-      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
-    else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + 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();
-}
-
-void DrawGameDoorValues()
-{
-  DisplayGameControlValues();
-}
-
-void DrawGameDoorValues_OLD()
-{
-  int time_value = (level.time == 0 ? 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;
-  }
+  /*
+    Summary of bugfix:
+    Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
 
-  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);
+    Bug was introduced in version:
+    2.0.1
 
-      dynamite_value += stored_player[i].inventory_size;
-    }
-  }
-  else
-  {
-    int player_nr = game.centered_player_nr;
+    Bug was fixed in version:
+    4.2.0.0
 
-    for (i = 0; i < MAX_NUM_KEYS; i++)
-      if (stored_player[player_nr].key[i])
-       key_bits |= (1 << i);
+    Description:
+    In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
+    but the property "can fall" was missing, which caused some levels to be
+    unsolvable. This was fixed in version 4.2.0.0.
 
-    dynamite_value = stored_player[player_nr].inventory_size;
-  }
+    Affected levels/tapes:
+    An example for a tape that was fixed by this bugfix is tape 029 from the
+    level set "rnd_sam_bateman".
+    The wrong behaviour will still be used for all levels or tapes that were
+    created/recorded with it. An example for this is tape 023 from the level
+    set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
+  */
 
-  DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
-                   key_bits);
-}
+  boolean use_amoeba_dropping_cannot_fall_bug =
+    ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
+      game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
+     (tape.playing &&
+      tape.game_version >= VERSION_IDENT(2,0,1,0) &&
+      tape.game_version <  VERSION_IDENT(4,2,0,0)));
 
+  /*
+    Summary of bugfix/change:
+    Fixed move speed of elements entering or leaving magic wall.
 
-/*
-  =============================================================================
-  InitGameEngine()
-  -----------------------------------------------------------------------------
-  initialize game engine due to level / tape version number
-  =============================================================================
-*/
+    Fixed/changed in version:
+    2.0.1
 
-static void InitGameEngine()
-{
-  int i, j, k, l, x, y;
+    Description:
+    Before 2.0.1, move speed of elements entering or leaving magic wall was
+    twice as fast as it is now.
+    Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
 
-  /* set game engine from tape file when re-playing, else from level file */
-  game.engine_version = (tape.playing ? tape.engine_version :
-                        level.game_version);
+    Affected levels/tapes:
+    The first condition is generally needed for all levels/tapes before version
+    2.0.1, which might use the old behaviour before it was changed; known tapes
+    that are affected: Tape 014 from the level set "rnd_conor_mancone".
+    The second condition is an exception from the above case and is needed for
+    the special case of tapes recorded with game (not engine!) version 2.0.1 or
+    above, but before it was known that this change would break tapes like the
+    above and was fixed in 4.2.0.0, so that the changed behaviour was active
+    although the engine version while recording maybe was before 2.0.1. There
+    are a lot of tapes that are affected by this exception, like tape 006 from
+    the level set "rnd_conor_mancone".
+  */
 
-  /* ---------------------------------------------------------------------- */
-  /* set flags for bugs and changes according to active game engine version */
-  /* ---------------------------------------------------------------------- */
+  boolean use_old_move_stepsize_for_magic_wall =
+    (game.engine_version < VERSION_IDENT(2,0,1,0) &&
+     !(tape.playing &&
+       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
+       tape.game_version <  VERSION_IDENT(4,2,0,0)));
 
   /*
     Summary of bugfix/change:
@@ -3250,81 +2984,70 @@ 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.
+  /* various special flags and settings for native Emerald Mine game engine */
 
-    Fixed/changed in version:
-    3.2.0-6
+  game_em.use_single_button =
+    (game.engine_version > VERSION_IDENT(4,0,0,2));
 
-    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_snap_key_bug =
+    (game.engine_version < VERSION_IDENT(4,0,1,0));
 
-    Affected levels/tapes:
-    Probably many.
-  */
+  game_em.use_random_bug =
+    (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
 
-#if USE_ONLY_ONE_CHANGE_PER_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
+  boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
+
+  game_em.use_old_explosions           = use_old_em_engine;
+  game_em.use_old_android              = use_old_em_engine;
+  game_em.use_old_push_elements                = use_old_em_engine;
+  game_em.use_old_push_into_acid       = use_old_em_engine;
+
+  game_em.use_wrap_around              = !use_old_em_engine;
+
+  // --------------------------------------------------------------------------
 
-  /* ---------------------------------------------------------------------- */
+  // set maximal allowed number of custom element changes per game frame
+  game.max_num_changes_per_frame = 1;
 
-  /* default scan direction: scan playfield from top/left to bottom/right */
+  // default scan direction: scan playfield from top/left to bottom/right
   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
 
-  /* dynamically adjust element properties according to game engine version */
+  // dynamically adjust element properties according to game engine version
   InitElementPropertiesEngine(game.engine_version);
 
-#if 0
-  printf("level %d: level version == %06d\n", level_nr, level.game_version);
-  printf("          tape version == %06d [%s] [file: %06d]\n",
-        tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
-        tape.file_version);
-  printf("       => game.engine_version == %06d\n", game.engine_version);
-#endif
+  // ---------- initialize special element properties -------------------------
+
+  // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
+  if (use_amoeba_dropping_cannot_fall_bug)
+    SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
 
-  /* ---------- initialize player's initial move delay --------------------- */
+  // ---------- initialize player's initial move delay ------------------------
 
-  /* dynamically adjust player properties according to level information */
+  // dynamically adjust player properties according to level information
   for (i = 0; i < MAX_PLAYERS; i++)
     game.initial_move_delay_value[i] =
       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
 
-  /* dynamically adjust player properties according to game engine version */
+  // dynamically adjust player properties according to game engine version
   for (i = 0; i < MAX_PLAYERS; i++)
     game.initial_move_delay[i] =
       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
        game.initial_move_delay_value[i] : 0);
 
-  /* ---------- initialize player's initial push delay --------------------- */
+  // ---------- initialize player's initial push delay ------------------------
 
-  /* dynamically adjust player properties according to game engine version */
+  // dynamically adjust player properties according to game engine version
   game.initial_push_delay_value =
     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
 
-  /* ---------- initialize changing elements ------------------------------- */
+  // ---------- initialize changing elements ----------------------------------
 
-  /* initialize changing elements information */
+  // initialize changing elements information
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     struct ElementInfo *ei = &element_info[i];
 
-    /* this pointer might have been changed in the level editor */
+    // this pointer might have been changed in the level editor
     ei->change = &ei->change_page[0];
 
     if (!IS_CUSTOM_ELEMENT(i))
@@ -3344,7 +3067,7 @@ static void InitGameEngine()
     }
   }
 
-  /* add changing elements from pre-defined list */
+  // add changing elements from pre-defined list
   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
   {
     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
@@ -3366,7 +3089,7 @@ static void InitGameEngine()
     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
   }
 
-  /* ---------- initialize internal run-time variables --------------------- */
+  // ---------- initialize internal run-time variables ------------------------
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
@@ -3380,7 +3103,7 @@ static void InitGameEngine()
     }
   }
 
-  /* add change events from custom element configuration */
+  // add change events from custom element configuration
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
@@ -3392,7 +3115,7 @@ static void InitGameEngine()
 
       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
       {
-       /* only add event page for the first page found with this event */
+       // only add event page for the first page found with this event
        if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
        {
          ei->has_change_event[k] = TRUE;
@@ -3404,8 +3127,7 @@ static void InitGameEngine()
     }
   }
 
-#if 1
-  /* ---------- initialize reference elements in change conditions --------- */
+  // ---------- initialize reference elements in change conditions ------------
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
@@ -3423,9 +3145,8 @@ static void InitGameEngine()
       ei->change_page[j].trigger_element = trigger_element;
     }
   }
-#endif
 
-  /* ---------- initialize run-time trigger player and element ------------- */
+  // ---------- initialize run-time trigger player and element ----------------
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
@@ -3434,22 +3155,22 @@ static void InitGameEngine()
     for (j = 0; j < ei->num_change_pages; j++)
     {
       ei->change_page[j].actual_trigger_element = EL_EMPTY;
-      ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
-      ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
+      ei->change_page[j].actual_trigger_player = EL_EMPTY;
+      ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
       ei->change_page[j].actual_trigger_ce_value = 0;
       ei->change_page[j].actual_trigger_ce_score = 0;
     }
   }
 
-  /* ---------- initialize trigger events ---------------------------------- */
+  // ---------- initialize trigger events -------------------------------------
 
-  /* initialize trigger events information */
+  // initialize trigger events information
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
       trigger_events[i][j] = FALSE;
 
-  /* add trigger events from element change event properties */
+  // add trigger events from element change event properties
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     struct ElementInfo *ei = &element_info[i];
@@ -3486,14 +3207,14 @@ static void InitGameEngine()
     }
   }
 
-  /* ---------- initialize push delay -------------------------------------- */
+  // ---------- initialize push delay -----------------------------------------
 
-  /* initialize push delay values to default */
+  // initialize push delay values to default
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     if (!IS_CUSTOM_ELEMENT(i))
     {
-      /* set default push delay values (corrected since version 3.0.7-1) */
+      // set default push delay values (corrected since version 3.0.7-1)
       if (game.engine_version < VERSION_IDENT(3,0,7,1))
       {
        element_info[i].push_delay_fixed = 2;
@@ -3507,7 +3228,7 @@ static void InitGameEngine()
     }
   }
 
-  /* set push delay value for certain elements from pre-defined list */
+  // set push delay value for certain elements from pre-defined list
   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
   {
     int e = push_delay_list[i].element;
@@ -3516,14 +3237,14 @@ static void InitGameEngine()
     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
   }
 
-  /* set push delay value for Supaplex elements for newer engine versions */
+  // set push delay value for Supaplex elements for newer engine versions
   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
   {
     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     {
       if (IS_SP_ELEMENT(i))
       {
-       /* set SP push delay to just enough to push under a falling zonk */
+       // set SP push delay to just enough to push under a falling zonk
        int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
 
        element_info[i].push_delay_fixed  = delay;
@@ -3532,53 +3253,63 @@ static void InitGameEngine()
     }
   }
 
-  /* ---------- initialize move stepsize ----------------------------------- */
+  // ---------- initialize move stepsize --------------------------------------
 
-  /* initialize move stepsize values to default */
+  // initialize move stepsize values to default
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     if (!IS_CUSTOM_ELEMENT(i))
       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
 
-  /* set move stepsize value for certain elements from pre-defined list */
+  // set move stepsize value for certain elements from pre-defined list
   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
   {
     int e = move_stepsize_list[i].element;
 
     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
+
+    // set move stepsize value for certain elements for older engine versions
+    if (use_old_move_stepsize_for_magic_wall)
+    {
+      if (e == EL_MAGIC_WALL_FILLING ||
+         e == EL_MAGIC_WALL_EMPTYING ||
+         e == EL_BD_MAGIC_WALL_FILLING ||
+         e == EL_BD_MAGIC_WALL_EMPTYING)
+       element_info[e].move_stepsize *= 2;
+    }
   }
 
-  /* ---------- initialize collect score ----------------------------------- */
+  // ---------- initialize collect score --------------------------------------
 
-  /* initialize collect score values for custom elements from initial value */
+  // initialize collect score values for custom elements from initial value
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     if (IS_CUSTOM_ELEMENT(i))
       element_info[i].collect_score = element_info[i].collect_score_initial;
 
-  /* ---------- initialize collect count ----------------------------------- */
+  // ---------- initialize collect count --------------------------------------
 
-  /* initialize collect count values for non-custom elements */
+  // initialize collect count values for non-custom elements
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     if (!IS_CUSTOM_ELEMENT(i))
       element_info[i].collect_count_initial = 0;
 
-  /* add collect count values for all elements from pre-defined list */
+  // add collect count values for all elements from pre-defined list
   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
     element_info[collect_count_list[i].element].collect_count_initial =
       collect_count_list[i].count;
 
-  /* ---------- initialize access direction -------------------------------- */
+  // ---------- initialize access direction -----------------------------------
 
-  /* initialize access direction values to default (access from every side) */
+  // initialize access direction values to default (access from every side)
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     if (!IS_CUSTOM_ELEMENT(i))
       element_info[i].access_direction = MV_ALL_DIRECTIONS;
 
-  /* set access direction value for certain elements from pre-defined list */
+  // set access direction value for certain elements from pre-defined list
   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
     element_info[access_direction_list[i].element].access_direction =
       access_direction_list[i].direction;
 
-  /* ---------- initialize explosion content ------------------------------- */
+  // ---------- initialize explosion content ----------------------------------
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     if (IS_CUSTOM_ELEMENT(i))
@@ -3586,7 +3317,7 @@ static void InitGameEngine()
 
     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
     {
-      /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
+      // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
 
       element_info[i].content.e[x][y] =
        (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
@@ -3611,20 +3342,70 @@ static void InitGameEngine()
     }
   }
 
-  /* ---------- initialize recursion detection ------------------------------ */
+  // ---------- initialize recursion detection --------------------------------
   recursion_loop_depth = 0;
   recursion_loop_detected = FALSE;
   recursion_loop_element = EL_UNDEFINED;
 
-  /* ---------- initialize graphics engine ---------------------------------- */
+  // ---------- initialize graphics engine ------------------------------------
   game.scroll_delay_value =
     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
+     level.game_engine_type == GAME_ENGINE_TYPE_EM &&
+     !setup.forced_scroll_delay           ? 0 :
      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;
+
+  // ---------- initialize level time for Supaplex engine ---------------------
+  // Supaplex levels with time limit currently unsupported -- should be added
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    level.time = 0;
+
+  // ---------- initialize flags for handling game actions --------------------
+
+  // set flags for game actions to default values
+  game.use_key_actions = TRUE;
+  game.use_mouse_actions = FALSE;
+
+  // when using Mirror Magic game engine, handle mouse events only
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+  {
+    game.use_key_actions = FALSE;
+    game.use_mouse_actions = TRUE;
+  }
+
+  // check for custom elements with mouse click events
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+  {
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+
+      if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
+         HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
+         HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
+         HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
+       game.use_mouse_actions = TRUE;
+    }
+  }
 }
 
-int get_num_special_action(int element, int action_first, int action_last)
+static int get_num_special_action(int element, int action_first,
+                                 int action_last)
 {
   int num_special_action = 0;
   int i, j;
@@ -3648,31 +3429,97 @@ int get_num_special_action(int element, int action_first, int action_last)
 }
 
 
-/*
-  =============================================================================
-  InitGame()
-  -----------------------------------------------------------------------------
-  initialize and start new game
-  =============================================================================
-*/
+// ============================================================================
+// InitGame()
+// ----------------------------------------------------------------------------
+// initialize and start new game
+// ============================================================================
 
-void InitGame()
+#if DEBUG_INIT_PLAYER
+static void DebugPrintPlayerStatus(char *message)
 {
-  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);
+  int i;
+
+  if (!options.debug)
+    return;
+
+  Debug("game:init:player", "%s:", message);
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    Debug("game:init:player",
+         "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
+         i + 1,
+         player->present,
+         player->connected,
+         player->connected_locally,
+         player->connected_network,
+         player->active,
+         (local_player == player ? " (local player)" : ""));
+  }
+}
 #endif
+
+void InitGame(void)
+{
+  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
+  int initial_move_dir = MV_DOWN;
   int i, j, x, y;
 
-  game_status = GAME_MODE_PLAYING;
+  // 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)
+    FadeSkipNextFadeOut();
+  else
+    FadeSetEnterScreen();
+
+  if (CheckFadeAll())
+    fade_mask = REDRAW_ALL;
+
+  FadeLevelSoundsAndMusic();
+
+  ExpireSoundLoops(TRUE);
+
+  FadeOut(fade_mask);
+
+  if (level_editor_test_game)
+    FadeSkipNextFadeIn();
+
+  // needed if different viewport properties defined for playing
+  ChangeViewportPropertiesIfNeeded();
+
+  ClearField();
+
+  DrawCompleteVideoDisplay();
+
+  OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
 
   InitGameEngine();
   InitGameControlValues();
 
-  /* don't play tapes over network */
-  network_playing = (options.network && !tape.playing);
+  // initialize tape actions from game when recording tape
+  if (tape.recording)
+  {
+    tape.use_key_actions   = game.use_key_actions;
+    tape.use_mouse_actions = game.use_mouse_actions;
+  }
+
+  // don't play tapes over network
+  network_playing = (network.enabled && !tape.playing);
 
   for (i = 0; i < MAX_PLAYERS; i++)
   {
@@ -3684,19 +3531,26 @@ void InitGame()
 
     player->present = FALSE;
     player->active = FALSE;
+    player->mapped = FALSE;
+
     player->killed = FALSE;
+    player->reanimated = FALSE;
+    player->buried = FALSE;
 
     player->action = 0;
     player->effective_action = 0;
     player->programmed_action = 0;
+    player->snap_action = 0;
 
-    player->score = 0;
-    player->score_final = 0;
+    player->mouse_action.lx = 0;
+    player->mouse_action.ly = 0;
+    player->mouse_action.button = 0;
+    player->mouse_action.button_hint = 0;
 
-    player->gems_still_needed = level.gems_needed;
-    player->sokobanfields_still_needed = 0;
-    player->lights_still_needed = 0;
-    player->friends_still_needed = 0;
+    player->effective_mouse_action.lx = 0;
+    player->effective_mouse_action.ly = 0;
+    player->effective_mouse_action.button = 0;
+    player->effective_mouse_action.button_hint = 0;
 
     for (j = 0; j < MAX_NUM_KEYS; j++)
       player->key[j] = FALSE;
@@ -3708,21 +3562,22 @@ void InitGame()
     player->dynabombs_left = 0;
     player->dynabomb_xl = FALSE;
 
-    player->MovDir = MV_NONE;
+    player->MovDir = initial_move_dir;
     player->MovPos = 0;
     player->GfxPos = 0;
-    player->GfxDir = MV_NONE;
+    player->GfxDir = initial_move_dir;
     player->GfxAction = ACTION_DEFAULT;
     player->Frame = 0;
     player->StepFrame = 0;
 
-    player->use_murphy = FALSE;
+    player->initial_element = player->element_nr;
     player->artwork_element =
       (level.use_artwork_element[i] ? level.artwork_element[i] :
        player->element_nr);
+    player->use_murphy = FALSE;
 
-    player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
-    player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
+    player->block_last_field = FALSE;  // initialized in InitPlayerField()
+    player->block_delay_adjustment = 0;        // initialized in InitPlayerField()
 
     player->gravity = level.initial_player_gravity[i];
 
@@ -3732,7 +3587,7 @@ void InitGame()
 
     player->step_counter = 0;
 
-    player->last_move_dir = MV_NONE;
+    player->last_move_dir = initial_move_dir;
 
     player->is_active = FALSE;
 
@@ -3750,13 +3605,20 @@ 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;
 
     player->anim_delay_counter = 0;
     player->post_delay_counter = 0;
 
-    player->dir_waiting = MV_NONE;
+    player->dir_waiting = initial_move_dir;
     player->action_waiting = ACTION_DEFAULT;
     player->last_action_waiting = ACTION_DEFAULT;
     player->special_action_bored = ACTION_DEFAULT;
@@ -3772,7 +3634,7 @@ void InitGame()
 
     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
 
-    player->push_delay       = -1;     /* initialized when pushing starts */
+    player->push_delay       = -1;     // initialized when pushing starts
     player->push_delay_value = game.initial_push_delay_value;
 
     player->drop_delay = 0;
@@ -3789,31 +3651,37 @@ void InitGame()
     player->inventory_infinite_element = EL_UNDEFINED;
     player->inventory_size = 0;
 
+    if (level.use_initial_inventory[i])
+    {
+      for (j = 0; j < level.initial_inventory_size[i]; j++)
+      {
+       int element = level.initial_inventory_content[i][j];
+       int collect_count = element_info[element].collect_count_initial;
+       int k;
+
+       if (!IS_CUSTOM_ELEMENT(element))
+         collect_count = 1;
+
+       if (collect_count == 0)
+         player->inventory_infinite_element = element;
+       else
+         for (k = 0; k < collect_count; k++)
+           if (player->inventory_size < MAX_INVENTORY_SIZE)
+             player->inventory_element[player->inventory_size++] = element;
+      }
+    }
+
     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
 
-    player->LevelSolved = FALSE;
-    player->GameOver = FALSE;
-
-    player->LevelSolved_GameWon = FALSE;
-    player->LevelSolved_GameEnd = FALSE;
-    player->LevelSolved_PanelOff = FALSE;
-    player->LevelSolved_SaveTape = FALSE;
-    player->LevelSolved_SaveScore = FALSE;
-    player->LevelSolved_CountingTime = 0;
-    player->LevelSolved_CountingScore = 0;
+    map_player_action[i] = i;
   }
 
   network_player_action_received = FALSE;
 
-#if defined(NETWORK_AVALIABLE)
-  /* initial null action */
+  // initial null action
   if (network_playing)
     SendToServer_MovePlayer(MV_NONE);
-#endif
-
-  ZX = ZY = -1;
-  ExitX = ExitY = -1;
 
   FrameCounter = 0;
   TimeFrames = 0;
@@ -3825,9 +3693,33 @@ void InitGame()
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
-  ScrollStepSize = 0;  /* will be correctly initialized by ScrollScreen() */
+  ScrollStepSize = 0;  // will be correctly initialized by ScrollScreen()
+
+  game.robot_wheel_x = -1;
+  game.robot_wheel_y = -1;
+
+  game.exit_x = -1;
+  game.exit_y = -1;
+
+  game.all_players_gone = FALSE;
+
+  game.LevelSolved = FALSE;
+  game.GameOver = FALSE;
+
+  game.GamePlayed = !tape.playing;
 
-  AllPlayersGone = FALSE;
+  game.LevelSolved_GameWon = FALSE;
+  game.LevelSolved_GameEnd = FALSE;
+  game.LevelSolved_SaveTape = FALSE;
+  game.LevelSolved_SaveScore = FALSE;
+
+  game.LevelSolved_CountingTime = 0;
+  game.LevelSolved_CountingScore = 0;
+  game.LevelSolved_CountingHealth = 0;
+
+  game.panel.active = TRUE;
+
+  game.no_time_limit = (level.time == 0);
 
   game.yamyam_content_nr = 0;
   game.robot_wheel_active = FALSE;
@@ -3838,49 +3730,49 @@ 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.score = 0;
+  game.score_final = 0;
+
+  game.health = MAX_HEALTH;
+  game.health_final = MAX_HEALTH;
+
+  game.gems_still_needed = level.gems_needed;
+  game.sokoban_fields_still_needed = 0;
+  game.sokoban_objects_still_needed = 0;
+  game.lights_still_needed = 0;
+  game.players_still_needed = 0;
+  game.friends_still_needed = 0;
 
   game.lenses_time_left = 0;
   game.magnify_time_left = 0;
 
-  game.ball_state = level.ball_state_initial;
+  game.ball_active = level.ball_active_initial;
   game.ball_content_nr = 0;
 
-  game.envelope_active = FALSE;
-
-  /* set focus to local player for network games, else to all players */
-  game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
-  game.centered_player_nr_next = game.centered_player_nr;
-  game.set_centered_player = FALSE;
+  game.explosions_delayed = TRUE;
 
-  if (network_playing && tape.recording)
-  {
-    /* store client dependent player focus when recording network games */
-    tape.centered_player_nr_next = game.centered_player_nr_next;
-    tape.set_centered_player = TRUE;
-  }
+  game.envelope_active = FALSE;
 
   for (i = 0; i < NUM_BELTS; i++)
   {
     game.belt_dir[i] = MV_NONE;
-    game.belt_dir_nr[i] = 3;           /* not moving, next moving left */
+    game.belt_dir_nr[i] = 3;           // not moving, next moving left
   }
 
   for (i = 0; i < MAX_NUM_AMOEBA; i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
 
+#if DEBUG_INIT_PLAYER
+  DebugPrintPlayerStatus("Player status at level initialization");
+#endif
+
   SCAN_PLAYFIELD(x, y)
   {
-    Feld[x][y] = level.field[x][y];
+    Tile[x][y] = Last[x][y] = level.field[x][y];
     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
+    CustomValue[x][y] = 0;             // initialized in InitField()
     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
     AmoebaNr[x][y] = 0;
     WasJustMoving[x][y] = 0;
@@ -3910,11 +3802,11 @@ void InitGame()
 
   SCAN_PLAYFIELD(x, y)
   {
-    if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
+    if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
       emulate_bd = FALSE;
-    if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
+    if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
       emulate_sb = FALSE;
-    if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
+    if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
       emulate_sp = FALSE;
 
     InitField(x, y, TRUE);
@@ -3928,7 +3820,7 @@ void InitGame()
   {
     struct PlayerInfo *player = &stored_player[i];
 
-    /* set number of special actions for bored and sleeping animation */
+    // set number of special actions for bored and sleeping animation
     player->num_special_action_bored =
       get_num_special_action(player->artwork_element,
                             ACTION_BORING_1, ACTION_BORING_LAST);
@@ -3941,27 +3833,25 @@ void InitGame()
                    emulate_sb ? EMU_SOKOBAN :
                    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
 
-#if USE_NEW_ALL_SLIPPERY
-  /* initialize type of slippery elements */
+  // initialize type of slippery elements
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     if (!IS_CUSTOM_ELEMENT(i))
     {
-      /* default: elements slip down either to the left or right randomly */
+      // default: elements slip down either to the left or right randomly
       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
 
-      /* SP style elements prefer to slip down on the left side */
+      // SP style elements prefer to slip down on the left side
       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
        element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
 
-      /* BD style elements prefer to slip down on the left side */
+      // BD style elements prefer to slip down on the left side
       if (game.emulation == EMU_BOULDERDASH)
        element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
     }
   }
-#endif
 
-  /* initialize explosion and ignition delay */
+  // initialize explosion and ignition delay
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
     if (!IS_CUSTOM_ELEMENT(i))
@@ -3979,22 +3869,157 @@ 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;
+  // correct non-moving belts to start moving left
+  for (i = 0; i < NUM_BELTS; i++)
+    if (game.belt_dir[i] == MV_NONE)
+      game.belt_dir_nr[i] = 3;         // not moving, next moving left
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+  // use preferred player also in local single-player mode
+  if (!network.enabled && !game.team_mode)
+  {
+    int new_index_nr = setup.network_player_nr;
+
+    if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+       stored_player[i].connected_locally = FALSE;
+
+      stored_player[new_index_nr].connected_locally = TRUE;
+    }
+  }
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    stored_player[i].connected = FALSE;
+
+    // in network game mode, the local player might not be the first player
+    if (stored_player[i].connected_locally)
+      local_player = &stored_player[i];
+  }
+
+  if (!network.enabled)
+    local_player->connected = TRUE;
+
+  if (tape.playing)
+  {
+    for (i = 0; i < MAX_PLAYERS; i++)
+      stored_player[i].connected = tape.player_participates[i];
+  }
+  else if (network.enabled)
+  {
+    // add team mode players connected over the network (needed for correct
+    // assignment of player figures from level to locally playing players)
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (stored_player[i].connected_network)
+       stored_player[i].connected = TRUE;
+  }
+  else if (game.team_mode)
+  {
+    // 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 (setup.input[i].use_joystick ||
+         setup.input[i].key.left != KSYM_UNDEFINED)
+       stored_player[i].connected = TRUE;
+  }
+
+#if DEBUG_INIT_PLAYER
+  DebugPrintPlayerStatus("Player status after level initialization");
+#endif
+
+#if DEBUG_INIT_PLAYER
+  Debug("game:init:player", "Reassigning players ...");
+#endif
+
+  // check if any connected player was not found in playfield
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    if (player->connected && !player->present)
+    {
+      struct PlayerInfo *field_player = NULL;
+
+#if DEBUG_INIT_PLAYER
+      Debug("game:init:player",
+           "- looking for field player for player %d ...", i + 1);
+#endif
+
+      // assign first free player found that is present in the playfield
+
+      // first try: look for unmapped playfield player that is not connected
+      for (j = 0; j < MAX_PLAYERS; j++)
+       if (field_player == NULL &&
+           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
+      for (j = 0; j < MAX_PLAYERS; j++)
+       if (field_player == NULL &&
+           stored_player[j].present &&
+           !stored_player[j].mapped)
+         field_player = &stored_player[j];
+
+      if (field_player != NULL)
+      {
+       int jx = field_player->jx, jy = field_player->jy;
+
+#if DEBUG_INIT_PLAYER
+       Debug("game:init:player", "- found player %d",
+             field_player->index_nr + 1);
+#endif
+
+       player->present = FALSE;
+       player->active = FALSE;
+
+       field_player->present = TRUE;
+       field_player->active = TRUE;
+
+       /*
+       player->initial_element = field_player->initial_element;
+       player->artwork_element = field_player->artwork_element;
 
-    if (element_info[i].ignition_delay < 1)    /* !!! check again !!! */
-      element_info[i].ignition_delay = 1;
+       player->block_last_field       = field_player->block_last_field;
+       player->block_delay_adjustment = field_player->block_delay_adjustment;
+       */
+
+       StorePlayer[jx][jy] = field_player->element_nr;
+
+       field_player->jx = field_player->last_jx = jx;
+       field_player->jy = field_player->last_jy = jy;
+
+       if (local_player == player)
+         local_player = field_player;
+
+       map_player_action[field_player->index_nr] = i;
+
+       field_player->mapped = TRUE;
+
+#if DEBUG_INIT_PLAYER
+       Debug("game:init:player", "- map_player_action[%d] == %d",
+             field_player->index_nr + 1, i + 1);
 #endif
+      }
+    }
+
+    if (player->connected && player->present)
+      player->mapped = TRUE;
   }
 
-  /* correct non-moving belts to start moving left */
-  for (i = 0; i < NUM_BELTS; i++)
-    if (game.belt_dir[i] == MV_NONE)
-      game.belt_dir_nr[i] = 3;         /* not moving, next moving left */
+#if DEBUG_INIT_PLAYER
+  DebugPrintPlayerStatus("Player status after player assignment (first stage)");
+#endif
 
-  /* check if any connected player was not found in playfield */
+#else
+
+  // check if any connected player was not found in playfield
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     struct PlayerInfo *player = &stored_player[i];
@@ -4003,24 +4028,26 @@ void InitGame()
     {
       for (j = 0; j < MAX_PLAYERS; j++)
       {
-       struct PlayerInfo *some_player = &stored_player[j];
-       int jx = some_player->jx, jy = some_player->jy;
+       struct PlayerInfo *field_player = &stored_player[j];
+       int jx = field_player->jx, jy = field_player->jy;
 
-       /* assign first free player found that is present in the playfield */
-       if (some_player->present && !some_player->connected)
+       // assign first free player found that is present in the playfield
+       if (field_player->present && !field_player->connected)
        {
          player->present = TRUE;
          player->active = TRUE;
 
-         some_player->present = FALSE;
-         some_player->active = FALSE;
+         field_player->present = FALSE;
+         field_player->active = FALSE;
 
-         player->artwork_element = some_player->artwork_element;
+         player->initial_element = field_player->initial_element;
+         player->artwork_element = field_player->artwork_element;
 
-         player->block_last_field       = some_player->block_last_field;
-         player->block_delay_adjustment = some_player->block_delay_adjustment;
+         player->block_last_field       = field_player->block_last_field;
+         player->block_delay_adjustment = field_player->block_delay_adjustment;
 
          StorePlayer[jx][jy] = player->element_nr;
+
          player->jx = player->last_jx = jx;
          player->jy = player->last_jy = jy;
 
@@ -4029,73 +4056,116 @@ void InitGame()
       }
     }
   }
+#endif
+
+#if 0
+  Debug("game:init:player", "local_player->present == %d",
+       local_player->present);
+#endif
+
+  // set focus to local player for network games, else to all players
+  game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
+  game.centered_player_nr_next = game.centered_player_nr;
+  game.set_centered_player = FALSE;
+  game.set_centered_player_wrap = FALSE;
+
+  if (network_playing && tape.recording)
+  {
+    // store client dependent player focus when recording network games
+    tape.centered_player_nr_next = game.centered_player_nr_next;
+    tape.set_centered_player = TRUE;
+  }
 
   if (tape.playing)
   {
-    /* when playing a tape, eliminate all players who do not participate */
+    // when playing a tape, eliminate all players who do not participate
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+
+    if (!game.team_mode)
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       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
+         Debug("game:init:player", "Removing player %d at (%d, %d)",
+               i + 1, jx, jy);
+#endif
+
+         player->active = FALSE;
+         StorePlayer[jx][jy] = 0;
+         Tile[jx][jy] = EL_EMPTY;
+       }
+      }
+    }
+
+#else
 
     for (i = 0; i < MAX_PLAYERS; i++)
     {
-      if (stored_player[i].active && !tape.player_participates[i])
+      if (stored_player[i].active &&
+         !tape.player_participates[i])
       {
        struct PlayerInfo *player = &stored_player[i];
        int jx = player->jx, jy = player->jy;
 
        player->active = FALSE;
        StorePlayer[jx][jy] = 0;
-       Feld[jx][jy] = EL_EMPTY;
+       Tile[jx][jy] = EL_EMPTY;
       }
     }
+#endif
   }
-  else if (!options.network && !setup.team_mode)       /* && !tape.playing */
+  else if (!network.enabled && !game.team_mode)                // && !tape.playing
   {
-    /* when in single player mode, eliminate all but the first active player */
+    // when in single player mode, eliminate all but the local player
 
     for (i = 0; i < MAX_PLAYERS; i++)
     {
-      if (stored_player[i].active)
+      struct PlayerInfo *player = &stored_player[i];
+
+      if (player->active && player != local_player)
       {
-       for (j = i + 1; j < MAX_PLAYERS; j++)
-       {
-         if (stored_player[j].active)
-         {
-           struct PlayerInfo *player = &stored_player[j];
-           int jx = player->jx, jy = player->jy;
+       int jx = player->jx, jy = player->jy;
 
-           player->active = FALSE;
-           player->present = FALSE;
+       player->active = FALSE;
+       player->present = FALSE;
 
-           StorePlayer[jx][jy] = 0;
-           Feld[jx][jy] = EL_EMPTY;
-         }
-       }
+       StorePlayer[jx][jy] = 0;
+       Tile[jx][jy] = EL_EMPTY;
       }
     }
   }
 
-  /* when recording the game, store which players take part in the game */
+  for (i = 0; i < MAX_PLAYERS; i++)
+    if (stored_player[i].active)
+      game.players_still_needed++;
+
+  if (level.solved_by_one_player)
+    game.players_still_needed = 1;
+
+  // when recording the game, store which players take part in the game
   if (tape.recording)
   {
+#if USE_NEW_PLAYER_ASSIGNMENTS
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (stored_player[i].connected)
+       tape.player_participates[i] = TRUE;
+#else
     for (i = 0; i < MAX_PLAYERS; i++)
       if (stored_player[i].active)
        tape.player_participates[i] = TRUE;
+#endif
   }
 
-  if (options.debug)
-  {
-    for (i = 0; i < MAX_PLAYERS; i++)
-    {
-      struct PlayerInfo *player = &stored_player[i];
-
-      printf("Player %d: present == %d, connected == %d, active == %d.\n",
-            i+1,
-            player->present,
-            player->connected,
-            player->active);
-      if (local_player == player)
-       printf("Player  %d is local player.\n", i+1);
-    }
-  }
+#if DEBUG_INIT_PLAYER
+  DebugPrintPlayerStatus("Player status after player assignment (final stage)");
+#endif
 
   if (BorderElement == EL_EMPTY)
   {
@@ -4112,14 +4182,18 @@ void InitGame()
     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
   }
 
-  if (lev_fieldx + (SBX_Left == -1 ? 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 == -1 ? 2 : 0) <= SCR_FIELDY)
+  if (full_lev_fieldy <= SCR_FIELDY)
     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
 
-  /* if local player not found, look for custom element that might create
-     the player (make some assumptions about the right custom element) */
+  if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
+    SBX_Left--;
+  if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
+    SBY_Upper--;
+
+  // 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)
   {
     int start_x = 0, start_y = 0;
@@ -4129,7 +4203,7 @@ void InitGame()
 
     SCAN_PLAYFIELD(x, y)
     {
-      int element = Feld[x][y];
+      int element = Tile[x][y];
       int content;
       int xx, yy;
       boolean is_player;
@@ -4152,7 +4226,7 @@ void InitGame()
       {
        for (i = 0; i < element_info[element].num_change_pages; i++)
        {
-         /* check for player created from custom element as single target */
+         // check for player created from custom element as single target
          content = element_info[element].change_page[i].target_element;
          is_player = ELEM_IS_PLAYER(content);
 
@@ -4170,7 +4244,7 @@ void InitGame()
 
       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
       {
-       /* check for player created from custom element as explosion content */
+       // check for player created from custom element as explosion content
        content = element_info[element].content.e[xx][yy];
        is_player = ELEM_IS_PLAYER(content);
 
@@ -4189,7 +4263,7 @@ void InitGame()
 
        for (i = 0; i < element_info[element].num_change_pages; i++)
        {
-         /* check for player created from custom element as extended target */
+         // check for player created from custom element as extended target
          content =
            element_info[element].change_page[i].target_content.e[xx][yy];
 
@@ -4208,162 +4282,139 @@ 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
-  /* 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) !!! */
+  // !!! 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();
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+  {
+    InitGameEngine_MM();
   }
   else
   {
-    DrawLevel();
+    DrawLevel(REDRAW_FIELD);
     DrawAllPlayers();
 
-    /* after drawing the level, correct some elements */
+    // after drawing the level, correct some elements
     if (game.timegate_time_left == 0)
       CloseAllOpenTimegates();
+  }
 
-    /* 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);
+  // blit playfield from scroll buffer to normal back buffer for fading in
+  BlitScreenToBitmap(backbuffer);
+  // !!! FIX THIS (END) !!!
 
-    redraw_mask |= REDRAW_FROM_BACKBUFFER;
-  }
-  /* !!! FIX THIS (END) !!! */
+  DrawMaskedBorder(fade_mask);
 
-#if 1
-  FadeIn(REDRAW_FIELD);
-#else
-  if (do_fading)
-    FadeIn(REDRAW_FIELD);
+  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 */
-    BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+    // copy default game door content to main double buffer
+
+    // !!! CHECK AGAIN !!!
+    SetPanelBackground();
+    // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+    DrawBackground(DX, DY, DXSIZE, DYSIZE);
   }
 
   SetPanelBackground();
   SetDrawBackgroundMask(REDRAW_DOOR_1);
 
-#if 1
   UpdateAndDisplayGameControlValues();
-#else
-  UpdateGameDoorValues();
-  DrawGameDoorValues();
-#endif
 
   if (!game.restart_level)
   {
     UnmapGameButtons();
     UnmapTapeButtons();
-    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;
+
+    FreeGameButtons();
+    CreateGameButtons();
+
     MapGameButtons();
     MapTapeButtons();
 
-    /* copy actual game door content to door double buffer for OpenDoor() */
-    BlitBitmap(drawto, bitmap_db_door,
-              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+    // copy actual game door content to door double buffer for OpenDoor()
+    BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
 
     OpenDoor(DOOR_OPEN_ALL);
 
-    PlaySound(SND_GAME_STARTING);
-
-    if (setup.sound_music)
-      PlayLevelMusic();
-
     KeyboardAutoRepeatOffUnlessAutoplay();
 
-    if (options.debug)
-    {
-      for (i = 0; i < MAX_PLAYERS; i++)
-       printf("Player %d %sactive.\n",
-              i + 1, (stored_player[i].active ? "" : "not "));
-    }
+#if DEBUG_INIT_PLAYER
+    DebugPrintPlayerStatus("Player status (final)");
+#endif
   }
 
-#if 1
   UnmapAllGadgets();
 
   MapGameButtons();
   MapTapeButtons();
-#endif
+
+  if (!game.restart_level && !tape.playing)
+  {
+    LevelStats_incPlayed(level_nr);
+
+    SaveLevelSetup_SeriesInfo();
+  }
 
   game.restart_level = FALSE;
+  game.restart_game_message = NULL;
+  game.request_active = FALSE;
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    InitGameActions_MM();
+
+  SaveEngineSnapshotToListInitial();
+
+  if (!game.restart_level)
+  {
+    PlaySound(SND_GAME_STARTING);
+
+    if (setup.sound_music)
+      PlayLevelMusic();
+  }
 }
 
-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 */
+  // this is used for non-R'n'D game engines to update certain engine values
 
-  /* needed to determine if sounds are played within the visible screen area */
+  // 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)
 {
-  int i, element = Feld[x][y];
+  int i, element = Tile[x][y];
   static int xy[4][2] =
   {
     {  0, +1 },
@@ -4384,7 +4435,7 @@ void InitMovDir(int x, int y)
     case EL_BUG_UP:
     case EL_BUG_LEFT:
     case EL_BUG_DOWN:
-      Feld[x][y] = EL_BUG;
+      Tile[x][y] = EL_BUG;
       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
       break;
 
@@ -4392,7 +4443,7 @@ void InitMovDir(int x, int y)
     case EL_SPACESHIP_UP:
     case EL_SPACESHIP_LEFT:
     case EL_SPACESHIP_DOWN:
-      Feld[x][y] = EL_SPACESHIP;
+      Tile[x][y] = EL_SPACESHIP;
       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
       break;
 
@@ -4400,7 +4451,7 @@ void InitMovDir(int x, int y)
     case EL_BD_BUTTERFLY_UP:
     case EL_BD_BUTTERFLY_LEFT:
     case EL_BD_BUTTERFLY_DOWN:
-      Feld[x][y] = EL_BD_BUTTERFLY;
+      Tile[x][y] = EL_BD_BUTTERFLY;
       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
       break;
 
@@ -4408,7 +4459,7 @@ void InitMovDir(int x, int y)
     case EL_BD_FIREFLY_UP:
     case EL_BD_FIREFLY_LEFT:
     case EL_BD_FIREFLY_DOWN:
-      Feld[x][y] = EL_BD_FIREFLY;
+      Tile[x][y] = EL_BD_FIREFLY;
       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
       break;
 
@@ -4416,7 +4467,7 @@ void InitMovDir(int x, int y)
     case EL_PACMAN_UP:
     case EL_PACMAN_LEFT:
     case EL_PACMAN_DOWN:
-      Feld[x][y] = EL_PACMAN;
+      Tile[x][y] = EL_PACMAN;
       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
       break;
 
@@ -4424,7 +4475,7 @@ void InitMovDir(int x, int y)
     case EL_YAMYAM_RIGHT:
     case EL_YAMYAM_UP:
     case EL_YAMYAM_DOWN:
-      Feld[x][y] = EL_YAMYAM;
+      Tile[x][y] = EL_YAMYAM;
       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
       break;
 
@@ -4440,10 +4491,16 @@ void InitMovDir(int x, int y)
     case EL_MOLE_RIGHT:
     case EL_MOLE_UP:
     case EL_MOLE_DOWN:
-      Feld[x][y] = EL_MOLE;
+      Tile[x][y] = EL_MOLE;
       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
       break;
 
+    case EL_SPRING_LEFT:
+    case EL_SPRING_RIGHT:
+      Tile[x][y] = EL_SPRING;
+      MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
+      break;
+
     default:
       if (IS_CUSTOM_ELEMENT(element))
       {
@@ -4479,7 +4536,7 @@ void InitMovDir(int x, int y)
        else if (move_pattern == MV_ALONG_LEFT_SIDE ||
                 move_pattern == MV_ALONG_RIGHT_SIDE)
        {
-         /* use random direction as default start direction */
+         // use random direction as default start direction
          if (game.engine_version >= VERSION_IDENT(3,1,0,0))
            MovDir[x][y] = 1 << RND(4);
 
@@ -4540,7 +4597,7 @@ void InitMovDir(int x, int y)
 void InitAmoebaNr(int x, int y)
 {
   int i;
-  int group_nr = AmoebeNachbarNr(x, y);
+  int group_nr = AmoebaNeighbourNr(x, y);
 
   if (group_nr == 0)
   {
@@ -4559,120 +4616,148 @@ void InitAmoebaNr(int x, int y)
   AmoebaCnt2[group_nr]++;
 }
 
-static void PlayerWins(struct PlayerInfo *player)
+static void LevelSolved(void)
 {
-  player->LevelSolved = TRUE;
-  player->GameOver = TRUE;
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
+      game.players_still_needed > 0)
+    return;
 
-  player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
-                        level.native_em_level->lev->score : player->score);
+  game.LevelSolved = TRUE;
+  game.GameOver = TRUE;
 
-  player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
-  player->LevelSolved_CountingScore = player->score_final;
+  game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                     game_em.lev->score :
+                     level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+                     game_mm.score :
+                     game.score);
+  game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+                      MM_HEALTH(game_mm.laser_overload_value) :
+                      game.health);
+
+  game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
+  game.LevelSolved_CountingScore = game.score_final;
+  game.LevelSolved_CountingHealth = game.health_final;
 }
 
-void GameWon()
+void GameWon(void)
 {
+  static int time_count_steps;
   static int time, time_final;
   static int score, score_final;
+  static int health, health_final;
   static int game_over_delay_1 = 0;
   static int game_over_delay_2 = 0;
+  static int game_over_delay_3 = 0;
   int game_over_delay_value_1 = 50;
-  int game_over_delay_value_2 = 50;
+  int game_over_delay_value_2 = 25;
+  int game_over_delay_value_3 = 50;
 
-  if (!local_player->LevelSolved_GameWon)
+  if (!game.LevelSolved_GameWon)
   {
     int i;
 
-    /* do not start end game actions before the player stops moving (to exit) */
-    if (local_player->MovPos)
+    // do not start end game actions before the player stops moving (to exit)
+    if (local_player->active && local_player->MovPos)
       return;
 
-    local_player->LevelSolved_GameWon = TRUE;
-    local_player->LevelSolved_SaveTape = tape.recording;
-    local_player->LevelSolved_SaveScore = !tape.playing;
+    game.LevelSolved_GameWon = TRUE;
+    game.LevelSolved_SaveTape = tape.recording;
+    game.LevelSolved_SaveScore = !tape.playing;
+
+    if (!tape.playing)
+    {
+      LevelStats_incSolved(level_nr);
+
+      SaveLevelSetup_SeriesInfo();
+    }
 
-    if (tape.auto_play)                /* tape might already be stopped here */
+    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;
+    game_over_delay_1 = 0;
+    game_over_delay_2 = 0;
+    game_over_delay_3 = game_over_delay_value_3;
 
-    time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
-    score = score_final = local_player->score_final;
+    time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
+    score = score_final = game.score_final;
+    health = health_final = game.health_final;
 
-    if (TimeLeft > 0)
-    {
-      time_final = 0;
-      score_final += TimeLeft * level.score[SC_TIME_BONUS];
-    }
-    else if (level.time == 0 && TimePlayed < 999)
+    if (level.score[SC_TIME_BONUS] > 0)
     {
-      time_final = 999;
-      score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
-    }
+      if (TimeLeft > 0)
+      {
+       time_final = 0;
+       score_final += TimeLeft * level.score[SC_TIME_BONUS];
+      }
+      else if (game.no_time_limit && TimePlayed < 999)
+      {
+       time_final = 999;
+       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
+      }
+
+      time_count_steps = MAX(1, ABS(time_final - time) / 100);
 
-    local_player->score_final = score_final;
+      game_over_delay_1 = game_over_delay_value_1;
+
+      if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+      {
+       health_final = 0;
+       score_final += health * level.score[SC_TIME_BONUS];
+
+       game_over_delay_2 = game_over_delay_value_2;
+      }
+
+      game.score_final = score_final;
+      game.health_final = health_final;
+    }
 
     if (level_editor_test_game)
     {
       time = time_final;
       score = score_final;
 
-#if 1
-      local_player->LevelSolved_CountingTime = time;
-      local_player->LevelSolved_CountingScore = score;
+      game.LevelSolved_CountingTime = time;
+      game.LevelSolved_CountingScore = score;
 
       game_panel_controls[GAME_PANEL_TIME].value = time;
       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)
     {
-      if (ExitX >= 0 && ExitY >= 0)    /* local player has left the level */
+      // check if last player has left the level
+      if (game.exit_x >= 0 &&
+         game.exit_y >= 0)
       {
-       /* close exit door after last player */
-       if ((AllPlayersGone &&
-            (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
-             Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
-             Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
-           Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
-           Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
+       int x = game.exit_x;
+       int y = game.exit_y;
+       int element = Tile[x][y];
+
+       // close exit door after last player
+       if ((game.all_players_gone &&
+            (element == EL_EXIT_OPEN ||
+             element == EL_SP_EXIT_OPEN ||
+             element == EL_STEEL_EXIT_OPEN)) ||
+           element == EL_EM_EXIT_OPEN ||
+           element == EL_EM_STEEL_EXIT_OPEN)
        {
-         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);
-         }
+         Tile[x][y] =
+           (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(x, y, element, ACTION_CLOSING);
        }
 
-       /* player disappears */
-       DrawLevelField(ExitX, ExitY);
+       // player disappears
+       DrawLevelField(x, y);
       }
 
       for (i = 0; i < MAX_PLAYERS; i++)
@@ -4683,7 +4768,7 @@ void GameWon()
        {
          RemovePlayer(player);
 
-         /* player disappears */
+         // player disappears
          DrawLevelField(player->jx, player->jy);
        }
       }
@@ -4703,23 +4788,20 @@ void GameWon()
   {
     int time_to_go = ABS(time_final - time);
     int time_count_dir = (time < time_final ? +1 : -1);
-    int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
+
+    if (time_to_go < time_count_steps)
+      time_count_steps = 1;
 
     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;
+    game.LevelSolved_CountingTime = time;
+    game.LevelSolved_CountingScore = score;
 
     game_panel_controls[GAME_PANEL_TIME].value = time;
     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);
@@ -4731,8 +4813,6 @@ void GameWon()
     return;
   }
 
-  local_player->LevelSolved_PanelOff = TRUE;
-
   if (game_over_delay_2 > 0)
   {
     game_over_delay_2--;
@@ -4740,55 +4820,77 @@ void GameWon()
     return;
   }
 
-#if 1
+  if (health != health_final)
+  {
+    int health_count_dir = (health < health_final ? +1 : -1);
+
+    health += health_count_dir;
+    score  += level.score[SC_TIME_BONUS];
+
+    game.LevelSolved_CountingHealth = health;
+    game.LevelSolved_CountingScore = score;
+
+    game_panel_controls[GAME_PANEL_HEALTH].value = health;
+    game_panel_controls[GAME_PANEL_SCORE].value = score;
+
+    DisplayGameControlValues();
+
+    if (health == health_final)
+      StopSound(SND_GAME_LEVELTIME_BONUS);
+    else if (setup.sound_loops)
+      PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
+    else
+      PlaySound(SND_GAME_LEVELTIME_BONUS);
+
+    return;
+  }
+
+  game.panel.active = FALSE;
+
+  if (game_over_delay_3 > 0)
+  {
+    game_over_delay_3--;
+
+    return;
+  }
+
   GameEnd();
-#endif
 }
 
-void GameEnd()
+void GameEnd(void)
 {
+  // used instead of "level_nr" (needed for network games)
+  int last_level_nr = levelset.level_nr;
   int hi_pos;
-  boolean raise_level = FALSE;
 
-  local_player->LevelSolved_GameEnd = TRUE;
+  game.LevelSolved_GameEnd = TRUE;
 
-  CloseDoor(DOOR_CLOSE_1);
-
-  if (local_player->LevelSolved_SaveTape)
+  if (game.LevelSolved_SaveTape)
   {
-#if 0
-    TapeStop();
-#endif
+    // make sure that request dialog to save tape does not open door again
+    if (!global.use_envelope_request)
+      CloseDoor(DOOR_CLOSE_1);
 
-#if 1
-    SaveTapeChecked(tape.level_nr);    /* ask to save tape */
-#else
-    SaveTape(tape.level_nr);           /* ask to save tape */
-#endif
+    SaveTapeChecked_LevelSolved(tape.level_nr);                // ask to save tape
   }
 
+  // if no tape is to be saved, close both doors simultaneously
+  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 (!game.LevelSolved_SaveScore)
   {
-#if 1
-    FadeOut(REDRAW_FIELD);
-#endif
-
-    game_status = GAME_MODE_MAIN;
+    SetGameStatus(GAME_MODE_MAIN);
 
-    DrawAndFadeInMainMenu(REDRAW_FIELD);
+    DrawMainMenu();
 
     return;
   }
@@ -4796,70 +4898,78 @@ void GameEnd()
   if (level_nr == leveldir_current->handicap_level)
   {
     leveldir_current->handicap_level++;
+
     SaveLevelSetup_SeriesInfo();
   }
 
-  if (level_nr < leveldir_current->last_level)
-    raise_level = TRUE;                        /* advance to next level */
-
-  if ((hi_pos = NewHiScore()) >= 0) 
+  if (setup.increment_levels &&
+      level_nr < leveldir_current->last_level &&
+      !network_playing)
   {
-    game_status = GAME_MODE_SCORES;
-
-    DrawHallOfFame(hi_pos);
+    level_nr++;                // advance to next level
+    TapeErase();       // start with empty tape
 
-    if (raise_level)
+    if (setup.auto_play_next_level)
     {
-      level_nr++;
-      TapeErase();
+      LoadLevel(level_nr);
+
+      SaveLevelSetup_SeriesInfo();
     }
   }
-  else
-  {
-#if 1
-    FadeOut(REDRAW_FIELD);
-#endif
 
-    game_status = GAME_MODE_MAIN;
+  hi_pos = NewHiScore(last_level_nr);
 
-    if (raise_level)
-    {
-      level_nr++;
-      TapeErase();
-    }
+  if (hi_pos >= 0 && !setup.skip_scores_after_game)
+  {
+    SetGameStatus(GAME_MODE_SCORES);
+
+    DrawHallOfFame(last_level_nr, hi_pos);
+  }
+  else if (setup.auto_play_next_level && setup.increment_levels &&
+          last_level_nr < leveldir_current->last_level &&
+          !network_playing)
+  {
+    StartGameActions(network.enabled, setup.autorecord, level.random_seed);
+  }
+  else
+  {
+    SetGameStatus(GAME_MODE_MAIN);
 
-    DrawAndFadeInMainMenu(REDRAW_FIELD);
+    DrawMainMenu();
   }
 }
 
-int NewHiScore()
+int NewHiScore(int level_nr)
 {
   int k, l;
   int position = -1;
+  boolean one_score_entry_per_name = !program.many_scores_per_name;
 
   LoadScore(level_nr);
 
   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
-      local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
+      game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
     return -1;
 
-  for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
+  for (k = 0; k < MAX_SCORE_ENTRIES; k++)
   {
-    if (local_player->score_final > highscore[k].Score)
+    if (game.score_final > highscore[k].Score)
     {
-      /* player has made it to the hall of fame */
+      // player has made it to the hall of fame
 
       if (k < MAX_SCORE_ENTRIES - 1)
       {
        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--)
        {
@@ -4868,22 +4978,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; 
+      highscore[k].Score = game.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
-
+      break;   // player already there with a higher score
   }
 
   if (position >= 0) 
@@ -4892,20 +4999,20 @@ int NewHiScore()
   return position;
 }
 
-inline static int getElementMoveStepsizeExt(int x, int y, int direction)
+static int getElementMoveStepsizeExt(int x, int y, int direction)
 {
-  int element = Feld[x][y];
+  int element = Tile[x][y];
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   int horiz_move = (dx != 0);
   int sign = (horiz_move ? dx : dy);
   int step = sign * element_info[element].move_stepsize;
 
-  /* special values for move stepsize for spring and things on conveyor belt */
+  // special values for move stepsize for spring and things on conveyor belt
   if (horiz_move)
   {
     if (CAN_FALL(element) &&
-       y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
+       y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
       step = sign * MOVE_STEPSIZE_NORMAL / 2;
     else if (element == EL_SPRING)
       step = sign * MOVE_STEPSIZE_NORMAL * 2;
@@ -4914,7 +5021,7 @@ inline static int getElementMoveStepsizeExt(int x, int y, int direction)
   return step;
 }
 
-inline static int getElementMoveStepsize(int x, int y)
+static int getElementMoveStepsize(int x, int y)
 {
   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
 }
@@ -4923,11 +5030,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;
@@ -4935,12 +5037,14 @@ 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];
+  // profiling showed that "autotest" spends 10~20% of its time in this function
+  if (DrawingDeactivatedField())
+    return;
+
+  int element = Tile[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;
@@ -4950,11 +5054,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)
 {
@@ -4962,9 +5062,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)
@@ -4972,72 +5070,43 @@ static void ResetRandomAnimationValue(int x, int y)
   GfxRandom[x][y] = INIT_GFX_RANDOM();
 }
 
-void InitMovingField(int x, int y, int direction)
+static void InitMovingField(int x, int y, int direction)
 {
-  int element = Feld[x][y];
+  int element = Tile[x][y];
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   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
+  // check if element was/is moving or being moved before/after mode change
   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
+  // 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 (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" */
+  // this is needed for CEs with property "can move" / "not moving"
 
   if (is_moving_after)
   {
-    if (Feld[newx][newy] == EL_EMPTY)
-      Feld[newx][newy] = EL_BLOCKED;
+    if (Tile[newx][newy] == EL_EMPTY)
+      Tile[newx][newy] = EL_BLOCKED;
 
     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];
@@ -5074,16 +5143,16 @@ void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
   *comes_from_y = oldy;
 }
 
-int MovingOrBlocked2Element(int x, int y)
+static int MovingOrBlocked2Element(int x, int y)
 {
-  int element = Feld[x][y];
+  int element = Tile[x][y];
 
   if (element == EL_BLOCKED)
   {
     int oldx, oldy;
 
     Blocked2Moving(x, y, &oldx, &oldy);
-    return Feld[oldx][oldy];
+    return Tile[oldx][oldy];
   }
   else
     return element;
@@ -5091,10 +5160,10 @@ int MovingOrBlocked2Element(int x, int y)
 
 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
 {
-  /* like MovingOrBlocked2Element(), but if element is moving
-     and (x,y) is the field the moving element is just leaving,
-     return EL_BLOCKED instead of the element value */
-  int element = Feld[x][y];
+  // like MovingOrBlocked2Element(), but if element is moving
+  // and (x,y) is the field the moving element is just leaving,
+  // return EL_BLOCKED instead of the element value
+  int element = Tile[x][y];
 
   if (IS_MOVING(x, y))
   {
@@ -5103,7 +5172,7 @@ static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
       int oldx, oldy;
 
       Blocked2Moving(x, y, &oldx, &oldy);
-      return Feld[oldx][oldy];
+      return Tile[oldx][oldy];
     }
     else
       return EL_BLOCKED;
@@ -5114,35 +5183,28 @@ static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
 
 static void RemoveField(int x, int y)
 {
-  Feld[x][y] = EL_EMPTY;
+  Tile[x][y] = EL_EMPTY;
 
   MovPos[x][y] = 0;
   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;
-  GfxRedraw[x][y] = GFX_REDRAW_NONE;
 }
 
-void RemoveMovingField(int x, int y)
+static void RemoveMovingField(int x, int y)
 {
   int oldx = x, oldy = y, newx = x, newy = y;
-  int element = Feld[x][y];
+  int element = Tile[x][y];
   int next_element = EL_UNDEFINED;
 
   if (element != EL_BLOCKED && !IS_MOVING(x, y))
@@ -5152,11 +5214,11 @@ void RemoveMovingField(int x, int y)
   {
     Moving2Blocked(x, y, &newx, &newy);
 
-    if (Feld[newx][newy] != EL_BLOCKED)
+    if (Tile[newx][newy] != EL_BLOCKED)
     {
-      /* element is moving, but target field is not free (blocked), but
-        already occupied by something different (example: acid pool);
-        in this case, only remove the moving field, but not the target */
+      // element is moving, but target field is not free (blocked), but
+      // already occupied by something different (example: acid pool);
+      // in this case, only remove the moving field, but not the target
 
       RemoveField(oldx, oldy);
 
@@ -5175,13 +5237,13 @@ void RemoveMovingField(int x, int y)
   }
 
   if (element == EL_BLOCKED &&
-      (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
-       Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
-       Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
-       Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
-       Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
-       Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
-    next_element = get_next_element(Feld[oldx][oldy]);
+      (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
+       Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
+       Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
+       Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
+       Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
+       Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
+    next_element = get_next_element(Tile[oldx][oldy]);
 
   RemoveField(oldx, oldy);
   RemoveField(newx, newy);
@@ -5189,7 +5251,7 @@ void RemoveMovingField(int x, int y)
   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
 
   if (next_element != EL_UNDEFINED)
-    Feld[oldx][oldy] = next_element;
+    Tile[oldx][oldy] = next_element;
 
   TEST_DrawLevelField(oldx, oldy);
   TEST_DrawLevelField(newx, newy);
@@ -5198,7 +5260,7 @@ void RemoveMovingField(int x, int y)
 void DrawDynamite(int x, int y)
 {
   int sx = SCREENX(x), sy = SCREENY(y);
-  int graphic = el2img(Feld[x][y]);
+  int graphic = el2img(Tile[x][y]);
   int frame;
 
   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
@@ -5220,9 +5282,9 @@ void DrawDynamite(int x, int y)
     DrawGraphic(sx, sy, graphic, frame);
 }
 
-void CheckDynamite(int x, int y)
+static void CheckDynamite(int x, int y)
 {
-  if (MovDelay[x][y] != 0)     /* dynamite is still waiting to explode */
+  if (MovDelay[x][y] != 0)     // dynamite is still waiting to explode
   {
     MovDelay[x][y]--;
 
@@ -5270,7 +5332,7 @@ static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
   }
 }
 
-static boolean checkIfAllPlayersFitToScreen_RND()
+static boolean checkIfAllPlayersFitToScreen_RND(void)
 {
   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
 
@@ -5290,172 +5352,100 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy)
   *sy = (sy1 + sy2) / 2;
 }
 
-void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
-                       boolean center_screen, boolean quick_relocation)
+static 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)))
   {
-    int offset = game.scroll_delay_value;
-
-    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 */
-
-       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_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-                   offset_x - MIDPOSX);
-
-       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 */
-
-      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);
-
-      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);
-
-      /* 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);
+    return;
+  }
 
-      /* 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);
-    }
+  if (!level.shifted_relocation || center_screen)
+  {
+    // relocation _with_ centering of screen
 
-    RedrawPlayfield(TRUE, 0,0,0,0);
+    new_scroll_x = SCROLL_POSITION_X(x);
+    new_scroll_y = SCROLL_POSITION_Y(y);
   }
   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);
+    // relocation _without_ centering of screen
 
-      int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                            old_y - MIDPOSY);
+    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);
 
-      int offset_x = x + (scroll_x - center_scroll_x);
-      int offset_y = y + (scroll_y - center_scroll_y);
+    // 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_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                  offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-                  offset_x - MIDPOSX);
+  if (quick_relocation)
+  {
+    // case 2: quick relocation (redraw without visible scrolling)
 
-      scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                  offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                  offset_y - MIDPOSY);
-    }
+    scroll_x = new_scroll_x;
+    scroll_y = new_scroll_y;
 
-#else
+    RedrawPlayfield();
 
-    /* visible relocation (with scrolling), with centering of screen */
+    return;
+  }
 
-    int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-                    x > SBX_Right + MIDPOSX ? SBX_Right :
-                    x - MIDPOSX);
+  // case 3: visible relocation (with scrolling to new position)
 
-    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
 
-    ScrollScreen(NULL, SCROLL_GO_ON);  /* scroll last frame to full tile */
+  SetVideoFrameDelay(wait_delay_value);
 
-    while (scroll_x != scroll_xx || scroll_y != scroll_yy)
-    {
-      int dx = 0, dy = 0;
-      int fx = FX, fy = FY;
+  while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
+  {
+    int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
+    int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
 
-      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;
 
-      if (dx == 0 && dy == 0)          /* no scrolling needed at all */
-       break;
+    scroll_x -= dx;
+    scroll_y -= dy;
 
-      scroll_x -= dx;
-      scroll_y -= dy;
+    // set values for horizontal/vertical screen scrolling (half tile size)
+    int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
+    int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
+    int pos_x = dx * TILEX / 2;
+    int pos_y = dy * TILEY / 2;
+    int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
+    int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
 
-      fx += dx * TILEX / 2;
-      fy += dy * TILEY / 2;
+    ScrollLevel(dx, dy);
+    DrawAllPlayers();
 
-      ScrollLevel(dx, dy);
-      DrawAllPlayers();
+    // scroll in two steps of half tile size to make things smoother
+    BlitScreenToBitmapExt_RND(window, fx, fy);
 
-      /* 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
+    BlitScreenToBitmap(window);
+  }
 
-      /* 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)
+static void RelocatePlayer(int jx, int jy, int el_player_raw)
 {
   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
   int player_nr = GET_PLAYER_NR(el_player);
@@ -5466,8 +5456,8 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
   int old_jx = player->jx;
   int old_jy = player->jy;
-  int old_element = Feld[old_jx][old_jy];
-  int element = Feld[jx][jy];
+  int old_element = Tile[old_jx][old_jy];
+  int element = Tile[jx][jy];
   boolean player_relocated = (old_jx != jx || old_jy != jy);
 
   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
@@ -5479,15 +5469,15 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   int enter_side = enter_side_horiz | enter_side_vert;
   int leave_side = leave_side_horiz | leave_side_vert;
 
-  if (player->GameOver)                /* do not reanimate dead player */
+  if (player->buried)          // do not reanimate dead player
     return;
 
-  if (!player_relocated)       /* no need to relocate the player */
+  if (!player_relocated)       // no need to relocate the player
     return;
 
-  if (IS_PLAYER(jx, jy))       /* player already placed at new position */
+  if (IS_PLAYER(jx, jy))       // player already placed at new position
   {
-    RemoveField(jx, jy);       /* temporarily remove newly placed player */
+    RemoveField(jx, jy);       // temporarily remove newly placed player
     DrawLevelField(jx, jy);
   }
 
@@ -5502,12 +5492,11 @@ 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 */
-    DrawLevelField(player->jx, player->jy);    /* remove player graphic */
+    DrawPlayer(player);                // needed here only to cleanup last field
+    DrawLevelField(player->jx, player->jy);    // remove player graphic
 
     player->is_moving = FALSE;
   }
@@ -5521,16 +5510,19 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
                                      CE_PLAYER_LEAVES_X,
                                      player->index_bit, leave_side);
 
-  Feld[jx][jy] = el_player;
+  Tile[jx][jy] = el_player;
   InitPlayerField(jx, jy, el_player, TRUE);
 
-  if (!ELEM_IS_PLAYER(element))        /* player may be set on walkable element */
+  /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
+     possible that the relocation target field did not contain a player element,
+     but a walkable element, to which the new player was relocated -- in this
+     case, restore that (already initialized!) element on the player field */
+  if (!ELEM_IS_PLAYER(element))        // player may be set on walkable element
   {
-    Feld[jx][jy] = element;
-    InitField(jx, jy, FALSE);
+    Tile[jx][jy] = element;    // restore previously existing element
   }
 
-  /* only visually relocate centered player */
+  // only visually relocate centered player
   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
                     FALSE, level.instant_relocation);
 
@@ -5543,15 +5535,28 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
 
   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
                                      player->index_bit, enter_side);
+
+  if (player->is_switching)
+  {
+    /* ensure that relocation while still switching an element does not cause
+       a new element to be treated as also switched directly after relocation
+       (this is important for teleporter switches that teleport the player to
+       a place where another teleporter switch is in the same direction, which
+       would then incorrectly be treated as immediately switched before the
+       direction key that caused the switch was released) */
+
+    player->switch_x += jx - old_jx;
+    player->switch_y += jy - old_jy;
+  }
 }
 
-void Explode(int ex, int ey, int phase, int mode)
+static void Explode(int ex, int ey, int phase, int mode)
 {
   int x, y;
   int last_phase;
   int border_element;
 
-  /* !!! eliminate this variable !!! */
+  // !!! eliminate this variable !!!
   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
 
   if (game.explosions_delayed)
@@ -5560,43 +5565,26 @@ void Explode(int ex, int ey, int phase, int mode)
     return;
   }
 
-  if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
+  if (phase == EX_PHASE_START)         // initialize 'Store[][]' field
   {
-    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
+    int center_element = Tile[ex][ey];
+    int artwork_element, explosion_element;    // set these values later
 
-#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 */
+    // remove things displayed in background while burning dynamite
     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
       Back[ex][ey] = 0;
 
     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
     {
-      /* put moving element to center field (and let it explode there) */
+      // put moving element to center field (and let it explode there)
       center_element = MovingOrBlocked2Element(ex, ey);
       RemoveMovingField(ex, ey);
-      Feld[ex][ey] = center_element;
+      Tile[ex][ey] = center_element;
     }
 
-    /* now "center_element" is finally determined -- set related values now */
-    artwork_element = center_element;          /* for custom player artwork */
-    explosion_element = center_element;                /* for custom player artwork */
+    // now "center_element" is finally determined -- set related values now
+    artwork_element = center_element;          // for custom player artwork
+    explosion_element = center_element;                // for custom player artwork
 
     if (IS_PLAYER(ex, ey))
     {
@@ -5611,12 +5599,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;
 
@@ -5631,7 +5617,7 @@ void Explode(int ex, int ey, int phase, int mode)
          (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
        continue;
 
-      element = Feld[x][y];
+      element = Tile[x][y];
 
       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
       {
@@ -5641,7 +5627,7 @@ void Explode(int ex, int ey, int phase, int mode)
          RemoveMovingField(x, y);
       }
 
-      /* indestructible elements can only explode in center (but not flames) */
+      // indestructible elements can only explode in center (but not flames)
       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
                                           mode == EX_TYPE_BORDER)) ||
          element == EL_FLAMES)
@@ -5650,7 +5636,7 @@ void Explode(int ex, int ey, int phase, int mode)
       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
         behaviour, for example when touching a yamyam that explodes to rocks
         with active deadly shield, a rock is created under the player !!! */
-      /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
+      // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
 #if 0
       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
          (game.engine_version < VERSION_IDENT(3,1,0,0) ||
@@ -5661,20 +5647,20 @@ void Explode(int ex, int ey, int phase, int mode)
       {
        if (IS_ACTIVE_BOMB(element))
        {
-         /* re-activate things under the bomb like gate or penguin */
-         Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
+         // re-activate things under the bomb like gate or penguin
+         Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
          Back[x][y] = 0;
        }
 
        continue;
       }
 
-      /* save walkable background elements while explosion on same tile */
+      // save walkable background elements while explosion on same tile
       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
          (x != ex || y != ey || mode == EX_TYPE_BORDER))
        Back[x][y] = element;
 
-      /* ignite explodable elements reached by other explosion */
+      // ignite explodable elements reached by other explosion
       if (element == EL_EXPLOSION)
        element = Store2[x][y];
 
@@ -5699,8 +5685,8 @@ void Explode(int ex, int ey, int phase, int mode)
          Store[x][y] = EL_EMPTY;
       }
 
-      /* !!! check this case -- currently needed for rnd_rado_negundo_v,
-        !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
+      // !!! check this case -- currently needed for rnd_rado_negundo_v,
+      // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
       else if (ELEM_IS_PLAYER(center_element))
        Store[x][y] = EL_EMPTY;
       else if (center_element == EL_YAMYAM)
@@ -5708,9 +5694,9 @@ void Explode(int ex, int ey, int phase, int mode)
       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
        Store[x][y] = element_info[center_element].content.e[xx][yy];
 #if 1
-      /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
-        (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
-        otherwise) -- FIX THIS !!! */
+      // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
+      // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
+      // otherwise) -- FIX THIS !!!
       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
        Store[x][y] = element_info[element].content.e[1][1];
 #else
@@ -5724,7 +5710,7 @@ void Explode(int ex, int ey, int phase, int mode)
          center_element == EL_AMOEBA_TO_DIAMOND)
        Store2[x][y] = element;
 
-      Feld[x][y] = EL_EXPLOSION;
+      Tile[x][y] = EL_EXPLOSION;
       GfxElement[x][y] = artwork_element;
 
       ExplodePhase[x][y] = 1;
@@ -5747,37 +5733,15 @@ void Explode(int ex, int ey, int phase, int mode)
   y = ey;
 
   if (phase == 1)
-    GfxFrame[x][y] = 0;                /* restart explosion animation */
+    GfxFrame[x][y] = 0;                // restart explosion animation
 
   last_phase = ExplodeDelay[x][y];
 
   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 */
+  // 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))
@@ -5796,21 +5760,21 @@ void Explode(int ex, int ey, int phase, int mode)
     }
     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
     {
-      Feld[x][y] = Store2[x][y];
+      Tile[x][y] = Store2[x][y];
       Store2[x][y] = 0;
       Bang(x, y);
       border_explosion = TRUE;
     }
     else if (border_element == EL_AMOEBA_TO_DIAMOND)
     {
-      AmoebeUmwandeln(x, y);
+      AmoebaToDiamond(x, y);
       Store2[x][y] = 0;
       border_explosion = TRUE;
     }
 
-    /* if an element just explodes due to another explosion (chain-reaction),
-       do not immediately end the new explosion when it was the last frame of
-       the explosion (as it would be done in the following "if"-statement!) */
+    // if an element just explodes due to another explosion (chain-reaction),
+    // do not immediately end the new explosion when it was the last frame of
+    // the explosion (as it would be done in the following "if"-statement!)
     if (border_explosion && phase == last_phase)
       return;
   }
@@ -5819,11 +5783,11 @@ void Explode(int ex, int ey, int phase, int mode)
   {
     int element;
 
-    element = Feld[x][y] = Store[x][y];
+    element = Tile[x][y] = Store[x][y];
     Store[x][y] = Store2[x][y] = 0;
     GfxElement[x][y] = EL_UNDEFINED;
 
-    /* player can escape from explosions and might therefore be still alive */
+    // player can escape from explosions and might therefore be still alive
     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
        element <= EL_PLAYER_IS_EXPLODING_4)
     {
@@ -5835,13 +5799,13 @@ void Explode(int ex, int ey, int phase, int mode)
       if (level.use_explosion_element[player_nr])
        explosion_element = level.explosion_element[player_nr];
 
-      Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
+      Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
                    element_info[explosion_element].content.e[xx][yy]);
     }
 
-    /* restore probably existing indestructible background element */
+    // restore probably existing indestructible background element
     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
-      element = Feld[x][y] = Back[x][y];
+      element = Tile[x][y] = Back[x][y];
     Back[x][y] = 0;
 
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
@@ -5849,9 +5813,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);
 
@@ -5860,7 +5822,7 @@ void Explode(int ex, int ey, int phase, int mode)
     TestIfElementTouchesCustomElement(x, y);
 
     if (GFX_CRUMBLED(element))
-      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
       StorePlayer[x][y] = 0;
@@ -5874,7 +5836,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
 
     if (phase == delay)
-      TEST_DrawLevelFieldCrumbledSand(x, y);
+      TEST_DrawLevelFieldCrumbled(x, y);
 
     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
@@ -5891,10 +5853,10 @@ void Explode(int ex, int ey, int phase, int mode)
   }
 }
 
-void DynaExplode(int ex, int ey)
+static void DynaExplode(int ex, int ey)
 {
   int i, j;
-  int dynabomb_element = Feld[ex][ey];
+  int dynabomb_element = Tile[ex][ey];
   int dynabomb_size = 1;
   boolean dynabomb_xl = FALSE;
   struct PlayerInfo *player;
@@ -5924,12 +5886,12 @@ void DynaExplode(int ex, int ey)
       int y = ey + j * xy[i][1];
       int element;
 
-      if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
+      if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
        break;
 
-      element = Feld[x][y];
+      element = Tile[x][y];
 
-      /* do not restart explosions of fields with active bombs */
+      // do not restart explosions of fields with active bombs
       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
        continue;
 
@@ -5951,8 +5913,7 @@ void Bang(int x, int y)
   {
     struct PlayerInfo *player = PLAYERINFO(x, y);
 
-    element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
-                           player->element_nr);
+    element = Tile[x][y] = player->initial_element;
 
     if (level.use_explosion_element[player->index_nr])
     {
@@ -5990,10 +5951,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;
 
@@ -6001,7 +5958,7 @@ void Bang(int x, int y)
     case EL_LAMP:
     case EL_LAMP_ACTIVE:
     case EL_AMOEBA_TO_DIAMOND:
-      if (!IS_PLAYER(x, y))    /* penguin and player may be at same field */
+      if (!IS_PLAYER(x, y))    // penguin and player may be at same field
        explosion_type = EX_TYPE_CENTER;
       break;
 
@@ -6021,22 +5978,22 @@ void Bang(int x, int y)
   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
 }
 
-void SplashAcid(int x, int y)
+static void SplashAcid(int x, int y)
 {
   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
       (!IN_LEV_FIELD(x - 1, y - 2) ||
        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
-    Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
+    Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
 
   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
       (!IN_LEV_FIELD(x + 1, y - 2) ||
        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
-    Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
+    Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
 
   PlayLevelSound(x, y, SND_ACID_SPLASHING);
 }
 
-static void InitBeltMovement()
+static void InitBeltMovement(void)
 {
   static int belt_base_element[4] =
   {
@@ -6055,7 +6012,7 @@ static void InitBeltMovement()
 
   int x, y, i, j;
 
-  /* set frame order for belt animation graphic according to belt direction */
+  // set frame order for belt animation graphic according to belt direction
   for (i = 0; i < NUM_BELTS; i++)
   {
     int belt_nr = i;
@@ -6081,7 +6038,7 @@ static void InitBeltMovement()
 
   SCAN_PLAYFIELD(x, y)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
 
     for (i = 0; i < NUM_BELTS; i++)
     {
@@ -6092,9 +6049,9 @@ static void InitBeltMovement()
 
        if (e_belt_nr == belt_nr)
        {
-         int belt_part = Feld[x][y] - belt_base_element[belt_nr];
+         int belt_part = Tile[x][y] - belt_base_element[belt_nr];
 
-         Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
+         Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
        }
       }
     }
@@ -6132,7 +6089,7 @@ static void ToggleBeltSwitch(int x, int y)
     MV_NONE,
   };
 
-  int element = Feld[x][y];
+  int element = Tile[x][y];
   int belt_nr = getBeltNrFromBeltSwitchElement(element);
   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
   int belt_dir = belt_move_dir[belt_dir_nr];
@@ -6147,7 +6104,7 @@ static void ToggleBeltSwitch(int x, int y)
   if (belt_dir_nr == 3)
     belt_dir_nr = 1;
 
-  /* set frame order for belt animation graphic according to belt direction */
+  // set frame order for belt animation graphic according to belt direction
   for (i = 0; i < NUM_BELT_PARTS; i++)
   {
     int element = belt_base_active_element[belt_nr] + i;
@@ -6168,7 +6125,7 @@ static void ToggleBeltSwitch(int x, int y)
 
   SCAN_PLAYFIELD(xx, yy)
   {
-    int element = Feld[xx][yy];
+    int element = Tile[xx][yy];
 
     if (IS_BELT_SWITCH(element))
     {
@@ -6176,7 +6133,7 @@ static void ToggleBeltSwitch(int x, int y)
 
       if (e_belt_nr == belt_nr)
       {
-       Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
+       Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
        TEST_DrawLevelField(xx, yy);
       }
     }
@@ -6186,9 +6143,9 @@ static void ToggleBeltSwitch(int x, int y)
 
       if (e_belt_nr == belt_nr)
       {
-       int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
+       int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
 
-       Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
+       Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
        TEST_DrawLevelField(xx, yy);
       }
     }
@@ -6198,9 +6155,9 @@ static void ToggleBeltSwitch(int x, int y)
 
       if (e_belt_nr == belt_nr)
       {
-       int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
+       int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
 
-       Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
+       Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
        TEST_DrawLevelField(xx, yy);
       }
     }
@@ -6215,54 +6172,39 @@ static void ToggleSwitchgateSwitch(int x, int y)
 
   SCAN_PLAYFIELD(xx, yy)
   {
-    int element = Feld[xx][yy];
+    int element = Tile[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;
+      Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
       TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
     {
-      Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
+      Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
       TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
     {
-      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
+      Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
       TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
-      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
+      Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
       TEST_DrawLevelField(xx, yy);
     }
-#endif
     else if (element == EL_SWITCHGATE_OPEN ||
             element == EL_SWITCHGATE_OPENING)
     {
-      Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
+      Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
 
       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
     }
     else if (element == EL_SWITCHGATE_CLOSED ||
             element == EL_SWITCHGATE_CLOSING)
     {
-      Feld[xx][yy] = EL_SWITCHGATE_OPENING;
+      Tile[xx][yy] = EL_SWITCHGATE_OPENING;
 
       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
     }
@@ -6285,36 +6227,36 @@ static int getInvisibleFromInvisibleActiveElement(int element)
          element);
 }
 
-static void RedrawAllLightSwitchesAndInvisibleElements()
+static void RedrawAllLightSwitchesAndInvisibleElements(void)
 {
   int x, y;
 
   SCAN_PLAYFIELD(x, y)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
 
     if (element == EL_LIGHT_SWITCH &&
        game.light_time_left > 0)
     {
-      Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
+      Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
       TEST_DrawLevelField(x, y);
     }
     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
             game.light_time_left == 0)
     {
-      Feld[x][y] = EL_LIGHT_SWITCH;
+      Tile[x][y] = EL_LIGHT_SWITCH;
       TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER &&
             game.light_time_left > 0)
     {
-      Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+      Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
       TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.light_time_left == 0)
     {
-      Feld[x][y] = EL_EMC_DRIPPER;
+      Tile[x][y] = EL_EMC_DRIPPER;
       TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
@@ -6322,48 +6264,48 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
             element == EL_INVISIBLE_SAND)
     {
       if (game.light_time_left > 0)
-       Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+       Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
       TEST_DrawLevelField(x, y);
 
-      /* uncrumble neighbour fields, if needed */
+      // uncrumble neighbour fields, if needed
       if (element == EL_INVISIBLE_SAND)
-       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
             element == EL_INVISIBLE_SAND_ACTIVE)
     {
       if (game.light_time_left == 0)
-       Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+       Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
       TEST_DrawLevelField(x, y);
 
-      /* re-crumble neighbour fields, if needed */
+      // re-crumble neighbour fields, if needed
       if (element == EL_INVISIBLE_SAND)
-       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
   }
 }
 
-static void RedrawAllInvisibleElementsForLenses()
+static void RedrawAllInvisibleElementsForLenses(void)
 {
   int x, y;
 
   SCAN_PLAYFIELD(x, y)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
 
     if (element == EL_EMC_DRIPPER &&
        game.lenses_time_left > 0)
     {
-      Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+      Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
       TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.lenses_time_left == 0)
     {
-      Feld[x][y] = EL_EMC_DRIPPER;
+      Tile[x][y] = EL_EMC_DRIPPER;
       TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
@@ -6371,71 +6313,75 @@ static void RedrawAllInvisibleElementsForLenses()
             element == EL_INVISIBLE_SAND)
     {
       if (game.lenses_time_left > 0)
-       Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+       Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
       TEST_DrawLevelField(x, y);
 
-      /* uncrumble neighbour fields, if needed */
+      // uncrumble neighbour fields, if needed
       if (element == EL_INVISIBLE_SAND)
-       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
             element == EL_INVISIBLE_SAND_ACTIVE)
     {
       if (game.lenses_time_left == 0)
-       Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+       Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
       TEST_DrawLevelField(x, y);
 
-      /* re-crumble neighbour fields, if needed */
+      // re-crumble neighbour fields, if needed
       if (element == EL_INVISIBLE_SAND)
-       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
   }
 }
 
-static void RedrawAllInvisibleElementsForMagnifier()
+static void RedrawAllInvisibleElementsForMagnifier(void)
 {
   int x, y;
 
   SCAN_PLAYFIELD(x, y)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
 
     if (element == EL_EMC_FAKE_GRASS &&
        game.magnify_time_left > 0)
     {
-      Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
+      Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
       TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
             game.magnify_time_left == 0)
     {
-      Feld[x][y] = EL_EMC_FAKE_GRASS;
+      Tile[x][y] = EL_EMC_FAKE_GRASS;
       TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY(element) &&
             game.magnify_time_left > 0)
     {
-      Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
+      Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
                    element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
                    IS_EM_GATE_GRAY(element) ?
                    element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
                    IS_EMC_GATE_GRAY(element) ?
                    element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+                   IS_DC_GATE_GRAY(element) ?
+                   EL_DC_GATE_WHITE_GRAY_ACTIVE :
                    element);
       TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY_ACTIVE(element) &&
             game.magnify_time_left == 0)
     {
-      Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
+      Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
                    element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
                    IS_EM_GATE_GRAY_ACTIVE(element) ?
                    element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
                    IS_EMC_GATE_GRAY_ACTIVE(element) ?
                    element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+                   IS_DC_GATE_GRAY_ACTIVE(element) ?
+                   EL_DC_GATE_WHITE_GRAY :
                    element);
       TEST_DrawLevelField(x, y);
     }
@@ -6444,7 +6390,7 @@ static void RedrawAllInvisibleElementsForMagnifier()
 
 static void ToggleLightSwitch(int x, int y)
 {
-  int element = Feld[x][y];
+  int element = Tile[x][y];
 
   game.light_time_left =
     (element == EL_LIGHT_SWITCH ?
@@ -6461,71 +6407,67 @@ static void ActivateTimegateSwitch(int x, int y)
 
   SCAN_PLAYFIELD(xx, yy)
   {
-    int element = Feld[xx][yy];
+    int element = Tile[xx][yy];
 
     if (element == EL_TIMEGATE_CLOSED ||
        element == EL_TIMEGATE_CLOSING)
     {
-      Feld[xx][yy] = EL_TIMEGATE_OPENING;
+      Tile[xx][yy] = EL_TIMEGATE_OPENING;
       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
     }
 
     /*
     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
     {
-      Feld[xx][yy] = EL_TIMEGATE_SWITCH;
+      Tile[xx][yy] = EL_TIMEGATE_SWITCH;
       TEST_DrawLevelField(xx, yy);
     }
     */
 
   }
 
-#if 1
-  Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
+  Tile[x][y] = (Tile[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)
+static void Impact(int x, int y)
 {
   boolean last_line = (y == lev_fieldy - 1);
   boolean object_hit = FALSE;
   boolean impact = (last_line || object_hit);
-  int element = Feld[x][y];
+  int element = Tile[x][y];
   int smashed = EL_STEELWALL;
 
-  if (!last_line)      /* check if element below was hit */
+  if (!last_line)      // check if element below was hit
   {
-    if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
+    if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
       return;
 
     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
                                         MovDir[x][y + 1] != MV_DOWN ||
                                         MovPos[x][y + 1] <= TILEY / 2));
 
-    /* do not smash moving elements that left the smashed field in time */
+    // do not smash moving elements that left the smashed field in time
     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
        ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
       object_hit = FALSE;
 
 #if USE_QUICKSAND_IMPACT_BUGFIX
-    if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
+    if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
     {
       RemoveMovingField(x, y + 1);
-      Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
-      Feld[x][y + 2] = EL_ROCK;
+      Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
+      Tile[x][y + 2] = EL_ROCK;
       TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
     }
 
-    if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
+    if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
     {
       RemoveMovingField(x, y + 1);
-      Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
-      Feld[x][y + 2] = EL_ROCK;
+      Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
+      Tile[x][y + 2] = EL_ROCK;
       TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
@@ -6538,14 +6480,14 @@ void Impact(int x, int y)
     impact = (last_line || object_hit);
   }
 
-  if (!last_line && smashed == EL_ACID)        /* element falls into acid */
+  if (!last_line && smashed == EL_ACID)        // element falls into acid
   {
     SplashAcid(x, y + 1);
     return;
   }
 
-  /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
-  /* only reset graphic animation if graphic really changes after impact */
+  // !!! not sufficient for all cases -- see EL_PEARL below !!!
+  // only reset graphic animation if graphic really changes after impact
   if (impact &&
       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
   {
@@ -6563,7 +6505,7 @@ void Impact(int x, int y)
   {
     ResetGfxAnimation(x, y);
 
-    Feld[x][y] = EL_PEARL_BREAKING;
+    Tile[x][y] = EL_PEARL_BREAKING;
     PlayLevelSound(x, y, SND_PEARL_BREAKING);
     return;
   }
@@ -6582,7 +6524,7 @@ void Impact(int x, int y)
       Bang(x, y + 1);
     else
     {
-      Feld[x][y] = EL_AMOEBA_GROWING;
+      Tile[x][y] = EL_AMOEBA_GROWING;
       Store[x][y] = EL_AMOEBA_WET;
 
       ResetRandomAnimationValue(x, y);
@@ -6590,7 +6532,7 @@ void Impact(int x, int y)
     return;
   }
 
-  if (object_hit)              /* check which object was hit */
+  if (object_hit)              // check which object was hit
   {
     if ((CAN_PASS_MAGIC_WALL(element) && 
         (smashed == EL_MAGIC_WALL ||
@@ -6604,11 +6546,11 @@ void Impact(int x, int y)
         smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
         EL_DC_MAGIC_WALL_ACTIVE);
 
-      /* activate magic wall / mill */
+      // activate magic wall / mill
       SCAN_PLAYFIELD(xx, yy)
       {
-       if (Feld[xx][yy] == smashed)
-         Feld[xx][yy] = activated_magic_wall;
+       if (Tile[xx][yy] == smashed)
+         Tile[xx][yy] = activated_magic_wall;
       }
 
       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
@@ -6674,7 +6616,7 @@ void Impact(int x, int y)
        }
        else if (smashed == EL_NUT)
        {
-         Feld[x][y + 1] = EL_NUT_BREAKING;
+         Tile[x][y + 1] = EL_NUT_BREAKING;
          PlayLevelSound(x, y, SND_NUT_BREAKING);
          RaiseScoreElement(EL_NUT);
          return;
@@ -6683,13 +6625,13 @@ void Impact(int x, int y)
        {
          ResetGfxAnimation(x, y);
 
-         Feld[x][y + 1] = EL_PEARL_BREAKING;
+         Tile[x][y + 1] = EL_PEARL_BREAKING;
          PlayLevelSound(x, y, SND_PEARL_BREAKING);
          return;
        }
        else if (smashed == EL_DIAMOND)
        {
-         Feld[x][y + 1] = EL_DIAMOND_BREAKING;
+         Tile[x][y + 1] = EL_DIAMOND_BREAKING;
          PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
          return;
        }
@@ -6711,10 +6653,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,
@@ -6730,28 +6668,28 @@ void Impact(int x, int y)
     }
   }
 
-  /* play sound of magic wall / mill */
+  // play sound of magic wall / mill
   if (!last_line &&
-      (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
-       Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
-       Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
+      (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+       Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
+       Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
   {
-    if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
+    if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
-    else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
+    else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
-    else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+    else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
 
     return;
   }
 
-  /* play sound of object that hits the ground */
+  // play sound of object that hits the ground
   if (last_line || object_hit)
     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
 }
 
-inline static void TurnRoundExt(int x, int y)
+static void TurnRoundExt(int x, int y)
 {
   static struct
   {
@@ -6782,7 +6720,7 @@ inline static void TurnRoundExt(int x, int y)
     { MV_RIGHT,        MV_LEFT,        MV_UP    }
   };
 
-  int element = Feld[x][y];
+  int element = Tile[x][y];
   int move_pattern = element_info[element].move_pattern;
 
   int old_move_dir = MovDir[x][y];
@@ -6812,7 +6750,7 @@ inline static void TurnRoundExt(int x, int y)
 
     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
       MovDelay[x][y] = 9;
-    else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
+    else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
       MovDelay[x][y] = 1;
   }
   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
@@ -6826,7 +6764,7 @@ inline static void TurnRoundExt(int x, int y)
 
     if (element == EL_SPACESHIP        && MovDir[x][y] != old_move_dir)
       MovDelay[x][y] = 9;
-    else if (element == EL_BD_FIREFLY)     /* && MovDir[x][y] == right_dir) */
+    else if (element == EL_BD_FIREFLY)     // && MovDir[x][y] == right_dir)
       MovDelay[x][y] = 1;
   }
   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
@@ -6950,7 +6888,7 @@ inline static void TurnRoundExt(int x, int y)
     yy = y + move_xy[MovDir[x][y]].dy;
 
     if (!IN_LEV_FIELD(xx, yy) ||
-        (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
+        (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
       MovDir[x][y] = old_move_dir;
 
     MovDelay[x][y] = 0;
@@ -6986,17 +6924,17 @@ inline static void TurnRoundExt(int x, int y)
   {
     boolean can_move_on =
       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
-                           IS_AMOEBOID(Feld[move_x][move_y]) ||
-                           Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
+                           IS_AMOEBOID(Tile[move_x][move_y]) ||
+                           Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
     if (!can_move_on)
     {
       boolean can_turn_left =
        (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
-                             IS_AMOEBOID(Feld[left_x][left_y])));
+                             IS_AMOEBOID(Tile[left_x][left_y])));
 
       boolean can_turn_right =
        (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
-                             IS_AMOEBOID(Feld[right_x][right_y])));
+                             IS_AMOEBOID(Tile[right_x][right_y])));
 
       if (can_turn_left && can_turn_right)
        MovDir[x][y] = (RND(2) ? left_dir : right_dir);
@@ -7016,13 +6954,12 @@ 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) &&
          !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
       {
-       Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
+       Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
        ResetGfxAnimation(move_x, move_y);
        TEST_DrawLevelField(move_x, move_y);
 
@@ -7032,12 +6969,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;
   }
@@ -7048,10 +6979,10 @@ inline static void TurnRoundExt(int x, int y)
   {
     int attr_x = -1, attr_y = -1;
 
-    if (AllPlayersGone)
+    if (game.all_players_gone)
     {
-      attr_x = ExitX;
-      attr_y = ExitY;
+      attr_x = game.exit_x;
+      attr_y = game.exit_y;
     }
     else
     {
@@ -7074,12 +7005,14 @@ inline static void TurnRoundExt(int x, int y)
       }
     }
 
-    if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
-       (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
+    if (element == EL_ROBOT &&
+       game.robot_wheel_x >= 0 &&
+       game.robot_wheel_y >= 0 &&
+       (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
         game.engine_version < VERSION_IDENT(3,1,0,0)))
     {
-      attr_x = ZX;
-      attr_y = ZY;
+      attr_x = game.robot_wheel_x;
+      attr_y = game.robot_wheel_y;
     }
 
     if (element == EL_PENGUIN)
@@ -7098,10 +7031,10 @@ inline static void TurnRoundExt(int x, int y)
        int ex = x + xy[i][0];
        int ey = y + xy[i][1];
 
-       if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
-                                    Feld[ex][ey] == EL_EM_EXIT_OPEN ||
-                                    Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
-                                    Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
+       if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
+                                    Tile[ex][ey] == EL_EM_EXIT_OPEN ||
+                                    Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
+                                    Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
        {
          attr_x = ex;
          attr_y = ey;
@@ -7112,13 +7045,13 @@ inline static void TurnRoundExt(int x, int y)
 
     MovDir[x][y] = MV_NONE;
     if (attr_x < x)
-      MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
+      MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
     else if (attr_x > x)
-      MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
+      MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
     if (attr_y < y)
-      MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
+      MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
     else if (attr_y > y)
-      MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
+      MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
 
     if (element == EL_ROBOT)
     {
@@ -7195,22 +7128,22 @@ inline static void TurnRoundExt(int x, int y)
     {
       static int check_pos[16] =
       {
-       -1,             /*  0 => (invalid)          */
-       7,              /*  1 => MV_LEFT            */
-       3,              /*  2 => MV_RIGHT           */
-       -1,             /*  3 => (invalid)          */
-       1,              /*  4 =>            MV_UP   */
-       0,              /*  5 => MV_LEFT  | MV_UP   */
-       2,              /*  6 => MV_RIGHT | MV_UP   */
-       -1,             /*  7 => (invalid)          */
-       5,              /*  8 =>            MV_DOWN */
-       6,              /*  9 => MV_LEFT  | MV_DOWN */
-       4,              /* 10 => MV_RIGHT | MV_DOWN */
-       -1,             /* 11 => (invalid)          */
-       -1,             /* 12 => (invalid)          */
-       -1,             /* 13 => (invalid)          */
-       -1,             /* 14 => (invalid)          */
-       -1,             /* 15 => (invalid)          */
+       -1,             //  0 => (invalid)
+       7,              //  1 => MV_LEFT
+       3,              //  2 => MV_RIGHT
+       -1,             //  3 => (invalid)
+       1,              //  4 =>            MV_UP
+       0,              //  5 => MV_LEFT  | MV_UP
+       2,              //  6 => MV_RIGHT | MV_UP
+       -1,             //  7 => (invalid)
+       5,              //  8 =>            MV_DOWN
+       6,              //  9 => MV_LEFT  | MV_DOWN
+       4,              // 10 => MV_RIGHT | MV_DOWN
+       -1,             // 11 => (invalid)
+       -1,             // 12 => (invalid)
+       -1,             // 13 => (invalid)
+       -1,             // 14 => (invalid)
+       -1,             // 15 => (invalid)
       };
       static struct
       {
@@ -7231,7 +7164,7 @@ inline static void TurnRoundExt(int x, int y)
       boolean can_clone = FALSE;
       int i;
 
-      /* check if there is any free field around current position */
+      // check if there is any free field around current position
       for (i = 0; i < 8; i++)
       {
        int newx = x + check_xy[i].dx;
@@ -7245,7 +7178,7 @@ inline static void TurnRoundExt(int x, int y)
        }
       }
 
-      if (can_clone)           /* randomly find an element to clone */
+      if (can_clone)           // randomly find an element to clone
       {
        can_clone = FALSE;
 
@@ -7264,7 +7197,7 @@ inline static void TurnRoundExt(int x, int y)
            element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
            element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
 
-           Store[x][y] = Feld[newx][newy];
+           Store[x][y] = Tile[newx][newy];
 
            can_clone = TRUE;
 
@@ -7273,7 +7206,7 @@ inline static void TurnRoundExt(int x, int y)
        }
       }
 
-      if (can_clone)           /* randomly find a direction to move */
+      if (can_clone)           // randomly find a direction to move
       {
        can_clone = FALSE;
 
@@ -7300,17 +7233,17 @@ inline static void TurnRoundExt(int x, int y)
        }
       }
 
-      if (can_clone)           /* cloning and moving successful */
+      if (can_clone)           // cloning and moving successful
        return;
 
-      /* cannot clone -- try to move towards player */
+      // cannot clone -- try to move towards player
 
       start_pos = check_pos[MovDir[x][y] & 0x0f];
       check_order = (RND(2) ? -1 : +1);
 
       for (i = 0; i < 3; i++)
       {
-       /* first check start_pos, then previous/next or (next/previous) pos */
+       // first check start_pos, then previous/next or (next/previous) pos
        int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
        int pos = (pos_raw + 8) % 8;
        int newx = x + check_xy[pos].dx;
@@ -7342,7 +7275,7 @@ inline static void TurnRoundExt(int x, int y)
     boolean can_turn_right =
       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
 
-    if (element_info[element].move_stepsize == 0)      /* "not moving" */
+    if (element_info[element].move_stepsize == 0)      // "not moving"
       return;
 
     if (move_pattern == MV_TURNING_LEFT)
@@ -7417,10 +7350,10 @@ inline static void TurnRoundExt(int x, int y)
     int newx, newy;
     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
 
-    if (AllPlayersGone)
+    if (game.all_players_gone)
     {
-      attr_x = ExitX;
-      attr_y = ExitY;
+      attr_x = game.exit_x;
+      attr_y = game.exit_y;
     }
     else
     {
@@ -7460,7 +7393,7 @@ inline static void TurnRoundExt(int x, int y)
       boolean first_horiz = RND(2);
       int new_move_dir = MovDir[x][y];
 
-      if (element_info[element].move_stepsize == 0)    /* "not moving" */
+      if (element_info[element].move_stepsize == 0)    // "not moving"
       {
        first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
        MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
@@ -7516,7 +7449,7 @@ inline static void TurnRoundExt(int x, int y)
       MV_RIGHT,
     };
     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
-    int move_preference = -1000000;    /* start with very low preference */
+    int move_preference = -1000000;    // start with very low preference
     int new_move_dir = MV_NONE;
     int start_test = RND(4);
     int i;
@@ -7530,7 +7463,7 @@ inline static void TurnRoundExt(int x, int y)
       yy = y + test_xy[start_test + i][1];
 
       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
-         (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
+         (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
       {
        new_move_dir = move_dir;
 
@@ -7546,14 +7479,14 @@ inline static void TurnRoundExt(int x, int y)
 
       if (move_dir_preference > move_preference)
       {
-       /* prefer field that has not been visited for the longest time */
+       // prefer field that has not been visited for the longest time
        move_preference = move_dir_preference;
        new_move_dir = move_dir;
       }
       else if (move_dir_preference == move_preference &&
               move_dir == old_move_dir)
       {
-       /* prefer last direction when all directions are preferred equally */
+       // prefer last direction when all directions are preferred equally
        move_preference = move_dir_preference;
        new_move_dir = move_dir;
       }
@@ -7579,7 +7512,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)
@@ -7603,10 +7536,10 @@ static boolean JustBeingPushed(int x, int y)
   return FALSE;
 }
 
-void StartMoving(int x, int y)
+static void StartMoving(int x, int y)
 {
-  boolean started_moving = FALSE;      /* some elements can fall _and_ move */
-  int element = Feld[x][y];
+  boolean started_moving = FALSE;      // some elements can fall _and_ move
+  int element = Tile[x][y];
 
   if (Stop[x][y])
     return;
@@ -7628,7 +7561,7 @@ void StartMoving(int x, int y)
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
 
-       Feld[x][y] = EL_QUICKSAND_EMPTYING;
+       Tile[x][y] = EL_QUICKSAND_EMPTYING;
 #if USE_QUICKSAND_BD_ROCK_BUGFIX
        if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
          Store[x][y] = EL_ROCK;
@@ -7638,7 +7571,7 @@ void StartMoving(int x, int y)
 
        PlayLevelSoundAction(x, y, ACTION_EMPTYING);
       }
-      else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+      else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
       {
        if (!MovDelay[x][y])
        {
@@ -7658,14 +7591,14 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_QUICKSAND_EMPTY;
-       Feld[x][y + 1] = EL_QUICKSAND_FULL;
+       Tile[x][y] = EL_QUICKSAND_EMPTY;
+       Tile[x][y + 1] = EL_QUICKSAND_FULL;
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 
        PlayLevelSoundAction(x, y, ACTION_FILLING);
       }
-      else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+      else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
       {
        if (!MovDelay[x][y])
        {
@@ -7685,8 +7618,8 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_QUICKSAND_EMPTY;
-       Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+       Tile[x][y] = EL_QUICKSAND_EMPTY;
+       Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 
@@ -7700,7 +7633,7 @@ void StartMoving(int x, int y)
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
 
-       Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
+       Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
 #if USE_QUICKSAND_BD_ROCK_BUGFIX
        if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
          Store[x][y] = EL_ROCK;
@@ -7710,7 +7643,7 @@ void StartMoving(int x, int y)
 
        PlayLevelSoundAction(x, y, ACTION_EMPTYING);
       }
-      else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+      else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
       {
        if (!MovDelay[x][y])
        {
@@ -7730,14 +7663,14 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
-       Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+       Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
+       Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 
        PlayLevelSoundAction(x, y, ACTION_FILLING);
       }
-      else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+      else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
       {
        if (!MovDelay[x][y])
        {
@@ -7757,8 +7690,8 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
-       Feld[x][y + 1] = EL_QUICKSAND_FULL;
+       Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
+       Tile[x][y + 1] = EL_QUICKSAND_FULL;
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 
@@ -7766,23 +7699,23 @@ void StartMoving(int x, int y)
       }
     }
     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
-            Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+            Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
     {
       InitMovingField(x, y, MV_DOWN);
       started_moving = TRUE;
 
-      Feld[x][y] = EL_QUICKSAND_FILLING;
+      Tile[x][y] = EL_QUICKSAND_FILLING;
       Store[x][y] = element;
 
       PlayLevelSoundAction(x, y, ACTION_FILLING);
     }
     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
-            Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+            Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
     {
       InitMovingField(x, y, MV_DOWN);
       started_moving = TRUE;
 
-      Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
+      Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
       Store[x][y] = element;
 
       PlayLevelSoundAction(x, y, ACTION_FILLING);
@@ -7794,13 +7727,13 @@ void StartMoving(int x, int y)
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
 
-       Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
+       Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
        Store[x][y] = EL_CHANGED(Store[x][y]);
       }
-      else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
+      else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -7809,8 +7742,8 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
-       Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
+       Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
+       Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
        Store[x][y + 1] = EL_CHANGED(Store[x][y]);
        Store[x][y] = 0;
       }
@@ -7822,13 +7755,13 @@ void StartMoving(int x, int y)
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
 
-       Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
+       Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
        Store[x][y] = EL_CHANGED_BD(Store[x][y]);
       }
-      else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
+      else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -7837,8 +7770,8 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
-       Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
+       Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
+       Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
        Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
        Store[x][y] = 0;
       }
@@ -7850,13 +7783,13 @@ void StartMoving(int x, int y)
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
 
-       Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
+       Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
        Store[x][y] = EL_CHANGED_DC(Store[x][y]);
       }
-      else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+      else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -7865,29 +7798,29 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
-       Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
+       Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
+       Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
        Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
        Store[x][y] = 0;
       }
     }
     else if ((CAN_PASS_MAGIC_WALL(element) &&
-             (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
-              Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
+             (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+              Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
             (CAN_PASS_DC_MAGIC_WALL(element) &&
-             (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
+             (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
 
     {
       InitMovingField(x, y, MV_DOWN);
       started_moving = TRUE;
 
-      Feld[x][y] =
-       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
-        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
+      Tile[x][y] =
+       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
+        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
         EL_DC_MAGIC_WALL_FILLING);
       Store[x][y] = element;
     }
-    else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
+    else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
     {
       SplashAcid(x, y + 1);
 
@@ -7897,20 +7830,15 @@ 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))) ||
+             (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
 
             (game.engine_version < VERSION_IDENT(2,2,0,7) &&
              CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
-             (Feld[x][y + 1] == EL_BLOCKED)))
+             (Tile[x][y + 1] == EL_BLOCKED)))
     {
       /* this is needed for a special case not covered by calling "Impact()"
         from "ContinueMoving()": if an element moves to a tile directly below
@@ -7935,9 +7863,9 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
       }
     }
-    else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
+    else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
     {
-      if (WasJustFalling[x][y])        /* prevent animation from being restarted */
+      if (WasJustFalling[x][y])        // prevent animation from being restarted
        MovDir[x][y] = MV_DOWN;
 
       InitMovingField(x, y, MV_DOWN);
@@ -7945,25 +7873,24 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_AMOEBA_DROP)
     {
-      Feld[x][y] = EL_AMOEBA_GROWING;
+      Tile[x][y] = EL_AMOEBA_GROWING;
       Store[x][y] = EL_AMOEBA_WET;
     }
-    else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
-             (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
+    else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
+             (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
             !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
             element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
     {
       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
                                (IS_FREE(x - 1, y + 1) ||
-                                Feld[x - 1][y + 1] == EL_ACID));
+                                Tile[x - 1][y + 1] == EL_ACID));
       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
                                (IS_FREE(x + 1, y + 1) ||
-                                Feld[x + 1][y + 1] == EL_ACID));
+                                Tile[x + 1][y + 1] == EL_ACID));
       boolean can_fall_any  = (can_fall_left || can_fall_right);
       boolean can_fall_both = (can_fall_left && can_fall_right);
-      int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
+      int slippery_type = element_info[Tile[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)
@@ -7978,75 +7905,29 @@ 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)
-         can_fall_right = FALSE;       /* slip down on left side */
-       else
-         can_fall_left = !(can_fall_right = RND(2));
-
-       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 */
+         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)
       {
-       /* if not determined otherwise, prefer left side for slipping down */
+       // if not determined otherwise, prefer left side for slipping down
        InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
        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
+    else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
     {
       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
-      int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
+      int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
       int belt_dir = game.belt_dir[belt_nr];
 
       if ((belt_dir == MV_LEFT  && left_is_free) ||
@@ -8064,32 +7945,17 @@ void StartMoving(int x, int y)
       }
       else
       {
-       MovDir[x][y] = 0;       /* if element was moving, stop it */
+       MovDir[x][y] = 0;       // if element was moving, stop it
       }
     }
   }
 
-  /* 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
+  // not "else if" because of elements that can fall and move (EL_SPRING)
   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))
@@ -8103,14 +7969,14 @@ void StartMoving(int x, int y)
 
       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
 
-      if (Feld[x][y] != element)       /* element has changed */
+      if (Tile[x][y] != element)       // element has changed
        return;
     }
 
-    if (!MovDelay[x][y])       /* start new movement phase */
+    if (!MovDelay[x][y])       // start new movement phase
     {
-      /* all objects that can change their move direction after each step
-        (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
+      // all objects that can change their move direction after each step
+      // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
 
       if (element != EL_YAMYAM &&
          element != EL_DARK_YAMYAM &&
@@ -8133,7 +7999,7 @@ void StartMoving(int x, int y)
       }
     }
 
-    if (MovDelay[x][y])                /* wait some time before next movement */
+    if (MovDelay[x][y])                // wait some time before next movement
     {
       MovDelay[x][y]--;
 
@@ -8175,48 +8041,38 @@ void StartMoving(int x, int y)
          int sy = SCREENY(yy);
          int flame_graphic = graphic + (i - 1);
 
-         if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
+         if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
            break;
 
          if (MovDelay[x][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;
 
-           Feld[xx][yy] = EL_FLAMES;
+           Tile[xx][yy] = EL_FLAMES;
 
            if (IN_SCR_FIELD(sx, sy))
            {
-             TEST_DrawLevelFieldCrumbledSand(xx, yy);
+             TEST_DrawLevelFieldCrumbled(xx, yy);
              DrawGraphic(sx, sy, flame_graphic, frame);
            }
          }
          else
          {
-           if (Feld[xx][yy] == EL_FLAMES)
-             Feld[xx][yy] = EL_EMPTY;
+           if (Tile[xx][yy] == EL_FLAMES)
+             Tile[xx][yy] = EL_EMPTY;
            TEST_DrawLevelField(xx, yy);
          }
        }
       }
 
-      if (MovDelay[x][y])      /* element still has to wait some time */
+      if (MovDelay[x][y])      // element still has to wait some time
       {
        PlayLevelSoundAction(x, y, ACTION_WAITING);
 
@@ -8224,9 +8080,9 @@ void StartMoving(int x, int y)
       }
     }
 
-    /* now make next step */
+    // now make next step
 
-    Moving2Blocked(x, y, &newx, &newy);        /* get next screen position */
+    Moving2Blocked(x, y, &newx, &newy);        // get next screen position
 
     if (DONT_COLLIDE_WITH(element) &&
        IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
@@ -8238,7 +8094,7 @@ void StartMoving(int x, int y)
     }
 
     else if (CAN_MOVE_INTO_ACID(element) &&
-            IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
+            IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
             !IS_MV_DIAGONAL(MovDir[x][y]) &&
             (MovDir[x][y] == MV_DOWN ||
              game.engine_version >= VERSION_IDENT(3,1,0,0)))
@@ -8248,10 +8104,10 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
     {
-      if (Feld[newx][newy] == EL_EXIT_OPEN ||
-         Feld[newx][newy] == EL_EM_EXIT_OPEN ||
-         Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
-         Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
+      if (Tile[newx][newy] == EL_EXIT_OPEN ||
+         Tile[newx][newy] == EL_EM_EXIT_OPEN ||
+         Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
+         Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
       {
        RemoveField(x, y);
        TEST_DrawLevelField(x, y);
@@ -8260,14 +8116,15 @@ void StartMoving(int x, int y)
        if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
          DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
 
-       local_player->friends_still_needed--;
-       if (!local_player->friends_still_needed &&
-           !local_player->GameOver && AllPlayersGone)
-         PlayerWins(local_player);
+       game.friends_still_needed--;
+       if (!game.friends_still_needed &&
+           !game.GameOver &&
+           game.all_players_gone)
+         LevelSolved();
 
        return;
       }
-      else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
+      else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
       {
        if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
          TEST_DrawLevelField(newx, newy);
@@ -8288,13 +8145,13 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
     {
-      if (IS_FOOD_PIG(Feld[newx][newy]))
+      if (IS_FOOD_PIG(Tile[newx][newy]))
       {
        if (IS_MOVING(newx, newy))
          RemoveMovingField(newx, newy);
        else
        {
-         Feld[newx][newy] = EL_EMPTY;
+         Tile[newx][newy] = EL_EMPTY;
          TEST_DrawLevelField(newx, newy);
        }
 
@@ -8317,10 +8174,10 @@ void StartMoving(int x, int y)
        boolean can_clone = FALSE;
        int xx, yy;
 
-       /* check if element to clone is still there */
+       // check if element to clone is still there
        for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
        {
-         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
+         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
          {
            can_clone = TRUE;
 
@@ -8328,7 +8185,7 @@ void StartMoving(int x, int y)
          }
        }
 
-       /* cannot clone or target field not free anymore -- do not clone */
+       // cannot clone or target field not free anymore -- do not clone
        if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
          Store[x][y] = EL_EMPTY;
       }
@@ -8342,7 +8199,7 @@ void StartMoving(int x, int y)
          int change_delay = 8;
          int graphic;
 
-         /* android is moving diagonally */
+         // android is moving diagonally
 
          CreateField(x, y, EL_DIAGONAL_SHRINKING);
 
@@ -8358,7 +8215,7 @@ void StartMoving(int x, int y)
          DrawLevelGraphicAnimation(x, y, graphic);
          PlayLevelSoundAction(x, y, ACTION_SHRINKING);
 
-         if (Feld[newx][newy] == EL_ACID)
+         if (Tile[newx][newy] == EL_ACID)
          {
            SplashAcid(newx, newy);
 
@@ -8383,7 +8240,7 @@ void StartMoving(int x, int y)
        }
        else
        {
-         Feld[newx][newy] = EL_EMPTY;
+         Tile[newx][newy] = EL_EMPTY;
          TEST_DrawLevelField(newx, newy);
 
          PlayLevelSoundAction(x, y, ACTION_DIGGING);
@@ -8391,83 +8248,19 @@ 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)
       {
        RunnerVisit[x][y] = FrameCounter;
-       PlayerVisit[x][y] /= 8;         /* expire player visit path */
+       PlayerVisit[x][y] /= 8;         // expire player visit path
       }
     }
     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
@@ -8510,134 +8303,97 @@ 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;
-         }
+         Tile[newx][newy] = EL_FLAMES;
+         if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
+           Tile[newx1][newy1] = EL_FLAMES;
+         if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
+           Tile[newx2][newy2] = EL_FLAMES;
 
          return;
        }
       }
     }
     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
-            Feld[newx][newy] == EL_DIAMOND)
+            Tile[newx][newy] == EL_DIAMOND)
     {
       if (IS_MOVING(newx, newy))
        RemoveMovingField(newx, newy);
       else
       {
-       Feld[newx][newy] = EL_EMPTY;
+       Tile[newx][newy] = EL_EMPTY;
        TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
     }
     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
-            IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
+            IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
     {
       if (AmoebaNr[newx][newy])
       {
        AmoebaCnt2[AmoebaNr[newx][newy]]--;
-       if (Feld[newx][newy] == EL_AMOEBA_FULL ||
-           Feld[newx][newy] == EL_BD_AMOEBA)
+       if (Tile[newx][newy] == EL_AMOEBA_FULL ||
+           Tile[newx][newy] == EL_BD_AMOEBA)
          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;
+       Tile[newx][newy] = EL_EMPTY;
        TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
     }
     else if ((element == EL_PACMAN || element == EL_MOLE)
-            && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
+            && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
     {
       if (AmoebaNr[newx][newy])
       {
        AmoebaCnt2[AmoebaNr[newx][newy]]--;
-       if (Feld[newx][newy] == EL_AMOEBA_FULL ||
-           Feld[newx][newy] == EL_BD_AMOEBA)
+       if (Tile[newx][newy] == EL_AMOEBA_FULL ||
+           Tile[newx][newy] == EL_BD_AMOEBA)
          AmoebaCnt[AmoebaNr[newx][newy]]--;
       }
 
       if (element == EL_MOLE)
       {
-       Feld[newx][newy] = EL_AMOEBA_SHRINKING;
+       Tile[newx][newy] = EL_AMOEBA_SHRINKING;
        PlayLevelSound(x, y, SND_MOLE_DIGGING);
 
        ResetGfxAnimation(x, y);
        GfxAction[x][y] = ACTION_DIGGING;
        TEST_DrawLevelField(x, y);
 
-       MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
+       MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
 
-       return;                         /* wait for shrinking amoeba */
+       return;                         // wait for shrinking amoeba
       }
-      else     /* element == EL_PACMAN */
+      else     // element == EL_PACMAN
       {
-       Feld[newx][newy] = EL_EMPTY;
+       Tile[newx][newy] = EL_EMPTY;
        TEST_DrawLevelField(newx, newy);
        PlayLevelSound(x, y, SND_PACMAN_DIGGING);
       }
     }
     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
-            (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
-             (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
+            (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
+             (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
     {
-      /* wait for shrinking amoeba to completely disappear */
+      // wait for shrinking amoeba to completely disappear
       return;
     }
     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
     {
-      /* object was running against a wall */
+      // object was running against a wall
 
       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) !!! */
+      if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
        DrawLevelElementAnimation(x, y, element);
 
       if (DONT_TOUCH(element))
@@ -8657,7 +8413,7 @@ void StartMoving(int x, int y)
 
 void ContinueMoving(int x, int y)
 {
-  int element = Feld[x][y];
+  int element = Tile[x][y];
   struct ElementInfo *ei = &element_info[element];
   int direction = MovDir[x][y];
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
@@ -8671,129 +8427,112 @@ void ContinueMoving(int x, int y)
 
   MovPos[x][y] += getElementMoveStepsize(x, y);
 
-  if (pushed_by_player)        /* special case: moving object pushed by player */
+  if (pushed_by_player)        // special case: moving object pushed by player
     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
 
   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 */
+    return;    // element is still moving
   }
 
-  /* element reached destination field */
+  // element reached destination field
 
-  Feld[x][y] = EL_EMPTY;
-  Feld[newx][newy] = element;
-  MovPos[x][y] = 0;    /* force "not moving" for "crumbled sand" */
+  Tile[x][y] = EL_EMPTY;
+  Tile[newx][newy] = element;
+  MovPos[x][y] = 0;    // force "not moving" for "crumbled sand"
 
-  if (Store[x][y] == EL_ACID)  /* element is moving into acid pool */
+  if (Store[x][y] == EL_ACID)  // element is moving into acid pool
   {
-    element = Feld[newx][newy] = EL_ACID;
+    element = Tile[newx][newy] = EL_ACID;
   }
   else if (element == EL_MOLE)
   {
-    Feld[x][y] = EL_SAND;
+    Tile[x][y] = EL_SAND;
 
-    TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+    TEST_DrawLevelFieldCrumbledNeighbours(x, y);
   }
   else if (element == EL_QUICKSAND_FILLING)
   {
-    element = Feld[newx][newy] = get_next_element(element);
+    element = Tile[newx][newy] = get_next_element(element);
     Store[newx][newy] = Store[x][y];
   }
   else if (element == EL_QUICKSAND_EMPTYING)
   {
-    Feld[x][y] = get_next_element(element);
-    element = Feld[newx][newy] = Store[x][y];
+    Tile[x][y] = get_next_element(element);
+    element = Tile[newx][newy] = Store[x][y];
   }
   else if (element == EL_QUICKSAND_FAST_FILLING)
   {
-    element = Feld[newx][newy] = get_next_element(element);
+    element = Tile[newx][newy] = get_next_element(element);
     Store[newx][newy] = Store[x][y];
   }
   else if (element == EL_QUICKSAND_FAST_EMPTYING)
   {
-    Feld[x][y] = get_next_element(element);
-    element = Feld[newx][newy] = Store[x][y];
+    Tile[x][y] = get_next_element(element);
+    element = Tile[newx][newy] = Store[x][y];
   }
   else if (element == EL_MAGIC_WALL_FILLING)
   {
-    element = Feld[newx][newy] = get_next_element(element);
+    element = Tile[newx][newy] = get_next_element(element);
     if (!game.magic_wall_active)
-      element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
+      element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
     Store[newx][newy] = Store[x][y];
   }
   else if (element == EL_MAGIC_WALL_EMPTYING)
   {
-    Feld[x][y] = get_next_element(element);
+    Tile[x][y] = get_next_element(element);
     if (!game.magic_wall_active)
-      Feld[x][y] = EL_MAGIC_WALL_DEAD;
-    element = Feld[newx][newy] = Store[x][y];
+      Tile[x][y] = EL_MAGIC_WALL_DEAD;
+    element = Tile[newx][newy] = Store[x][y];
 
-#if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
-#endif
   }
   else if (element == EL_BD_MAGIC_WALL_FILLING)
   {
-    element = Feld[newx][newy] = get_next_element(element);
+    element = Tile[newx][newy] = get_next_element(element);
     if (!game.magic_wall_active)
-      element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
+      element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
     Store[newx][newy] = Store[x][y];
   }
   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
   {
-    Feld[x][y] = get_next_element(element);
+    Tile[x][y] = get_next_element(element);
     if (!game.magic_wall_active)
-      Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
-    element = Feld[newx][newy] = Store[x][y];
+      Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
+    element = Tile[newx][newy] = Store[x][y];
 
-#if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
-#endif
   }
   else if (element == EL_DC_MAGIC_WALL_FILLING)
   {
-    element = Feld[newx][newy] = get_next_element(element);
+    element = Tile[newx][newy] = get_next_element(element);
     if (!game.magic_wall_active)
-      element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
+      element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
     Store[newx][newy] = Store[x][y];
   }
   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
   {
-    Feld[x][y] = get_next_element(element);
+    Tile[x][y] = get_next_element(element);
     if (!game.magic_wall_active)
-      Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
-    element = Feld[newx][newy] = Store[x][y];
+      Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
+    element = Tile[newx][newy] = Store[x][y];
 
-#if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
-#endif
   }
   else if (element == EL_AMOEBA_DROPPING)
   {
-    Feld[x][y] = get_next_element(element);
-    element = Feld[newx][newy] = Store[x][y];
+    Tile[x][y] = get_next_element(element);
+    element = Tile[newx][newy] = Store[x][y];
   }
   else if (element == EL_SOKOBAN_OBJECT)
   {
     if (Back[x][y])
-      Feld[x][y] = Back[x][y];
+      Tile[x][y] = Back[x][y];
 
     if (Back[newx][newy])
-      Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
+      Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
 
     Back[x][y] = Back[newx][newy] = 0;
   }
@@ -8807,80 +8546,57 @@ void ContinueMoving(int x, int y)
 
   if (CAN_CHANGE_OR_HAS_ACTION(element))
   {
-    /* copy element change control values to new field */
+    // copy element change control values to new field
     ChangeDelay[newx][newy] = ChangeDelay[x][y];
     ChangePage[newx][newy]  = ChangePage[x][y];
     ChangeCount[newx][newy] = ChangeCount[x][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 */
+  // copy animation control values to new field
   GfxFrame[newx][newy]  = GfxFrame[x][y];
-  GfxRandom[newx][newy] = GfxRandom[x][y];     /* keep same random value */
-  GfxAction[newx][newy] = GfxAction[x][y];     /* keep action one frame  */
-  GfxDir[newx][newy]    = GfxDir[x][y];                /* keep element direction */
+  GfxRandom[newx][newy] = GfxRandom[x][y];     // keep same random value
+  GfxAction[newx][newy] = GfxAction[x][y];     // keep action one frame
+  GfxDir[newx][newy]    = GfxDir[x][y];                // keep element direction
 
   Pushed[x][y] = Pushed[newx][newy] = FALSE;
 
-  /* some elements can leave other elements behind after moving */
-#if 1
+  // some elements can leave other elements behind after moving
   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 */
+    // 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;
+    Tile[x][y] = move_leave_element;
 
-    if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+    if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
       MovDir[x][y] = direction;
 
     InitField(x, y, FALSE);
 
-    if (GFX_CRUMBLED(Feld[x][y]))
-      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+    if (GFX_CRUMBLED(Tile[x][y]))
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
     if (ELEM_IS_PLAYER(move_leave_element))
       RelocatePlayer(x, y, move_leave_element);
   }
 
-  /* do this after checking for left-behind element */
-  ResetGfxAnimation(x, y);     /* reset animation values for old field */
+  // do this after checking for left-behind element
+  ResetGfxAnimation(x, y);     // reset animation values for old field
 
   if (!CAN_MOVE(element) ||
       (CAN_FALL(element) && direction == MV_DOWN &&
@@ -8892,15 +8608,15 @@ void ContinueMoving(int x, int y)
   TEST_DrawLevelField(x, y);
   TEST_DrawLevelField(newx, newy);
 
-  Stop[newx][newy] = TRUE;     /* ignore this element until the next frame */
+  Stop[newx][newy] = TRUE;     // ignore this element until the next frame
 
-  /* prevent pushed element from moving on in pushed direction */
+  // prevent pushed element from moving on in pushed direction
   if (pushed_by_player && CAN_MOVE(element) &&
       element_info[element].move_pattern & MV_ANY_DIRECTION &&
       !(element_info[element].move_pattern & direction))
     TurnRound(newx, newy);
 
-  /* prevent elements on conveyor belt from moving on in last direction */
+  // prevent elements on conveyor belt from moving on in last direction
   if (pushed_by_conveyor && CAN_FALL(element) &&
       direction & MV_HORIZONTAL)
     MovDir[newx][newy] = 0;
@@ -8918,13 +8634,11 @@ 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 */
+  if (DONT_TOUCH(element))     // object may be nasty to player or others
   {
     TestIfBadThingTouchesPlayer(newx, newy);
     TestIfBadThingTouchesFriend(newx, newy);
@@ -8935,7 +8649,12 @@ void ContinueMoving(int x, int y)
   else if (element == EL_PENGUIN)
     TestIfFriendTouchesBadThing(newx, newy);
 
-  /* give the player one last chance (one more frame) to move away */
+  if (DONT_GET_HIT_BY(element))
+  {
+    TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
+  }
+
+  // give the player one last chance (one more frame) to move away
   if (CAN_FALL(element) && direction == MV_DOWN &&
       (last_line || (!IS_FREE(x, newy + 1) &&
                     (!IS_PLAYER(x, newy + 1) ||
@@ -8953,35 +8672,12 @@ void ContinueMoving(int x, int y)
                                        player->index_bit, push_side);
   }
 
-  if (element == EL_EMC_ANDROID && pushed_by_player)   /* make another move */
+  if (element == EL_EMC_ANDROID && pushed_by_player)   // make another move
     MovDelay[newx][newy] = 1;
 
   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
-
+  TestIfElementTouchesCustomElement(x, y);     // empty or new element
   TestIfElementHitsCustomElement(newx, newy, direction);
   TestIfPlayerTouchesCustomElement(newx, newy);
   TestIfElementTouchesCustomElement(newx, newy);
@@ -8992,10 +8688,10 @@ void ContinueMoving(int x, int y)
                             MV_DIR_OPPOSITE(direction));
 }
 
-int AmoebeNachbarNr(int ax, int ay)
+int AmoebaNeighbourNr(int ax, int ay)
 {
   int i;
-  int element = Feld[ax][ay];
+  int element = Tile[ax][ay];
   int group_nr = 0;
   static int xy[4][2] =
   {
@@ -9013,14 +8709,14 @@ int AmoebeNachbarNr(int ax, int ay)
     if (!IN_LEV_FIELD(x, y))
       continue;
 
-    if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
+    if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
       group_nr = AmoebaNr[x][y];
   }
 
   return group_nr;
 }
 
-void AmoebenVereinigen(int ax, int ay)
+static void AmoebaMerge(int ax, int ay)
 {
   int i, x, y, xx, yy;
   int new_group_nr = AmoebaNr[ax][ay];
@@ -9043,9 +8739,9 @@ void AmoebenVereinigen(int ax, int ay)
     if (!IN_LEV_FIELD(x, y))
       continue;
 
-    if ((Feld[x][y] == EL_AMOEBA_FULL ||
-        Feld[x][y] == EL_BD_AMOEBA ||
-        Feld[x][y] == EL_AMOEBA_DEAD) &&
+    if ((Tile[x][y] == EL_AMOEBA_FULL ||
+        Tile[x][y] == EL_BD_AMOEBA ||
+        Tile[x][y] == EL_AMOEBA_DEAD) &&
        AmoebaNr[x][y] != new_group_nr)
     {
       int old_group_nr = AmoebaNr[x][y];
@@ -9067,29 +8763,30 @@ void AmoebenVereinigen(int ax, int ay)
   }
 }
 
-void AmoebeUmwandeln(int ax, int ay)
+void AmoebaToDiamond(int ax, int ay)
 {
   int i, x, y;
 
-  if (Feld[ax][ay] == EL_AMOEBA_DEAD)
+  if (Tile[ax][ay] == EL_AMOEBA_DEAD)
   {
     int group_nr = AmoebaNr[ax][ay];
 
 #ifdef DEBUG
     if (group_nr == 0)
     {
-      printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
-      printf("AmoebeUmwandeln(): This should never happen!\n");
+      Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
+      Debug("game:playing:AmoebaToDiamond", "This should never happen!");
+
       return;
     }
 #endif
 
     SCAN_PLAYFIELD(x, y)
     {
-      if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
+      if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
       {
        AmoebaNr[x][y] = 0;
-       Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
+       Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
       }
     }
 
@@ -9116,7 +8813,7 @@ void AmoebeUmwandeln(int ax, int ay)
       if (!IN_LEV_FIELD(x, y))
        continue;
 
-      if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
+      if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
       {
        PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
                              SND_AMOEBA_TURNING_TO_GEM :
@@ -9127,7 +8824,7 @@ void AmoebeUmwandeln(int ax, int ay)
   }
 }
 
-void AmoebeUmwandelnBD(int ax, int ay, int new_element)
+static void AmoebaToDiamondBD(int ax, int ay, int new_element)
 {
   int x, y;
   int group_nr = AmoebaNr[ax][ay];
@@ -9136,8 +8833,9 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
 #ifdef DEBUG
   if (group_nr == 0)
   {
-    printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
-    printf("AmoebeUmwandelnBD(): This should never happen!\n");
+    Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
+    Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
+
     return;
   }
 #endif
@@ -9145,12 +8843,12 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
   SCAN_PLAYFIELD(x, y)
   {
     if (AmoebaNr[x][y] == group_nr &&
-       (Feld[x][y] == EL_AMOEBA_DEAD ||
-        Feld[x][y] == EL_BD_AMOEBA ||
-        Feld[x][y] == EL_AMOEBA_GROWING))
+       (Tile[x][y] == EL_AMOEBA_DEAD ||
+        Tile[x][y] == EL_BD_AMOEBA ||
+        Tile[x][y] == EL_AMOEBA_GROWING))
     {
       AmoebaNr[x][y] = 0;
-      Feld[x][y] = new_element;
+      Tile[x][y] = new_element;
       InitField(x, y, FALSE);
       TEST_DrawLevelField(x, y);
       done = TRUE;
@@ -9163,12 +8861,12 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
                            SND_BD_AMOEBA_TURNING_TO_GEM));
 }
 
-void AmoebeWaechst(int x, int y)
+static void AmoebaGrowing(int x, int y)
 {
-  static unsigned long sound_delay = 0;
-  static unsigned long sound_delay_value = 0;
+  static unsigned int sound_delay = 0;
+  static unsigned int sound_delay_value = 0;
 
-  if (!MovDelay[x][y])         /* start new growing cycle */
+  if (!MovDelay[x][y])         // start new growing cycle
   {
     MovDelay[x][y] = 7;
 
@@ -9179,7 +8877,7 @@ void AmoebeWaechst(int x, int y)
     }
   }
 
-  if (MovDelay[x][y])          /* wait some time before growing bigger */
+  if (MovDelay[x][y])          // wait some time before growing bigger
   {
     MovDelay[x][y]--;
     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
@@ -9192,19 +8890,19 @@ void AmoebeWaechst(int x, int y)
 
     if (!MovDelay[x][y])
     {
-      Feld[x][y] = Store[x][y];
+      Tile[x][y] = Store[x][y];
       Store[x][y] = 0;
       TEST_DrawLevelField(x, y);
     }
   }
 }
 
-void AmoebaDisappearing(int x, int y)
+static void AmoebaShrinking(int x, int y)
 {
-  static unsigned long sound_delay = 0;
-  static unsigned long sound_delay_value = 0;
+  static unsigned int sound_delay = 0;
+  static unsigned int sound_delay_value = 0;
 
-  if (!MovDelay[x][y])         /* start new shrinking cycle */
+  if (!MovDelay[x][y])         // start new shrinking cycle
   {
     MovDelay[x][y] = 7;
 
@@ -9212,7 +8910,7 @@ void AmoebaDisappearing(int x, int y)
       sound_delay_value = 30;
   }
 
-  if (MovDelay[x][y])          /* wait some time before shrinking */
+  if (MovDelay[x][y])          // wait some time before shrinking
   {
     MovDelay[x][y]--;
     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
@@ -9225,20 +8923,20 @@ void AmoebaDisappearing(int x, int y)
 
     if (!MovDelay[x][y])
     {
-      Feld[x][y] = EL_EMPTY;
+      Tile[x][y] = EL_EMPTY;
       TEST_DrawLevelField(x, y);
 
-      /* don't let mole enter this field in this cycle;
-        (give priority to objects falling to this field from above) */
+      // don't let mole enter this field in this cycle;
+      // (give priority to objects falling to this field from above)
       Stop[x][y] = TRUE;
     }
   }
 }
 
-void AmoebeAbleger(int ax, int ay)
+static void AmoebaReproduce(int ax, int ay)
 {
   int i;
-  int element = Feld[ax][ay];
+  int element = Tile[ax][ay];
   int graphic = el2img(element);
   int newax = ax, neway = ay;
   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
@@ -9252,7 +8950,7 @@ void AmoebeAbleger(int ax, int ay)
 
   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
   {
-    Feld[ax][ay] = EL_AMOEBA_DEAD;
+    Tile[ax][ay] = EL_AMOEBA_DEAD;
     TEST_DrawLevelField(ax, ay);
     return;
   }
@@ -9260,17 +8958,17 @@ void AmoebeAbleger(int ax, int ay)
   if (IS_ANIMATED(graphic))
     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
 
-  if (!MovDelay[ax][ay])       /* start making new amoeba field */
+  if (!MovDelay[ax][ay])       // start making new amoeba field
     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
 
-  if (MovDelay[ax][ay])                /* wait some time before making new amoeba */
+  if (MovDelay[ax][ay])                // wait some time before making new amoeba
   {
     MovDelay[ax][ay]--;
     if (MovDelay[ax][ay])
       return;
   }
 
-  if (can_drop)                        /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
+  if (can_drop)                        // EL_AMOEBA_WET or EL_EMC_DRIPPER
   {
     int start = RND(4);
     int x = ax + xy[start][0];
@@ -9280,9 +8978,9 @@ void AmoebeAbleger(int ax, int ay)
       return;
 
     if (IS_FREE(x, y) ||
-       CAN_GROW_INTO(Feld[x][y]) ||
-       Feld[x][y] == EL_QUICKSAND_EMPTY ||
-       Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
+       CAN_GROW_INTO(Tile[x][y]) ||
+       Tile[x][y] == EL_QUICKSAND_EMPTY ||
+       Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
     {
       newax = x;
       neway = y;
@@ -9291,7 +8989,7 @@ void AmoebeAbleger(int ax, int ay)
     if (newax == ax && neway == ay)
       return;
   }
-  else                         /* normal or "filled" (BD style) amoeba */
+  else                         // normal or "filled" (BD style) amoeba
   {
     int start = RND(4);
     boolean waiting_for_player = FALSE;
@@ -9306,9 +9004,9 @@ void AmoebeAbleger(int ax, int ay)
        continue;
 
       if (IS_FREE(x, y) ||
-         CAN_GROW_INTO(Feld[x][y]) ||
-         Feld[x][y] == EL_QUICKSAND_EMPTY ||
-         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
+         CAN_GROW_INTO(Tile[x][y]) ||
+         Tile[x][y] == EL_QUICKSAND_EMPTY ||
+         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
       {
        newax = x;
        neway = y;
@@ -9318,35 +9016,37 @@ void AmoebeAbleger(int ax, int ay)
        waiting_for_player = TRUE;
     }
 
-    if (newax == ax && neway == ay)            /* amoeba cannot grow */
+    if (newax == ax && neway == ay)            // amoeba cannot grow
     {
       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
       {
-       Feld[ax][ay] = EL_AMOEBA_DEAD;
+       Tile[ax][ay] = EL_AMOEBA_DEAD;
        TEST_DrawLevelField(ax, ay);
        AmoebaCnt[AmoebaNr[ax][ay]]--;
 
-       if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
+       if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
        {
          if (element == EL_AMOEBA_FULL)
-           AmoebeUmwandeln(ax, ay);
+           AmoebaToDiamond(ax, ay);
          else if (element == EL_BD_AMOEBA)
-           AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
+           AmoebaToDiamondBD(ax, ay, level.amoeba_content);
        }
       }
       return;
     }
     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
     {
-      /* amoeba gets larger by growing in some direction */
+      // amoeba gets larger by growing in some direction
 
       int new_group_nr = AmoebaNr[ax][ay];
 
 #ifdef DEBUG
   if (new_group_nr == 0)
   {
-    printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
-    printf("AmoebeAbleger(): This should never happen!\n");
+    Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
+         newax, neway);
+    Debug("game:playing:AmoebaReproduce", "This should never happen!");
+
     return;
   }
 #endif
@@ -9355,12 +9055,12 @@ void AmoebeAbleger(int ax, int ay)
       AmoebaCnt[new_group_nr]++;
       AmoebaCnt2[new_group_nr]++;
 
-      /* if amoeba touches other amoeba(s) after growing, unify them */
-      AmoebenVereinigen(newax, neway);
+      // if amoeba touches other amoeba(s) after growing, unify them
+      AmoebaMerge(newax, neway);
 
       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
       {
-       AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
+       AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
        return;
       }
     }
@@ -9369,19 +9069,19 @@ void AmoebeAbleger(int ax, int ay)
   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
       (neway == lev_fieldy - 1 && newax != ax))
   {
-    Feld[newax][neway] = EL_AMOEBA_GROWING;    /* creation of new amoeba */
+    Tile[newax][neway] = EL_AMOEBA_GROWING;    // creation of new amoeba
     Store[newax][neway] = element;
   }
   else if (neway == ay || element == EL_EMC_DRIPPER)
   {
-    Feld[newax][neway] = EL_AMOEBA_DROP;       /* drop left/right of amoeba */
+    Tile[newax][neway] = EL_AMOEBA_DROP;       // drop left/right of amoeba
 
     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
   }
   else
   {
-    InitMovingField(ax, ay, MV_DOWN);          /* drop dripping from amoeba */
-    Feld[ax][ay] = EL_AMOEBA_DROPPING;
+    InitMovingField(ax, ay, MV_DOWN);          // drop dripping from amoeba
+    Tile[ax][ay] = EL_AMOEBA_DROPPING;
     Store[ax][ay] = EL_AMOEBA_DROP;
     ContinueMoving(ax, ay);
     return;
@@ -9390,11 +9090,11 @@ void AmoebeAbleger(int ax, int ay)
   TEST_DrawLevelField(newax, neway);
 }
 
-void Life(int ax, int ay)
+static void Life(int ax, int ay)
 {
   int x1, y1, x2, y2;
   int life_time = 40;
-  int element = Feld[ax][ay];
+  int element = Tile[ax][ay];
   int graphic = el2img(element);
   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
                         level.biomaze);
@@ -9406,10 +9106,10 @@ void Life(int ax, int ay)
   if (Stop[ax][ay])
     return;
 
-  if (!MovDelay[ax][ay])       /* start new "game of life" cycle */
+  if (!MovDelay[ax][ay])       // start new "game of life" cycle
     MovDelay[ax][ay] = life_time;
 
-  if (MovDelay[ax][ay])                /* wait some time before next cycle */
+  if (MovDelay[ax][ay])                // wait some time before next cycle
   {
     MovDelay[ax][ay]--;
     if (MovDelay[ax][ay])
@@ -9419,7 +9119,8 @@ void Life(int ax, int ay)
   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
   {
     int xx = ax+x1, yy = ay+y1;
-    int nachbarn = 0;
+    int old_element = Tile[xx][yy];
+    int num_neighbours = 0;
 
     if (!IN_LEV_FIELD(xx, yy))
       continue;
@@ -9431,33 +9132,48 @@ void Life(int ax, int ay)
       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
        continue;
 
-      if (((Feld[x][y] == element ||
-           (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
-          !Stop[x][y]) ||
-         (IS_FREE(x, y) && Stop[x][y]))
-       nachbarn++;
+      boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
+      boolean is_neighbour = FALSE;
+
+      if (level.use_life_bugs)
+       is_neighbour =
+         (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
+          (IS_FREE(x, y)                             &&  Stop[x][y]));
+      else
+       is_neighbour =
+         (Last[x][y] == element || is_player_cell);
+
+      if (is_neighbour)
+       num_neighbours++;
     }
 
-    if (xx == ax && yy == ay)          /* field in the middle */
+    boolean is_free = FALSE;
+
+    if (level.use_life_bugs)
+      is_free = (IS_FREE(xx, yy));
+    else
+      is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
+
+    if (xx == ax && yy == ay)          // field in the middle
     {
-      if (nachbarn < life_parameter[0] ||
-         nachbarn > life_parameter[1])
+      if (num_neighbours < life_parameter[0] ||
+         num_neighbours > life_parameter[1])
       {
-       Feld[xx][yy] = EL_EMPTY;
-       if (!Stop[xx][yy])
+       Tile[xx][yy] = EL_EMPTY;
+       if (Tile[xx][yy] != old_element)
          TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
     }
-    else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
-    {                                  /* free border field */
-      if (nachbarn >= life_parameter[2] &&
-         nachbarn <= life_parameter[3])
+    else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
+    {                                  // free border field
+      if (num_neighbours >= life_parameter[2] &&
+         num_neighbours <= life_parameter[3])
       {
-       Feld[xx][yy] = element;
+       Tile[xx][yy] = element;
        MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
-       if (!Stop[xx][yy])
+       if (Tile[xx][yy] != old_element)
          TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
@@ -9482,10 +9198,11 @@ static void RunRobotWheel(int x, int y)
 
 static void StopRobotWheel(int x, int y)
 {
-  if (ZX == x && ZY == y)
+  if (game.robot_wheel_x == x &&
+      game.robot_wheel_y == y)
   {
-    ZX = ZY = -1;
-
+    game.robot_wheel_x = -1;
+    game.robot_wheel_y = -1;
     game.robot_wheel_active = FALSE;
   }
 }
@@ -9502,11 +9219,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)
@@ -9515,7 +9228,7 @@ static void ActivateMagicBall(int bx, int by)
 
   if (level.ball_random)
   {
-    int pos_border = RND(8);   /* select one of the eight border elements */
+    int pos_border = RND(8);   // select one of the eight border elements
     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
     int xx = pos_content % 3;
     int yy = pos_content / 3;
@@ -9523,7 +9236,7 @@ static void ActivateMagicBall(int bx, int by)
     x = bx - 1 + xx;
     y = by - 1 + yy;
 
-    if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
+    if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
   }
   else
@@ -9533,7 +9246,7 @@ static void ActivateMagicBall(int bx, int by)
       int xx = x - bx + 1;
       int yy = y - by + 1;
 
-      if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
+      if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
        CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
     }
   }
@@ -9541,13 +9254,14 @@ static void ActivateMagicBall(int bx, int by)
   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
 }
 
-void CheckExit(int x, int y)
+static void CheckExit(int x, int y)
 {
-  if (local_player->gems_still_needed > 0 ||
-      local_player->sokobanfields_still_needed > 0 ||
-      local_player->lights_still_needed > 0)
+  if (game.gems_still_needed > 0 ||
+      game.sokoban_fields_still_needed > 0 ||
+      game.sokoban_objects_still_needed > 0 ||
+      game.lights_still_needed > 0)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
     int graphic = el2img(element);
 
     if (IS_ANIMATED(graphic))
@@ -9556,21 +9270,23 @@ void CheckExit(int x, int y)
     return;
   }
 
-  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+  // do not re-open exit door closed after last player
+  if (game.all_players_gone)
     return;
 
-  Feld[x][y] = EL_EXIT_OPENING;
+  Tile[x][y] = EL_EXIT_OPENING;
 
   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
 }
 
-void CheckExitEM(int x, int y)
+static void CheckExitEM(int x, int y)
 {
-  if (local_player->gems_still_needed > 0 ||
-      local_player->sokobanfields_still_needed > 0 ||
-      local_player->lights_still_needed > 0)
+  if (game.gems_still_needed > 0 ||
+      game.sokoban_fields_still_needed > 0 ||
+      game.sokoban_objects_still_needed > 0 ||
+      game.lights_still_needed > 0)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
     int graphic = el2img(element);
 
     if (IS_ANIMATED(graphic))
@@ -9579,21 +9295,23 @@ void CheckExitEM(int x, int y)
     return;
   }
 
-  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+  // do not re-open exit door closed after last player
+  if (game.all_players_gone)
     return;
 
-  Feld[x][y] = EL_EM_EXIT_OPENING;
+  Tile[x][y] = EL_EM_EXIT_OPENING;
 
   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
 }
 
-void CheckExitSteel(int x, int y)
+static void CheckExitSteel(int x, int y)
 {
-  if (local_player->gems_still_needed > 0 ||
-      local_player->sokobanfields_still_needed > 0 ||
-      local_player->lights_still_needed > 0)
+  if (game.gems_still_needed > 0 ||
+      game.sokoban_fields_still_needed > 0 ||
+      game.sokoban_objects_still_needed > 0 ||
+      game.lights_still_needed > 0)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
     int graphic = el2img(element);
 
     if (IS_ANIMATED(graphic))
@@ -9602,21 +9320,23 @@ void CheckExitSteel(int x, int y)
     return;
   }
 
-  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+  // do not re-open exit door closed after last player
+  if (game.all_players_gone)
     return;
 
-  Feld[x][y] = EL_STEEL_EXIT_OPENING;
+  Tile[x][y] = EL_STEEL_EXIT_OPENING;
 
   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
 }
 
-void CheckExitSteelEM(int x, int y)
+static void CheckExitSteelEM(int x, int y)
 {
-  if (local_player->gems_still_needed > 0 ||
-      local_player->sokobanfields_still_needed > 0 ||
-      local_player->lights_still_needed > 0)
+  if (game.gems_still_needed > 0 ||
+      game.sokoban_fields_still_needed > 0 ||
+      game.sokoban_objects_still_needed > 0 ||
+      game.lights_still_needed > 0)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
     int graphic = el2img(element);
 
     if (IS_ANIMATED(graphic))
@@ -9625,19 +9345,20 @@ void CheckExitSteelEM(int x, int y)
     return;
   }
 
-  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+  // do not re-open exit door closed after last player
+  if (game.all_players_gone)
     return;
 
-  Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
+  Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
 
   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
 }
 
-void CheckExitSP(int x, int y)
+static void CheckExitSP(int x, int y)
 {
-  if (local_player->gems_still_needed > 0)
+  if (game.gems_still_needed > 0)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
     int graphic = el2img(element);
 
     if (IS_ANIMATED(graphic))
@@ -9646,47 +9367,48 @@ void CheckExitSP(int x, int y)
     return;
   }
 
-  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+  // do not re-open exit door closed after last player
+  if (game.all_players_gone)
     return;
 
-  Feld[x][y] = EL_SP_EXIT_OPENING;
+  Tile[x][y] = EL_SP_EXIT_OPENING;
 
   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
 }
 
-static void CloseAllOpenTimegates()
+static void CloseAllOpenTimegates(void)
 {
   int x, y;
 
   SCAN_PLAYFIELD(x, y)
   {
-    int element = Feld[x][y];
+    int element = Tile[x][y];
 
     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
     {
-      Feld[x][y] = EL_TIMEGATE_CLOSING;
+      Tile[x][y] = EL_TIMEGATE_CLOSING;
 
       PlayLevelSoundAction(x, y, ACTION_CLOSING);
     }
   }
 }
 
-void DrawTwinkleOnField(int x, int y)
+static void DrawTwinkleOnField(int x, int y)
 {
   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
     return;
 
-  if (Feld[x][y] == EL_BD_DIAMOND)
+  if (Tile[x][y] == EL_BD_DIAMOND)
     return;
 
-  if (MovDelay[x][y] == 0)     /* next animation frame */
+  if (MovDelay[x][y] == 0)     // next animation frame
     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
 
-  if (MovDelay[x][y] != 0)     /* wait some time before next frame */
+  if (MovDelay[x][y] != 0)     // wait some time before next frame
   {
     MovDelay[x][y]--;
 
-    DrawLevelElementAnimation(x, y, Feld[x][y]);
+    DrawLevelElementAnimation(x, y, Tile[x][y]);
 
     if (MovDelay[x][y] != 0)
     {
@@ -9698,20 +9420,20 @@ void DrawTwinkleOnField(int x, int y)
   }
 }
 
-void MauerWaechst(int x, int y)
+static void MauerWaechst(int x, int y)
 {
   int delay = 6;
 
-  if (!MovDelay[x][y])         /* next animation frame */
+  if (!MovDelay[x][y])         // next animation frame
     MovDelay[x][y] = 3 * delay;
 
-  if (MovDelay[x][y])          /* wait some time before next frame */
+  if (MovDelay[x][y])          // wait some time before next frame
   {
     MovDelay[x][y]--;
 
     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
     {
-      int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
+      int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
 
       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
@@ -9721,26 +9443,26 @@ void MauerWaechst(int x, int y)
     {
       if (MovDir[x][y] == MV_LEFT)
       {
-       if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
+       if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
          TEST_DrawLevelField(x - 1, y);
       }
       else if (MovDir[x][y] == MV_RIGHT)
       {
-       if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
+       if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
          TEST_DrawLevelField(x + 1, y);
       }
       else if (MovDir[x][y] == MV_UP)
       {
-       if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
+       if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
          TEST_DrawLevelField(x, y - 1);
       }
       else
       {
-       if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
+       if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
          TEST_DrawLevelField(x, y + 1);
       }
 
-      Feld[x][y] = Store[x][y];
+      Tile[x][y] = Store[x][y];
       Store[x][y] = 0;
       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
       TEST_DrawLevelField(x, y);
@@ -9748,9 +9470,9 @@ void MauerWaechst(int x, int y)
   }
 }
 
-void MauerAbleger(int ax, int ay)
+static void MauerAbleger(int ax, int ay)
 {
-  int element = Feld[ax][ay];
+  int element = Tile[ax][ay];
   int graphic = el2img(element);
   boolean oben_frei = FALSE, unten_frei = FALSE;
   boolean links_frei = FALSE, rechts_frei = FALSE;
@@ -9761,10 +9483,10 @@ void MauerAbleger(int ax, int ay)
   if (IS_ANIMATED(graphic))
     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
 
-  if (!MovDelay[ax][ay])       /* start building new wall */
+  if (!MovDelay[ax][ay])       // start building new wall
     MovDelay[ax][ay] = 6;
 
-  if (MovDelay[ax][ay])                /* wait some time before building new wall */
+  if (MovDelay[ax][ay])                // wait some time before building new wall
   {
     MovDelay[ax][ay]--;
     if (MovDelay[ax][ay])
@@ -9785,7 +9507,7 @@ void MauerAbleger(int ax, int ay)
   {
     if (oben_frei)
     {
-      Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
+      Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
       Store[ax][ay-1] = element;
       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
@@ -9795,7 +9517,7 @@ void MauerAbleger(int ax, int ay)
     }
     if (unten_frei)
     {
-      Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
+      Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
       Store[ax][ay+1] = element;
       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
@@ -9812,7 +9534,7 @@ void MauerAbleger(int ax, int ay)
   {
     if (links_frei)
     {
-      Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
+      Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
       Store[ax-1][ay] = element;
       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
@@ -9823,7 +9545,7 @@ void MauerAbleger(int ax, int ay)
 
     if (rechts_frei)
     {
-      Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
+      Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
       Store[ax+1][ay] = element;
       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
@@ -9836,13 +9558,13 @@ void MauerAbleger(int ax, int ay)
   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
     TEST_DrawLevelField(ax, ay);
 
-  if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
+  if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
     oben_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
+  if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
     unten_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
+  if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
     links_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
+  if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
     rechts_massiv = TRUE;
 
   if (((oben_massiv && unten_massiv) ||
@@ -9850,15 +9572,15 @@ void MauerAbleger(int ax, int ay)
        element == EL_EXPANDABLE_WALL) &&
       ((links_massiv && rechts_massiv) ||
        element == EL_EXPANDABLE_WALL_VERTICAL))
-    Feld[ax][ay] = EL_WALL;
+    Tile[ax][ay] = EL_WALL;
 
   if (new_wall)
     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
 }
 
-void MauerAblegerStahl(int ax, int ay)
+static void MauerAblegerStahl(int ax, int ay)
 {
-  int element = Feld[ax][ay];
+  int element = Tile[ax][ay];
   int graphic = el2img(element);
   boolean oben_frei = FALSE, unten_frei = FALSE;
   boolean links_frei = FALSE, rechts_frei = FALSE;
@@ -9869,10 +9591,10 @@ void MauerAblegerStahl(int ax, int ay)
   if (IS_ANIMATED(graphic))
     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
 
-  if (!MovDelay[ax][ay])       /* start building new wall */
+  if (!MovDelay[ax][ay])       // start building new wall
     MovDelay[ax][ay] = 6;
 
-  if (MovDelay[ax][ay])                /* wait some time before building new wall */
+  if (MovDelay[ax][ay])                // wait some time before building new wall
   {
     MovDelay[ax][ay]--;
     if (MovDelay[ax][ay])
@@ -9893,7 +9615,7 @@ void MauerAblegerStahl(int ax, int ay)
   {
     if (oben_frei)
     {
-      Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
+      Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
       Store[ax][ay-1] = element;
       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
@@ -9903,7 +9625,7 @@ void MauerAblegerStahl(int ax, int ay)
     }
     if (unten_frei)
     {
-      Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
+      Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
       Store[ax][ay+1] = element;
       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
@@ -9918,7 +9640,7 @@ void MauerAblegerStahl(int ax, int ay)
   {
     if (links_frei)
     {
-      Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
+      Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
       Store[ax-1][ay] = element;
       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
@@ -9929,7 +9651,7 @@ void MauerAblegerStahl(int ax, int ay)
 
     if (rechts_frei)
     {
-      Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
+      Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
       Store[ax+1][ay] = element;
       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
@@ -9939,26 +9661,26 @@ void MauerAblegerStahl(int ax, int ay)
     }
   }
 
-  if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
+  if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
     oben_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
+  if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
     unten_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
+  if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
     links_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
+  if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
     rechts_massiv = TRUE;
 
   if (((oben_massiv && unten_massiv) ||
        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
       ((links_massiv && rechts_massiv) ||
        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
-    Feld[ax][ay] = EL_STEELWALL;
+    Tile[ax][ay] = EL_STEELWALL;
 
   if (new_wall)
     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
 }
 
-void CheckForDragon(int x, int y)
+static void CheckForDragon(int x, int y)
 {
   int i, j;
   boolean dragon_found = FALSE;
@@ -9977,9 +9699,9 @@ void CheckForDragon(int x, int y)
       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
 
       if (IN_LEV_FIELD(xx, yy) &&
-         (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
+         (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
       {
-       if (Feld[xx][yy] == EL_DRAGON)
+       if (Tile[xx][yy] == EL_DRAGON)
          dragon_found = TRUE;
       }
       else
@@ -9995,9 +9717,9 @@ void CheckForDragon(int x, int y)
       {
        int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
   
-       if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
+       if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
        {
-         Feld[xx][yy] = EL_EMPTY;
+         Tile[xx][yy] = EL_EMPTY;
          TEST_DrawLevelField(xx, yy);
        }
        else
@@ -10009,7 +9731,7 @@ void CheckForDragon(int x, int y)
 
 static void InitBuggyBase(int x, int y)
 {
-  int element = Feld[x][y];
+  int element = Tile[x][y];
   int activating_delay = FRAMES_PER_SECOND / 4;
 
   ChangeDelay[x][y] =
@@ -10060,9 +9782,9 @@ static void ChangeActiveTrap(int x, int y)
 {
   int graphic = IMG_TRAP_ACTIVE;
 
-  /* if new animation frame was drawn, correct crumbled sand border */
+  // if new animation frame was drawn, correct crumbled sand border
   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
-    TEST_DrawLevelFieldCrumbledSand(x, y);
+    TEST_DrawLevelFieldCrumbled(x, y);
 }
 
 static int getSpecialActionElement(int element, int number, int base_element)
@@ -10096,22 +9818,28 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
   int action_type = change->action_type;
   int action_mode = change->action_mode;
   int action_arg = change->action_arg;
+  int action_element = change->action_element;
   int i;
 
   if (!change->has_action)
     return;
 
-  /* ---------- determine action paramater values -------------------------- */
+  // ---------- determine action paramater values -----------------------------
 
   int level_time_value =
     (level.time > 0 ? TimeLeft :
      TimePlayed);
 
-  int action_arg_element =
+  int action_arg_element_raw =
     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
+     action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
+     action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
+     action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
+     action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
      EL_EMPTY);
+  int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
 
   int action_arg_direction =
     (action_arg >= CA_ARG_DIRECTION_LEFT &&
@@ -10140,11 +9868,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);
 
@@ -10156,28 +9880,27 @@ 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 :
-     action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
-     action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
+     action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
+     action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
+     action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
+     action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
+     action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
      -1);
 
   int action_arg_number_old =
-    (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
+    (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
-     action_type == CA_SET_LEVEL_SCORE ? local_player->score :
+     action_type == CA_SET_LEVEL_SCORE ? game.score :
      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
      action_type == CA_SET_CE_SCORE ? ei->collect_score :
      0);
@@ -10187,23 +9910,18 @@ 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;
-#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
+    (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
+     change->actual_trigger_player_bits : change->trigger_player);
 
   int action_arg_player_bits =
     (action_arg >= CA_ARG_PLAYER_1 &&
      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
+     action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
      PLAYER_BITS_ANY);
 
-  /* ---------- execute action  -------------------------------------------- */
+  // ---------- execute action  -----------------------------------------------
 
   switch (action_type)
   {
@@ -10212,7 +9930,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       return;
     }
 
-    /* ---------- level actions  ------------------------------------------- */
+    // ---------- level actions  ----------------------------------------------
 
     case CA_RESTART_LEVEL:
     {
@@ -10234,17 +9952,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_LEVEL_TIME:
     {
-      if (level.time > 0)      /* only modify limited time value */
+      if (level.time > 0)      // only modify limited time value
       {
        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++)
@@ -10256,60 +9970,54 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_LEVEL_SCORE:
     {
-      local_player->score = action_arg_number_new;
+      game.score = action_arg_number_new;
 
-#if 1
-      game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+      game_panel_controls[GAME_PANEL_SCORE].value = game.score;
 
       DisplayGameControlValues();
-#else
-      DrawGameValue_Score(local_player->score);
-#endif
 
       break;
     }
 
     case CA_SET_LEVEL_GEMS:
     {
-      local_player->gems_still_needed = action_arg_number_new;
+      game.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;
+
+      game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
 
       DisplayGameControlValues();
-#else
-      DrawGameValue_Emeralds(local_player->gems_still_needed);
-#endif
 
       break;
     }
 
-#if !USE_PLAYER_GRAVITY
-    case CA_SET_LEVEL_GRAVITY:
+    case CA_SET_LEVEL_WIND:
     {
-      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);
+      game.wind_direction = action_arg_direction;
+
       break;
     }
-#endif
 
-    case CA_SET_LEVEL_WIND:
+    case CA_SET_LEVEL_RANDOM_SEED:
     {
-      game.wind_direction = action_arg_direction;
+      // ensure that setting a new random seed while playing is predictable
+      InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
 
       break;
     }
 
-    /* ---------- player actions  ------------------------------------------ */
+    // ---------- player actions  ---------------------------------------------
 
     case CA_MOVE_PLAYER:
+    case CA_MOVE_PLAYER_NEW:
     {
-      /* automatically move to the next field in specified direction */
+      // automatically move to the next field in specified direction
       for (i = 0; i < MAX_PLAYERS; i++)
        if (trigger_player_bits & (1 << i))
-         stored_player[i].programmed_action = action_arg_direction;
+         if (action_type == CA_MOVE_PLAYER ||
+             stored_player[i].MovPos == 0)
+           stored_player[i].programmed_action = action_arg_direction;
 
       break;
     }
@@ -10318,7 +10026,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       for (i = 0; i < MAX_PLAYERS; i++)
        if (action_arg_player_bits & (1 << i))
-         PlayerWins(&stored_player[i]);
+         ExitPlayer(&stored_player[i]);
+
+      if (game.players_still_needed == 0)
+       LevelSolved();
 
       break;
     }
@@ -10419,7 +10130,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++)
@@ -10436,7 +10146,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
       break;
     }
-#endif
 
     case CA_SET_PLAYER_ARTWORK:
     {
@@ -10451,16 +10160,14 @@ 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;
 
          SetPlayerWaiting(&stored_player[i], FALSE);
 
-         /* set number of special actions for bored and sleeping animation */
+         // set number of special actions for bored and sleeping animation
          stored_player[i].num_special_action_bored =
            get_num_special_action(artwork_element,
                                   ACTION_BORING_1, ACTION_BORING_LAST);
@@ -10473,11 +10180,108 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
-    /* ---------- CE actions  ---------------------------------------------- */
+    case CA_SET_PLAYER_INVENTORY:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int j, k;
+
+       if (trigger_player_bits & (1 << i))
+       {
+         int inventory_element = action_arg_element;
+
+         if (action_arg == CA_ARG_ELEMENT_TARGET ||
+             action_arg == CA_ARG_ELEMENT_TRIGGER ||
+             action_arg == CA_ARG_ELEMENT_ACTION)
+         {
+           int element = inventory_element;
+           int collect_count = element_info[element].collect_count_initial;
+
+           if (!IS_CUSTOM_ELEMENT(element))
+             collect_count = 1;
+
+           if (collect_count == 0)
+             player->inventory_infinite_element = element;
+           else
+             for (k = 0; k < collect_count; k++)
+               if (player->inventory_size < MAX_INVENTORY_SIZE)
+                 player->inventory_element[player->inventory_size++] =
+                   element;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
+                  action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
+                  action_arg == CA_ARG_INVENTORY_RM_ACTION)
+         {
+           if (player->inventory_infinite_element != EL_UNDEFINED &&
+               IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
+                                    action_arg_element_raw))
+             player->inventory_infinite_element = EL_UNDEFINED;
+
+           for (k = 0, j = 0; j < player->inventory_size; j++)
+           {
+             if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
+                                       action_arg_element_raw))
+               player->inventory_element[k++] = player->inventory_element[j];
+           }
+
+           player->inventory_size = k;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
+         {
+           if (player->inventory_size > 0)
+           {
+             for (j = 0; j < player->inventory_size - 1; j++)
+               player->inventory_element[j] = player->inventory_element[j + 1];
+
+             player->inventory_size--;
+           }
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
+         {
+           if (player->inventory_size > 0)
+             player->inventory_size--;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
+         {
+           player->inventory_infinite_element = EL_UNDEFINED;
+           player->inventory_size = 0;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RESET)
+         {
+           player->inventory_infinite_element = EL_UNDEFINED;
+           player->inventory_size = 0;
+
+           if (level.use_initial_inventory[i])
+           {
+             for (j = 0; j < level.initial_inventory_size[i]; j++)
+             {
+               int element = level.initial_inventory_content[i][j];
+               int collect_count = element_info[element].collect_count_initial;
+
+               if (!IS_CUSTOM_ELEMENT(element))
+                 collect_count = 1;
+
+               if (collect_count == 0)
+                 player->inventory_infinite_element = element;
+               else
+                 for (k = 0; k < collect_count; k++)
+                   if (player->inventory_size < MAX_INVENTORY_SIZE)
+                     player->inventory_element[player->inventory_size++] =
+                       element;
+             }
+           }
+         }
+       }
+      }
+
+      break;
+    }
+
+    // ---------- CE actions  -------------------------------------------------
 
     case CA_SET_CE_VALUE:
     {
-#if USE_NEW_CUSTOM_VALUE
       int last_ce_value = CustomValue[x][y];
 
       CustomValue[x][y] = action_arg_number_new;
@@ -10489,18 +10293,19 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
        if (CustomValue[x][y] == 0)
        {
+         // reset change counter (else CE_VALUE_GETS_ZERO would not work)
+         ChangeCount[x][y] = 0;        // allow at least one more change
+
          CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
          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;
@@ -10514,6 +10319,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
        {
          int xx, yy;
 
+         // reset change counter (else CE_SCORE_GETS_ZERO would not work)
+         ChangeCount[x][y] = 0;        // allow at least one more change
+
          CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
          CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
 
@@ -10530,18 +10338,49 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
          */
          SCAN_PLAYFIELD(xx, yy)
          {
-           if (Feld[xx][yy] == element)
+           if (Tile[xx][yy] == element)
              CheckElementChange(xx, yy, element, EL_UNDEFINED,
                                 CE_SCORE_GETS_ZERO);
          }
        }
       }
-#endif
 
       break;
     }
 
-    /* ---------- engine actions  ------------------------------------------ */
+    case CA_SET_CE_ARTWORK:
+    {
+      int artwork_element = action_arg_element;
+      boolean reset_frame = FALSE;
+      int xx, yy;
+
+      if (action_arg == CA_ARG_ELEMENT_RESET)
+       artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
+                          element);
+
+      if (ei->gfx_element != artwork_element)
+       reset_frame = TRUE;
+
+      ei->gfx_element = artwork_element;
+
+      SCAN_PLAYFIELD(xx, yy)
+      {
+       if (Tile[xx][yy] == element)
+       {
+         if (reset_frame)
+         {
+           ResetGfxAnimation(xx, yy);
+           ResetRandomAnimationValue(xx, yy);
+         }
+
+         TEST_DrawLevelField(xx, yy);
+       }
+      }
+
+      break;
+    }
+
+    // ---------- engine actions  ---------------------------------------------
 
     case CA_SET_ENGINE_SCAN_MODE:
     {
@@ -10557,35 +10396,16 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
 static void CreateFieldExt(int x, int y, int element, boolean is_change)
 {
-  int old_element = Feld[x][y];
+  int old_element = Tile[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))
@@ -10593,41 +10413,30 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
     else
       RemoveField(x, y);
 
-    Feld[x][y] = new_element;
-
-#if !USE_GFX_RESET_GFX_ANIMATION
-    ResetGfxAnimation(x, y);
-    ResetRandomAnimationValue(x, y);
-#endif
+    Tile[x][y] = new_element;
 
     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 */
+    new_element = Tile[x][y];  // element may have changed
 
-#if USE_GFX_RESET_GFX_ANIMATION
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
-#endif
 
     TEST_DrawLevelField(x, y);
 
     if (GFX_CRUMBLED(new_element))
-      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+      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
+  // 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 (IS_PLAYER(x, y) && !player_explosion_protected &&
       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
   {
@@ -10635,23 +10444,13 @@ 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 */
+  // "ChangeCount" not set yet to allow "entered by player" change one time
   if (new_element_is_player)
     RelocatePlayer(x, y, new_element);
 
   if (is_change)
-    ChangeCount[x][y]++;       /* count number of changes in the same frame */
+    ChangeCount[x][y]++;       // count number of changes in the same frame
 
   TestIfBadThingTouchesPlayer(x, y);
   TestIfPlayerTouchesCustomElement(x, y);
@@ -10667,18 +10466,16 @@ 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];
+    int old_element = Tile[x][y];
 
-    /* prevent changed element from moving in same engine frame
-       unless both old and new element can either fall or move */
+    // prevent changed element from moving in same engine frame
+    // unless both old and new element can either fall or move
     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
        (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
       Stop[x][y] = TRUE;
   }
-#endif
 
   CreateFieldExt(x, y, element, TRUE);
 }
@@ -10690,28 +10487,28 @@ static boolean ChangeElement(int x, int y, int element, int page)
   int ce_value = CustomValue[x][y];
   int ce_score = ei->collect_score;
   int target_element;
-  int old_element = Feld[x][y];
+  int old_element = Tile[x][y];
 
-  /* always use default change event to prevent running into a loop */
+  // always use default change event to prevent running into a loop
   if (ChangeEvent[x][y] == -1)
     ChangeEvent[x][y] = CE_DELAY;
 
   if (ChangeEvent[x][y] == CE_DELAY)
   {
-    /* reset actual trigger element, trigger player and action element */
+    // reset actual trigger element, trigger player and action element
     change->actual_trigger_element = EL_EMPTY;
-    change->actual_trigger_player = EL_PLAYER_1;
-    change->actual_trigger_player_bits = CH_PLAYER_1;
+    change->actual_trigger_player = EL_EMPTY;
+    change->actual_trigger_player_bits = CH_PLAYER_NONE;
     change->actual_trigger_side = CH_SIDE_NONE;
     change->actual_trigger_ce_value = 0;
     change->actual_trigger_ce_score = 0;
   }
 
-  /* do not change elements more than a specified maximum number of changes */
+  // do not change elements more than a specified maximum number of changes
   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
     return FALSE;
 
-  ChangeCount[x][y]++;         /* count number of changes in the same frame */
+  ChangeCount[x][y]++;         // count number of changes in the same frame
 
   if (change->explode)
   {
@@ -10741,12 +10538,12 @@ static boolean ChangeElement(int x, int y, int element, int page)
 
       can_replace[xx][yy] = TRUE;
 
-      if (ex == x && ey == y)  /* do not check changing element itself */
+      if (ex == x && ey == y)  // do not check changing element itself
        continue;
 
       if (content_element == EL_EMPTY_SPACE)
       {
-       can_replace[xx][yy] = FALSE;    /* do not replace border with space */
+       can_replace[xx][yy] = FALSE;    // do not replace border with space
 
        continue;
       }
@@ -10759,7 +10556,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
        continue;
       }
 
-      e = Feld[ex][ey];
+      e = Tile[ex][ey];
 
       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
        e = MovingOrBlocked2Element(ex, ey);
@@ -10816,9 +10613,9 @@ static boolean ChangeElement(int x, int y, int element, int page)
 
          something_has_changed = TRUE;
 
-         /* for symmetry reasons, freeze newly created border elements */
+         // for symmetry reasons, freeze newly created border elements
          if (ex != x || ey != y)
-           Stop[ex][ey] = TRUE;        /* no more moving in this frame */
+           Stop[ex][ey] = TRUE;        // no more moving in this frame
        }
       }
 
@@ -10848,62 +10645,91 @@ static boolean ChangeElement(int x, int y, int element, int page)
     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
   }
 
-  /* this uses direct change before indirect change */
+  // this uses direct change before indirect change
   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
 
   return TRUE;
 }
 
-#if USE_NEW_DELAYED_ACTION
-
 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];
+  boolean handle_action_before_change = FALSE;
 
 #ifdef DEBUG
   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
       !CAN_CHANGE_OR_HAS_ACTION(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");
+    Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
+         x, y, element, element_info[element].token_name);
+    Debug("game:playing:HandleElementChange", "This should never happen!");
   }
 #endif
 
-  /* this can happen with classic bombs on walkable, changing elements */
+  // 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;
   }
 
-  if (ChangeDelay[x][y] == 0)          /* initialize element change */
+  if (ChangeDelay[x][y] == 0)          // initialize element change
   {
     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
 
     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
-      /* 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
+      // !!! not clear why graphic animation should be reset at all here !!!
+      // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
+      // !!! 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 (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);
@@ -10912,7 +10738,7 @@ static void HandleElementChange(int x, int y, int page)
 
   ChangeDelay[x][y]--;
 
-  if (ChangeDelay[x][y] != 0)          /* continue element change */
+  if (ChangeDelay[x][y] != 0)          // continue element change
   {
     if (change->can_change)
     {
@@ -10925,9 +10751,9 @@ static void HandleElementChange(int x, int y, int page)
        change->change_function(x, y);
     }
   }
-  else                                 /* finish element change */
+  else                                 // finish element change
   {
-    if (ChangePage[x][y] != -1)                /* remember page from delayed change */
+    if (ChangePage[x][y] != -1)                // remember page from delayed change
     {
       page = ChangePage[x][y];
       ChangePage[x][y] = -1;
@@ -10935,14 +10761,21 @@ static void HandleElementChange(int x, int y, int page)
       change = &ei->change_page[page];
     }
 
-    if (IS_MOVING(x, y))               /* never change a running system ;-) */
+    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 */
+      ChangeDelay[x][y] = 1;           // try change after next move step
+      ChangePage[x][y] = page;         // remember page to use for change
 
       return;
     }
 
+    // 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);
+
     if (change->can_change)
     {
       if (ChangeElement(x, y, element, page))
@@ -10952,92 +10785,11 @@ static void HandleElementChange(int x, int y, int page)
       }
     }
 
-    if (change->has_action)
+    if (change->has_action && !handle_action_before_change)
       ExecuteCustomElementAction(x, y, element, 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,
@@ -11052,12 +10804,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++)
@@ -11094,28 +10840,36 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
 
          SCAN_PLAYFIELD(x, y)
          {
-           if (Feld[x][y] == element)
+           if (Tile[x][y] == element)
            {
              if (change->can_change && !change_done)
              {
+               // 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
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+
                ChangeDelay[x][y] = 1;
                ChangeEvent[x][y] = trigger_event;
 
                HandleElementChange(x, y, p);
              }
-#if USE_NEW_DELAYED_ACTION
              else if (change->has_action)
              {
+               // 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
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+
                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
            }
          }
 
@@ -11148,32 +10902,20 @@ static boolean CheckElementChangeExt(int x, int y,
       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
     return FALSE;
 
-  if (Feld[x][y] == EL_BLOCKED)
+  if (Tile[x][y] == EL_BLOCKED)
   {
     Blocked2Moving(x, y, &x, &y);
-    element = Feld[x][y];
+    element = Tile[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 */
+  // 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) ||
+       Tile[x][y] != element) ||
 
       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
        (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);
 
@@ -11189,10 +10931,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] &&
@@ -11208,7 +10947,7 @@ static boolean CheckElementChangeExt(int x, int y,
       change->actual_trigger_ce_value = CustomValue[x][y];
       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
 
-      /* special case: trigger element not at (x,y) position for some events */
+      // special case: trigger element not at (x,y) position for some events
       if (check_trigger_element)
       {
        static struct
@@ -11241,19 +10980,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
     }
   }
 
@@ -11286,7 +11017,7 @@ static void PlayPlayerSound(struct PlayerInfo *player)
   }
 }
 
-static void PlayAllPlayersSound()
+static void PlayAllPlayersSound(void)
 {
   int i;
 
@@ -11305,7 +11036,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
 
   if (is_waiting)
   {
-    if (!last_waiting)         /* not waiting -> waiting */
+    if (!last_waiting)         // not waiting -> waiting
     {
       player->is_waiting = TRUE;
 
@@ -11338,14 +11069,14 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
 
     if (player->is_sleeping && player->use_murphy)
     {
-      /* special case for sleeping Murphy when leaning against non-free tile */
+      // special case for sleeping Murphy when leaning against non-free tile
 
       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
-         (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
+         (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
           !IS_MOVING(player->jx - 1, player->jy)))
        move_dir = MV_LEFT;
       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
-              (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
+              (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
                !IS_MOVING(player->jx + 1, player->jy)))
        move_dir = MV_RIGHT;
       else
@@ -11424,7 +11155,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
       }
     }
   }
-  else if (last_waiting)       /* waiting -> not waiting */
+  else if (last_waiting)       // waiting -> not waiting
   {
     player->is_waiting = FALSE;
     player->is_bored = FALSE;
@@ -11444,9 +11175,60 @@ 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 struct MouseActionInfo mouse_action_last = { 0 };
+  struct MouseActionInfo mouse_action = player->effective_mouse_action;
+  boolean new_released = (!mouse_action.button && mouse_action_last.button);
+
+  if (new_released)
+    CheckSaveEngineSnapshotToList();
+
+  mouse_action_last = mouse_action;
+}
+
+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 &&
+       !player->is_dropping_pressed &&
+       !player->effective_mouse_action.button)
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+  }
+
+  CheckSaveEngineSnapshot(player);
+}
+
 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
 {
-  boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
   int left     = player_action & JOY_LEFT;
   int right    = player_action & JOY_RIGHT;
   int up       = player_action & JOY_UP;
@@ -11462,23 +11244,16 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
   if (player_action)
   {
     if (button1)
-      snapped = SnapField(player, dx, dy);
+      SnapField(player, dx, dy);
     else
     {
       if (button2)
-       dropped = DropElement(player);
+       DropElement(player);
 
-      moved = MovePlayer(player, dx, dy);
+      MovePlayer(player, dx, dy);
     }
 
-    if (tape.single_step && tape.recording && !tape.pausing)
-    {
-      if (button1 || (dropped && !moved))
-      {
-       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
-       SnapField(player, 0, 0);                /* stop snapping */
-      }
-    }
+    CheckSingleStepMode(player);
 
     SetPlayerWaiting(player, FALSE);
 
@@ -11486,7 +11261,7 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
   }
   else
   {
-    /* no actions for this player (no input at player's configured device) */
+    // no actions for this player (no input at player's configured device)
 
     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
@@ -11495,38 +11270,93 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
     if (player->MovPos == 0)
       SetPlayerWaiting(player, TRUE);
 
-    if (player->MovPos == 0)   /* needed for tape.playing */
+    if (player->MovPos == 0)   // needed for tape.playing
       player->is_moving = FALSE;
 
     player->is_dropping = FALSE;
     player->is_dropping_pressed = FALSE;
     player->drop_pressed_delay = 0;
 
+    CheckSingleStepMode(player);
+
     return 0;
   }
 }
 
-static void CheckLevelTime()
+static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
+                                        byte *tape_action)
 {
-  int i;
+  if (!tape.use_mouse_actions)
+    return;
+
+  mouse_action->lx     = tape_action[TAPE_ACTION_LX];
+  mouse_action->ly     = tape_action[TAPE_ACTION_LY];
+  mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
+}
+
+static void SetTapeActionFromMouseAction(byte *tape_action,
+                                        struct MouseActionInfo *mouse_action)
+{
+  if (!tape.use_mouse_actions)
+    return;
 
+  tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
+  tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
+  tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
+}
+
+static void CheckLevelSolved(void)
+{
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
-    if (level.native_em_level->lev->home == 0) /* all players at home */
+    if (game_em.level_solved &&
+       !game_em.game_over)                             // game won
+    {
+      LevelSolved();
+
+      game_em.game_over = TRUE;
+
+      game.all_players_gone = TRUE;
+    }
+
+    if (game_em.game_over)                             // game lost
+      game.all_players_gone = TRUE;
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    if (game_sp.level_solved &&
+       !game_sp.game_over)                             // game won
+    {
+      LevelSolved();
+
+      game_sp.game_over = TRUE;
+
+      game.all_players_gone = TRUE;
+    }
+
+    if (game_sp.game_over)                             // game lost
+      game.all_players_gone = TRUE;
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+  {
+    if (game_mm.level_solved &&
+       !game_mm.game_over)                             // game won
     {
-      PlayerWins(local_player);
+      LevelSolved();
 
-      AllPlayersGone = TRUE;
+      game_mm.game_over = TRUE;
 
-      level.native_em_level->lev->home = -1;
+      game.all_players_gone = TRUE;
     }
 
-    if (level.native_em_level->ply[0]->alive == 0 &&
-       level.native_em_level->ply[1]->alive == 0 &&
-       level.native_em_level->ply[2]->alive == 0 &&
-       level.native_em_level->ply[3]->alive == 0)      /* all dead */
-      AllPlayersGone = TRUE;
+    if (game_mm.game_over)                             // game lost
+      game.all_players_gone = TRUE;
   }
+}
+
+static void CheckLevelTime(void)
+{
+  int i;
 
   if (TimeFrames >= FRAMES_PER_SECOND)
   {
@@ -11546,7 +11376,7 @@ static void CheckLevelTime()
       }
     }
 
-    if (!local_player->LevelSolved && !level.use_step_counter)
+    if (!game.LevelSolved && !level.use_step_counter)
     {
       TimePlayed++;
 
@@ -11557,71 +11387,57 @@ static void CheckLevelTime()
        if (TimeLeft <= 10 && setup.time_limit)
          PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
-#if 1
-       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+       /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
+          is reset from other values in UpdateGameDoorValues() -- FIX THIS */
 
-       DisplayGameControlValues();
-#else
-       DrawGameValue_Time(TimeLeft);
-#endif
+       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
 
        if (!TimeLeft && setup.time_limit)
        {
          if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-           level.native_em_level->lev->killed_out_of_time = TRUE;
+           game_em.lev->killed_out_of_time = TRUE;
          else
            for (i = 0; i < MAX_PLAYERS; i++)
              KillPlayer(&stored_player[i]);
        }
       }
-#if 1
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      else if (game.no_time_limit && !game.all_players_gone)
       {
        game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
-
-       DisplayGameControlValues();
       }
-#else
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
-       DrawGameValue_Time(TimePlayed);
-#endif
 
-      level.native_em_level->lev->time =
-       (level.time == 0 ? TimePlayed : TimeLeft);
+      game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
     }
 
     if (tape.recording || tape.playing)
       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)
 {
   int i;
 
-  /* advance frame counters (global frame counter and time frame counter) */
+  // advance frame counters (global frame counter and time frame counter)
   FrameCounter++;
   TimeFrames++;
 
-  /* advance player counters (counters for move delay, move animation etc.) */
+  // advance player counters (counters for move delay, move animation etc.)
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
     int move_delay_value = stored_player[i].move_delay_value;
     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
 
-    if (!advance_player_counters)      /* not all players may be affected */
+    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 */
+    if (move_frames == 0)      // less than one move per game frame
     {
       int stepsize = TILEX / move_delay_value;
       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
@@ -11631,7 +11447,6 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
       if (count % delay == 0)
        move_frames = 1;
     }
-#endif
 
     stored_player[i].Frame += move_frames;
 
@@ -11641,7 +11456,7 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
     if (stored_player[i].move_delay > 0)
       stored_player[i].move_delay--;
 
-    /* due to bugs in previous versions, counter must count up, not down */
+    // due to bugs in previous versions, counter must count up, not down
     if (stored_player[i].push_delay != -1)
       stored_player[i].push_delay++;
 
@@ -11654,47 +11469,48 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
 }
 
 void StartGameActions(boolean init_network_game, boolean record_tape,
-                     long random_seed)
+                     int random_seed)
 {
-  unsigned long new_random_seed = InitRND(random_seed);
+  unsigned int new_random_seed = InitRND(random_seed);
 
   if (record_tape)
     TapeStartRecording(new_random_seed);
 
-#if defined(NETWORK_AVALIABLE)
   if (init_network_game)
   {
+    SendToServer_LevelFile();
     SendToServer_StartPlaying();
 
     return;
   }
-#endif
 
   InitGame();
 }
 
-void GameActions()
+static void GameActionsExt(void)
 {
-  static unsigned long game_frame_delay = 0;
-  unsigned long game_frame_delay_value;
+#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;
-  byte tape_action[MAX_PLAYERS];
+  byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
   int i;
 
-  /* detect endless loops, caused by custom element programming */
+  // detect endless loops, caused by custom element programming
   if (recursion_loop_detected && recursion_loop_depth == 0)
   {
-    char *message = getStringCat3("Internal Error ! Element ",
+    char *message = getStringCat3("Internal Error! Element ",
                                  EL_NAME(recursion_loop_element),
-                                 " caused endless loop ! Quit the game ?");
+                                 " caused endless loop! Quit the game?");
 
-    Error(ERR_WARN, "element '%s' caused endless loop in game engine",
-         EL_NAME(recursion_loop_element));
+    Warn("element '%s' caused endless loop in game engine",
+        EL_NAME(recursion_loop_element));
 
     RequestQuitGameExt(FALSE, level_editor_test_game, message);
 
-    recursion_loop_detected = FALSE;   /* if game should be continued */
+    recursion_loop_detected = FALSE;   // if game should be continued
 
     free(message);
 
@@ -11702,33 +11518,17 @@ void GameActions()
   }
 
   if (game.restart_level)
-    StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
+    StartGameActions(network.enabled, setup.autorecord, level.random_seed);
 
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-  {
-    if (level.native_em_level->lev->home == 0) /* all players at home */
-    {
-      PlayerWins(local_player);
-
-      AllPlayersGone = TRUE;
-
-      level.native_em_level->lev->home = -1;
-    }
-
-    if (level.native_em_level->ply[0]->alive == 0 &&
-       level.native_em_level->ply[1]->alive == 0 &&
-       level.native_em_level->ply[2]->alive == 0 &&
-       level.native_em_level->ply[3]->alive == 0)      /* all dead */
-      AllPlayersGone = TRUE;
-  }
+  CheckLevelSolved();
 
-  if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
+  if (game.LevelSolved && !game.LevelSolved_GameEnd)
     GameWon();
 
-  if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
+  if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
     TapeStop();
 
-  if (game_status != GAME_MODE_PLAYING)                /* status might have changed */
+  if (game_status != GAME_MODE_PLAYING)                // status might have changed
     return;
 
   game_frame_delay_value =
@@ -11737,44 +11537,69 @@ void GameActions()
   if (tape.playing && tape.warp_forward && !tape.pausing)
     game_frame_delay_value = 0;
 
-  /* ---------- main game synchronization point ---------- */
+  SetVideoFrameDelay(game_frame_delay_value);
+
+  // (de)activate virtual buttons depending on current game status
+  if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
+  {
+    if (game.all_players_gone) // if no players there to be controlled anymore
+      SetOverlayActive(FALSE);
+    else if (!tape.playing)    // if game continues after tape stopped playing
+      SetOverlayActive(TRUE);
+  }
+
+#if 0
+#if 0
+  // ---------- main game synchronization point ----------
+
+  int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+
+  Debug("game:playing:skip", "skip == %d", skip);
+
+#else
+  // ---------- main game synchronization point ----------
 
   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+#endif
+#endif
 
   if (network_playing && !network_player_action_received)
   {
-    /* try to get network player actions in time */
+    // try to get network player actions in time
 
-#if defined(NETWORK_AVALIABLE)
-    /* last chance to get network player actions without main loop delay */
+    // last chance to get network player actions without main loop delay
     HandleNetworking();
-#endif
 
-    /* game was quit by network peer */
+    // game was quit by network peer
     if (game_status != GAME_MODE_PLAYING)
       return;
 
-    if (!network_player_action_received)
-      return;          /* failed to get network player actions in time */
+    // check if network player actions still missing and game still running
+    if (!network_player_action_received && !checkGameEnded())
+      return;          // failed to get network player actions in time
 
-    /* do not yet reset "network_player_action_received" (for tape.pausing) */
+    // do not yet reset "network_player_action_received" (for tape.pausing)
   }
 
   if (tape.pausing)
     return;
 
-  /* at this point we know that we really continue executing the game */
+  // at this point we know that we really continue executing the game
 
   network_player_action_received = FALSE;
 
-  /* when playing tape, read previously recorded player input from tape data */
+  // 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" */
+  local_player->effective_mouse_action = local_player->mouse_action;
+
+  if (recorded_player_action != NULL)
+    SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
+                                recorded_player_action);
+
+  // TapePlayAction() may return NULL when toggling to "pause before death"
   if (tape.pausing)
     return;
-#endif
 
   if (tape.set_centered_player)
   {
@@ -11786,22 +11611,27 @@ void GameActions()
   {
     summarized_player_action |= stored_player[i].action;
 
-    if (!network_playing)
+    if (!network_playing && (game.team_mode || tape.playing))
       stored_player[i].effective_action = stored_player[i].action;
   }
 
-#if defined(NETWORK_AVALIABLE)
-  if (network_playing)
+  if (network_playing && !checkGameEnded())
     SendToServer_MovePlayer(summarized_player_action);
-#endif
 
-  if (!options.network && !setup.team_mode)
-    local_player->effective_action = summarized_player_action;
+  // summarize all actions at local players mapped input device position
+  // (this allows using different input devices in single player mode)
+  if (!network.enabled && !game.team_mode)
+    stored_player[map_player_action[local_player->index_nr]].effective_action =
+      summarized_player_action;
 
-  if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
+  // summarize all actions at centered player in local team mode
+  if (tape.recording &&
+      setup.team_mode && !network.enabled &&
+      setup.input_on_focus &&
+      game.centered_player_nr != -1)
   {
     for (i = 0; i < MAX_PLAYERS; i++)
-      stored_player[i].effective_action =
+      stored_player[map_player_action[i]].effective_action =
        (i == game.centered_player_nr ? summarized_player_action : 0);
   }
 
@@ -11813,49 +11643,196 @@ void GameActions()
   {
     tape_action[i] = stored_player[i].effective_action;
 
-    /* (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 */
+    /* (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 (setup.team_mode &&
+       tape.recording &&
+       tape_action[i] &&
+       !tape.player_participates[i])
+      tape.player_participates[i] = TRUE;
   }
 
-  /* only record actions from input devices, but not programmed actions */
+  SetTapeActionFromMouseAction(tape_action,
+                              &local_player->effective_mouse_action);
+
+  // only record actions from input devices, but not programmed actions
   if (tape.recording)
     TapeRecordAction(tape_action);
 
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-  {
-    GameActions_EM_Main();
-  }
-  else
+  // remember if game was played (especially after tape stopped playing)
+  if (!tape.playing && summarized_player_action)
+    game.GamePlayed = TRUE;
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+  // !!! also map player actions in single player mode !!!
+  // if (game.team_mode)
+  if (1)
   {
-    GameActions_RND();
-  }
-}
+    byte mapped_action[MAX_PLAYERS];
 
-void GameActions_EM_Main()
-{
-  byte effective_action[MAX_PLAYERS];
-  boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+#if DEBUG_PLAYER_ACTIONS
+    for (i = 0; i < MAX_PLAYERS; i++)
+      DebugContinued("", "%d, ", stored_player[i].effective_action);
+#endif
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      mapped_action[i] = stored_player[map_player_action[i]].effective_action;
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      stored_player[i].effective_action = mapped_action[i];
+
+#if DEBUG_PLAYER_ACTIONS
+    DebugContinued("", "=> ");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      DebugContinued("", "%d, ", stored_player[i].effective_action);
+    DebugContinued("game:playing:player", "\n");
+#endif
+  }
+#if DEBUG_PLAYER_ACTIONS
+  else
+  {
+    for (i = 0; i < MAX_PLAYERS; i++)
+      DebugContinued("", "%d, ", stored_player[i].effective_action);
+    DebugContinued("game:playing:player", "\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)
+  {
+    GameActions_EM_Main();
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    GameActions_SP_Main();
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+  {
+    GameActions_MM_Main();
+  }
+  else
+  {
+    GameActions_RND_Main();
+  }
+
+  BlitScreenToBitmap(backbuffer);
+
+  CheckLevelSolved();
+  CheckLevelTime();
+
+  AdvanceFrameAndPlayerCounters(-1);   // advance counters for all players
+
+  if (global.show_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();
+
+      // always draw FPS to screen after FPS value was updated
+      redraw_mask |= REDRAW_FPS;
+    }
+
+    // only draw FPS if no screen areas are deactivated (invisible warp mode)
+    if (GetDrawDeactivationMask() == REDRAW_NONE)
+      redraw_mask |= REDRAW_FPS;
+  }
+}
+
+static void GameActions_CheckSaveEngineSnapshot(void)
+{
+  if (!game.snapshot.save_snapshot)
+    return;
+
+  // clear flag for saving snapshot _before_ saving snapshot
+  game.snapshot.save_snapshot = FALSE;
+
+  SaveEngineSnapshotToList();
+}
+
+void GameActions(void)
+{
+  GameActionsExt();
+
+  GameActions_CheckSaveEngineSnapshot();
+}
+
+void GameActions_EM_Main(void)
+{
+  byte effective_action[MAX_PLAYERS];
+  boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
   int i;
 
   for (i = 0; i < MAX_PLAYERS; i++)
     effective_action[i] = stored_player[i].effective_action;
 
   GameActions_EM(effective_action, warp_mode);
+}
 
-  CheckLevelTime();
+void GameActions_SP_Main(void)
+{
+  byte effective_action[MAX_PLAYERS];
+  boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+  int i;
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    effective_action[i] = stored_player[i].effective_action;
+
+  GameActions_SP(effective_action, warp_mode);
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    if (stored_player[i].force_dropping)
+      stored_player[i].action |= KEY_BUTTON_DROP;
+
+    stored_player[i].force_dropping = FALSE;
+  }
+}
+
+void GameActions_MM_Main(void)
+{
+  boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+
+  GameActions_MM(local_player->effective_mouse_action, warp_mode);
+}
 
-  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
+void GameActions_RND_Main(void)
+{
+  GameActions_RND();
 }
 
-void GameActions_RND()
+void GameActions_RND(void)
 {
+  static struct MouseActionInfo mouse_action_last = { 0 };
+  struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
   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)
@@ -11864,20 +11841,19 @@ void GameActions_RND()
       ChangeEvent[x][y] = -1;
     }
   }
-#endif
 
   if (game.set_centered_player)
   {
     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
 
-    /* switching to "all players" only possible if all players fit to screen */
+    // switching to "all players" only possible if all players fit to screen
     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
     {
       game.centered_player_nr_next = game.centered_player_nr;
       game.set_centered_player = FALSE;
     }
 
-    /* do not switch focus to non-existing (or non-active) player */
+    // do not switch focus to non-existing (or non-active) player
     if (game.centered_player_nr_next >= 0 &&
        !stored_player[game.centered_player_nr_next].active)
     {
@@ -11887,7 +11863,7 @@ void GameActions_RND()
   }
 
   if (game.set_centered_player &&
-      ScreenMovPos == 0)       /* screen currently aligned at tile position */
+      ScreenMovPos == 0)       // screen currently aligned at tile position
   {
     int sx, sy;
 
@@ -11923,7 +11899,7 @@ void GameActions_RND()
       CheckGravityMovement(&stored_player[i]);
 #endif
 
-    /* overwrite programmed action with tape action */
+    // overwrite programmed action with tape action
     if (stored_player[i].programmed_action)
       actual_player_action = stored_player[i].programmed_action;
 
@@ -11953,36 +11929,33 @@ void GameActions_RND()
       if (player->active && player->is_pushing && player->is_moving &&
          IS_MOVING(x, y) &&
          (game.engine_version < VERSION_IDENT(2,2,0,7) ||
-          Feld[x][y] == EL_SPRING))
+          Tile[x][y] == EL_SPRING))
       {
        ContinueMoving(x, y);
 
-       /* continue moving after pushing (this is actually a bug) */
+       // continue moving after pushing (this is actually a bug)
        if (!IS_MOVING(x, y))
          Stop[x][y] = FALSE;
       }
     }
   }
 
-#if 0
-  debug_print_timestamp(0, "start main loop profiling");
-#endif
-
   SCAN_PLAYFIELD(x, y)
   {
+    Last[x][y] = Tile[x][y];
+
     ChangeCount[x][y] = 0;
     ChangeEvent[x][y] = -1;
 
-    /* this must be handled before main playfield loop */
-    if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
+    // this must be handled before main playfield loop
+    if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
     {
       MovDelay[x][y]--;
       if (MovDelay[x][y] <= 0)
        RemoveField(x, y);
     }
 
-#if USE_NEW_SNAP_DELAY
-    if (Feld[x][y] == EL_ELEMENT_SNAPPING)
+    if (Tile[x][y] == EL_ELEMENT_SNAPPING)
     {
       MovDelay[x][y]--;
       if (MovDelay[x][y] <= 0)
@@ -11990,16 +11963,16 @@ void GameActions_RND()
        RemoveField(x, y);
        TEST_DrawLevelField(x, y);
 
-       TestIfElementTouchesCustomElement(x, y);        /* for empty space */
+       TestIfElementTouchesCustomElement(x, y);        // for empty space
       }
     }
-#endif
 
 #if DEBUG
     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
     {
-      printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
-      printf("GameActions(): This should never happen!\n");
+      Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
+           x, y);
+      Debug("game:playing:GameActions_RND", "This should never happen!");
 
       ChangePage[x][y] = -1;
     }
@@ -12033,78 +12006,43 @@ void GameActions_RND()
       Blocked2Moving(x, y, &oldx, &oldy);
       if (!IS_MOVING(oldx, oldy))
       {
-       printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
-       printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
-       printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
-       printf("GameActions(): This should never happen!\n");
+       Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
+       Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
+       Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
+       Debug("game:playing:GameActions_RND", "This should never happen!");
       }
     }
 #endif
   }
 
-#if 0
-  debug_print_timestamp(0, "- time for pre-main loop:");
-#endif
-
-#if 0  // -------------------- !!! TEST ONLY !!! --------------------
-  SCAN_PLAYFIELD(x, y)
+  if (mouse_action.button)
   {
-    element = Feld[x][y];
-    graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[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];
+    int new_button = (mouse_action.button && mouse_action_last.button == 0);
 
-      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];
+    x = mouse_action.lx;
+    y = mouse_action.ly;
+    element = Tile[x][y];
 
-      if (redraw && GfxFrame[x][y] != last_gfx_frame)
-       DrawLevelGraphicAnimation(x, y, graphic2);
+    if (new_button)
+    {
+      CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
+      CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
     }
-#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
+    CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
+    CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
   }
-#endif // -------------------- !!! TEST ONLY !!! --------------------
-
-#if 0
-  debug_print_timestamp(0, "- time for TEST loop:     -->");
-#endif
 
   SCAN_PLAYFIELD(x, y)
   {
-    element = Feld[x][y];
+    element = Tile[x][y];
     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+    last_gfx_frame = GfxFrame[x][y];
 
-    ResetGfxFrame(x, y, TRUE);
+    ResetGfxFrame(x, y);
+
+    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))
@@ -12122,33 +12060,23 @@ void GameActions_RND()
       continue;
     }
 
-    /* this may take place after moving, so 'element' may have changed */
+    // this may take place after moving, so 'element' may have changed
     if (IS_CHANGING(x, y) &&
        (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
     {
       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];
+      element = Tile[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);
 
-      element = Feld[x][y];
+      element = Tile[x][y];
       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
 
       if (IS_ANIMATED(graphic) &&
@@ -12159,143 +12087,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 ||
@@ -12312,13 +12109,13 @@ void GameActions_RND()
     else if (IS_ACTIVE_BOMB(element))
       CheckDynamite(x, y);
     else if (element == EL_AMOEBA_GROWING)
-      AmoebeWaechst(x, y);
+      AmoebaGrowing(x, y);
     else if (element == EL_AMOEBA_SHRINKING)
-      AmoebaDisappearing(x, y);
+      AmoebaShrinking(x, y);
 
 #if !USE_NEW_AMOEBA_CODE
     else if (IS_AMOEBALIVE(element))
-      AmoebeAbleger(x, y);
+      AmoebaReproduce(x, y);
 #endif
 
     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
@@ -12349,7 +12146,7 @@ void GameActions_RND()
     else if (element == EL_FLAMES)
       CheckForDragon(x, y);
     else if (element == EL_EXPLOSION)
-      ;        /* drawing of correct explosion animation is handled separately */
+      ;        // drawing of correct explosion animation is handled separately
     else if (element == EL_ELEMENT_SNAPPING ||
             element == EL_DIAGONAL_SHRINKING ||
             element == EL_DIAGONAL_GROWING)
@@ -12361,8 +12158,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);
 
@@ -12370,7 +12165,7 @@ void GameActions_RND()
     {
       int jx = local_player->jx, jy = local_player->jy;
 
-      /* play the element sound at the position nearest to the player */
+      // play the element sound at the position nearest to the player
       if ((element == EL_MAGIC_WALL_FULL ||
           element == EL_MAGIC_WALL_ACTIVE ||
           element == EL_MAGIC_WALL_EMPTYING ||
@@ -12380,7 +12175,8 @@ void GameActions_RND()
           element == EL_DC_MAGIC_WALL_FULL ||
           element == EL_DC_MAGIC_WALL_ACTIVE ||
           element == EL_DC_MAGIC_WALL_EMPTYING) &&
-         ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
+         ABS(x - jx) + ABS(y - jy) <
+         ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
       {
        magic_wall_x = x;
        magic_wall_y = y;
@@ -12388,21 +12184,17 @@ void GameActions_RND()
     }
   }
 
-#if 0
-  debug_print_timestamp(0, "- time for MAIN loop:     -->");
-#endif
-
 #if USE_NEW_AMOEBA_CODE
-  /* new experimental amoeba growth stuff */
+  // new experimental amoeba growth stuff
   if (!(FrameCounter % 8))
   {
-    static unsigned long random = 1684108901;
+    static unsigned int random = 1684108901;
 
     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
     {
       x = RND(lev_fieldx);
       y = RND(lev_fieldy);
-      element = Feld[x][y];
+      element = Tile[x][y];
 
       if (!IS_PLAYER(x,y) &&
          (element == EL_EMPTY ||
@@ -12412,11 +12204,11 @@ void GameActions_RND()
           element == EL_ACID_SPLASH_LEFT ||
           element == EL_ACID_SPLASH_RIGHT))
       {
-       if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
-           (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
-           (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
-           (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
-         Feld[x][y] = EL_AMOEBA_DROP;
+       if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
+         Tile[x][y] = EL_AMOEBA_DROP;
       }
 
       random = random * 129 + 1;
@@ -12424,32 +12216,27 @@ void GameActions_RND()
   }
 #endif
 
-#if 0
-  if (game.explosions_delayed)
-#endif
-  {
-    game.explosions_delayed = FALSE;
-
-    SCAN_PLAYFIELD(x, y)
-    {
-      element = Feld[x][y];
+  game.explosions_delayed = FALSE;
 
-      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 = Tile[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))
     {
-      int element = Feld[magic_wall_x][magic_wall_y];
+      int element = Tile[magic_wall_x][magic_wall_y];
 
       if (element == EL_BD_MAGIC_WALL_FULL ||
          element == EL_BD_MAGIC_WALL_ACTIVE ||
@@ -12471,24 +12258,24 @@ void GameActions_RND()
       {
        SCAN_PLAYFIELD(x, y)
        {
-         element = Feld[x][y];
+         element = Tile[x][y];
 
          if (element == EL_MAGIC_WALL_ACTIVE ||
              element == EL_MAGIC_WALL_FULL)
          {
-           Feld[x][y] = EL_MAGIC_WALL_DEAD;
+           Tile[x][y] = EL_MAGIC_WALL_DEAD;
            TEST_DrawLevelField(x, y);
          }
          else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
                   element == EL_BD_MAGIC_WALL_FULL)
          {
-           Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
+           Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
            TEST_DrawLevelField(x, y);
          }
          else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
                   element == EL_DC_MAGIC_WALL_FULL)
          {
-           Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
+           Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
            TEST_DrawLevelField(x, y);
          }
        }
@@ -12546,21 +12333,19 @@ 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 !!! */
+
       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
        DrawLevelField(x, y);
 
       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
-       DrawLevelFieldCrumbledSand(x, y);
+       DrawLevelFieldCrumbled(x, y);
 
       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       DrawLevelFieldCrumbledNeighbours(x, y);
 
       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
        DrawTwinkleOnField(x, y);
@@ -12570,47 +12355,27 @@ void GameActions_RND()
   }
 #endif
 
-  CheckLevelTime();
-
   DrawAllPlayers();
   PlayAllPlayersSound();
 
-  if (options.debug)                   /* calculate frames per second */
+  for (i = 0; i < MAX_PLAYERS; i++)
   {
-    static unsigned long fps_counter = 0;
-    static int fps_frames = 0;
-    unsigned long fps_delay_ms = Counter() - fps_counter;
-
-    fps_frames++;
+    struct PlayerInfo *player = &stored_player[i];
 
-    if (fps_delay_ms >= 500)   /* calculate fps every 0.5 seconds */
+    if (player->show_envelope != 0 && (!player->active ||
+                                      player->MovPos == 0))
     {
-      global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
+      ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
 
-      fps_frames = 0;
-      fps_counter = Counter();
+      player->show_envelope = 0;
     }
-
-    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);
-
-    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 */
+  // use random number generator in every frame to make it less predictable
   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
     RND(1);
+
+  mouse_action_last = mouse_action;
 }
 
 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
@@ -12634,7 +12399,7 @@ static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
 }
 
-static boolean AllPlayersInVisibleScreen()
+static boolean AllPlayersInVisibleScreen(void)
 {
   int i;
 
@@ -12654,76 +12419,16 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
-#if 1
-  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 i, 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 1
-  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
-
-  int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
 
   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
+            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)
   {
@@ -12748,9 +12453,9 @@ static boolean canFallDown(struct PlayerInfo *player)
 
   return (IN_LEV_FIELD(jx, jy + 1) &&
          (IS_FREE(jx, jy + 1) ||
-          (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
-         IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
-         !IS_WALKABLE_INSIDE(Feld[jx][jy]));
+          (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
+         IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
+         !IS_WALKABLE_INSIDE(Tile[jx][jy]));
 }
 
 static boolean canPassField(int x, int y, int move_dir)
@@ -12760,12 +12465,12 @@ static boolean canPassField(int x, int y, int move_dir)
   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
   int nextx = x + dx;
   int nexty = y + dy;
-  int element = Feld[x][y];
+  int element = Tile[x][y];
 
   return (IS_PASSABLE_FROM(element, opposite_dir) &&
          !CAN_MOVE(element) &&
          IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
-         IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
+         IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
          (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
 }
 
@@ -12778,19 +12483,15 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
   int newy = y + dy;
 
   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
-         IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
-         (IS_DIGGABLE(Feld[newx][newy]) ||
-          IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+         IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
+         (IS_DIGGABLE(Tile[newx][newy]) ||
+          IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
           canPassField(newx, newy, 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;
@@ -12812,19 +12513,15 @@ 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 =
       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
     boolean player_is_standing_on_valid_field =
-      (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
-       (IS_WALKABLE(Feld[jx][jy]) &&
-       !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
+      (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
+       (IS_WALKABLE(Tile[jx][jy]) &&
+       !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
 
     if (field_under_player_is_free && !player_is_standing_on_valid_field)
       player->programmed_action = MV_DOWN;
@@ -12843,9 +12540,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;
 
@@ -12872,44 +12566,17 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
     }
   }
 
-#if 1
-  if (!options.network && game.centered_player_nr == -1 &&
+  if (!network.enabled && 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)
     return can_move;
 
-  /* check if DigField() has caused relocation of the player */
+  // check if DigField() has caused relocation of the player
   if (player->jx != jx || player->jy != jy)
-    return MP_NO_ACTION;       /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
+    return MP_NO_ACTION;       // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
 
   StorePlayer[jx][jy] = 0;
   player->last_jx = jx;
@@ -12931,12 +12598,10 @@ 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 */
+  // should better be called in MovePlayer(), but this breaks some tapes
   ScrollPlayer(player, SCROLL_INIT);
 #endif
 
@@ -12969,27 +12634,28 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
   if (player->move_delay > 0)
     return FALSE;
 
-  player->move_delay = -1;             /* set to "uninitialized" value */
+  player->move_delay = -1;             // set to "uninitialized" value
 
-  /* store if player is automatically moved to next field */
+  // store if player is automatically moved to next field
   player->is_auto_moving = (player->programmed_action != MV_NONE);
 
-  /* remove the last programmed player action */
+  // remove the last programmed player action
   player->programmed_action = 0;
 
   if (player->MovPos)
   {
-    /* should only happen if pre-1.2 tape recordings are played */
-    /* this is only for backward compatibility */
+    // should only happen if pre-1.2 tape recordings are played
+    // this is only for backward compatibility
 
     int original_move_delay_value = player->move_delay_value;
 
 #if DEBUG
-    printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
-          tape.counter);
+    Debug("game:playing:MovePlayer",
+         "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
+         tape.counter);
 #endif
 
-    /* scroll remaining steps with finest movement resolution */
+    // scroll remaining steps with finest movement resolution
     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
 
     while (player->MovPos)
@@ -13000,7 +12666,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;
@@ -13019,7 +12685,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;
@@ -13028,65 +12693,66 @@ 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;
 
     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
     {
-      /* actual player has left the screen -- scroll in that direction */
-      if (jx != old_jx)                /* player has moved horizontally */
+      // actual player has left the screen -- scroll in that direction
+      if (jx != old_jx)                // player has moved horizontally
        scroll_x += (jx - old_jx);
-      else                     /* player has moved vertically */
+      else                     // player has moved vertically
        scroll_y += (jy - old_jy);
     }
     else
     {
-      if (jx != old_jx)                /* player has moved horizontally */
+      int offset_raw = game.scroll_delay_value;
+
+      if (jx != old_jx)                // player has moved horizontally
       {
-       if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
-           (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
-         scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
+       int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
+       int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
+       int new_scroll_x = jx - MIDPOSX + offset_x;
+
+       if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
+           (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
+         scroll_x = new_scroll_x;
 
-       /* 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);
+       // don't scroll over playfield boundaries
+       scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
 
-       /* don't scroll more than one field at a time */
+       // don't scroll more than one field at a time
        scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
 
-       /* don't scroll against the player's moving direction */
+       // don't scroll against the player's moving direction
        if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
            (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
          scroll_x = old_scroll_x;
       }
-      else                     /* player has moved vertically */
+      else                     // player has moved vertically
       {
-       if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
-           (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
-         scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
+       int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
+       int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
+       int new_scroll_y = jy - MIDPOSY + offset_y;
 
-       /* 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);
+       if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
+           (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
+         scroll_y = new_scroll_y;
 
-       /* don't scroll more than one field at a time */
+       // don't scroll over playfield boundaries
+       scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
+
+       // don't scroll more than one field at a time
        scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
 
-       /* don't scroll against the player's moving direction */
+       // don't scroll against the player's moving direction
        if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
            (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
          scroll_y = old_scroll_y;
@@ -13095,22 +12761,13 @@ 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 &&
+      if (!network.enabled && game.centered_player_nr == -1 &&
          !AllPlayersInVisibleScreen())
       {
        scroll_x = old_scroll_x;
        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);
@@ -13127,7 +12784,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     else if (old_jx == jx && old_jy != jy)
       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
 
-    TEST_DrawLevelField(jx, jy);       /* for "crumbled sand" */
+    TEST_DrawLevelField(jx, jy);       // for "crumbled sand"
 
     player->last_move_dir = player->MovDir;
     player->is_moving = TRUE;
@@ -13138,7 +12795,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->drop_pressed_delay = 0;
 
 #if 0
-    /* should better be called here than above, but this breaks some tapes */
+    // should better be called here than above, but this breaks some tapes
     ScrollPlayer(player, SCROLL_INIT);
 #endif
   }
@@ -13154,10 +12811,10 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
        the player was forced to wait again for eight frames before next try) */
 
     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
-      player->move_delay = 0;  /* allow direct movement in the next frame */
+      player->move_delay = 0;  // allow direct movement in the next frame
   }
 
-  if (player->move_delay == -1)                /* not yet initialized by DigField() */
+  if (player->move_delay == -1)                // not yet initialized by DigField()
     player->move_delay = player->move_delay_value;
 
   if (game.engine_version < VERSION_IDENT(3,0,7,0))
@@ -13178,16 +12835,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)
+  if (player->MovPos == 0 && mode == SCROLL_GO_ON)     // player not moving
     return;
-#endif
 
   if (mode == SCROLL_INIT)
   {
@@ -13195,63 +12847,45 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
-       Feld[last_jx][last_jy] == EL_EMPTY)
+       Tile[last_jx][last_jy] == EL_EMPTY)
     {
-      int last_field_block_delay = 0;  /* start with no blocking at all */
+      int last_field_block_delay = 0;  // start with no blocking at all
       int block_delay_adjustment = player->block_delay_adjustment;
 
-      /* if player blocks last field, add delay for exactly one move */
+      // if player blocks last field, add delay for exactly one move
       if (player->block_last_field)
       {
        last_field_block_delay += player->move_delay_value;
 
-       /* when blocking enabled, prevent moving up despite gravity */
-#if USE_PLAYER_GRAVITY
+       // when blocking enabled, prevent moving up despite 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) */
+      // add block delay adjustment (also possible when not blocking)
       last_field_block_delay += block_delay_adjustment;
 
-      Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+      Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
       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 */
+    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;
     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
-    /* before DrawPlayer() to draw correct player graphic for this case */
+    // before DrawPlayer() to draw correct player graphic for this case
     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 */
+  if (player->MovPos == 0)     // player reached destination field
   {
     if (player->move_delay_reset_counter > 0)
     {
@@ -13259,10 +12893,10 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
       if (player->move_delay_reset_counter == 0)
       {
-       /* continue with normal speed after quickly moving through gate */
+       // continue with normal speed after quickly moving through gate
        HALVE_PLAYER_SPEED(player);
 
-       /* be able to make the next move without delay */
+       // be able to make the next move without delay
        player->move_delay = 0;
       }
     }
@@ -13270,30 +12904,32 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     player->last_jx = jx;
     player->last_jy = jy;
 
-    if (Feld[jx][jy] == EL_EXIT_OPEN ||
-       Feld[jx][jy] == EL_EM_EXIT_OPEN ||
-       Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
-       Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
-       Feld[jx][jy] == EL_SP_EXIT_OPEN ||
-       Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
+    if (Tile[jx][jy] == EL_EXIT_OPEN ||
+       Tile[jx][jy] == EL_EM_EXIT_OPEN ||
+       Tile[jx][jy] == EL_EM_EXIT_OPENING ||
+       Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
+       Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
+       Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
+       Tile[jx][jy] == EL_SP_EXIT_OPEN ||
+       Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
     {
-      DrawPlayer(player);      /* needed here only to cleanup last field */
-      RemovePlayer(player);
+      ExitPlayer(player);
 
-      if (local_player->friends_still_needed == 0 ||
-         IS_SP_ELEMENT(Feld[jx][jy]))
-       PlayerWins(player);
+      if (game.players_still_needed == 0 &&
+         (game.friends_still_needed == 0 ||
+          IS_SP_ELEMENT(Tile[jx][jy])))
+       LevelSolved();
     }
 
-    /* this breaks one level: "machine", level 000 */
+    // this breaks one level: "machine", level 000
     {
       int move_direction = player->MovDir;
       int enter_side = MV_DIR_OPPOSITE(move_direction);
       int leave_side = move_direction;
       int old_jx = last_jx;
       int old_jy = last_jy;
-      int old_element = Feld[old_jx][old_jy];
-      int new_element = Feld[jx][jy];
+      int old_element = Tile[old_jx][old_jy];
+      int new_element = Tile[jx][jy];
 
       if (IS_CUSTOM_ELEMENT(old_element))
        CheckElementChangeByPlayer(old_jx, old_jy, old_element,
@@ -13312,7 +12948,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                          CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
 
-      CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
+      CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
                                        CE_MOVE_OF_X, move_direction);
     }
 
@@ -13324,13 +12960,13 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       /* needed because pushed element has not yet reached its destination,
         so it would trigger a change event at its previous field location */
       if (!player->is_pushing)
-       TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
+       TestIfElementTouchesCustomElement(jx, jy);      // for empty space
 
       if (!player->active)
        RemovePlayer(player);
     }
 
-    if (!local_player->LevelSolved && level.use_step_counter)
+    if (!game.LevelSolved && level.use_step_counter)
     {
       int i;
 
@@ -13343,44 +12979,38 @@ 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 (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      else if (game.no_time_limit && !game.all_players_gone)
       {
        game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
 
        DisplayGameControlValues();
       }
-#else
-      else if (level.time == 0 && !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);
   }
 }
 
 void ScrollScreen(struct PlayerInfo *player, int mode)
 {
-  static unsigned long screen_frame_counter = 0;
+  static unsigned int screen_frame_counter = 0;
 
   if (mode == SCROLL_INIT)
   {
-    /* set scrolling step size according to actual player's moving speed */
+    // set scrolling step size according to actual player's moving speed
     ScrollStepSize = TILEX / player->move_delay_value;
 
     screen_frame_counter = FrameCounter;
@@ -13413,11 +13043,11 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
   };
   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 */
+    // 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] =
   {
@@ -13426,7 +13056,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     MV_UP   | MV_DOWN,
     MV_LEFT | MV_RIGHT
   };
-  int center_element = Feld[x][y];     /* should always be non-moving! */
+  int center_element = Tile[x][y];     // should always be non-moving!
   int i;
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
@@ -13440,33 +13070,44 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     if (!IN_LEV_FIELD(xx, yy))
       continue;
 
-    if (IS_PLAYER(x, y))
+    if (IS_PLAYER(x, y))               // player found at center element
     {
       struct PlayerInfo *player = PLAYERINFO(x, y);
 
       if (game.engine_version < VERSION_IDENT(3,0,7,0))
-       border_element = Feld[xx][yy];          /* may be moving! */
+       border_element = Tile[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 = Tile[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 */
+       continue;               // center and border element do not touch
 
       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
                                 player->index_bit, border_side);
       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, border_side);
+
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(x, y)->initial_element;
+
+       CheckElementChangeBySide(xx, yy, border_element, player_element,
+                                CE_TOUCHING_X, border_side);
+      }
     }
-    else if (IS_PLAYER(xx, yy))
+    else if (IS_PLAYER(xx, yy))                // player found at border element
     {
       struct PlayerInfo *player = PLAYERINFO(xx, yy);
 
       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
       {
        if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
-         continue;             /* center and border element do not touch */
+         continue;             // center and border element do not touch
       }
 
       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
@@ -13474,13 +13115,23 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       CheckTriggeredElementChangeByPlayer(x, y, center_element,
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, center_side);
+
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+       CheckElementChangeBySide(x, y, center_element, player_element,
+                                CE_TOUCHING_X, center_side);
+      }
+
       break;
     }
   }
 }
 
-#if USE_ELEMENT_TOUCHING_BUGFIX
-
 void TestIfElementTouchesCustomElement(int x, int y)
 {
   static int xy[4][2] =
@@ -13492,11 +13143,11 @@ void TestIfElementTouchesCustomElement(int x, int y)
   };
   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 */
+    // 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] =
   {
@@ -13506,7 +13157,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
     MV_LEFT | MV_RIGHT
   };
   boolean change_center_element = FALSE;
-  int center_element = Feld[x][y];     /* should always be non-moving! */
+  int center_element = Tile[x][y];     // should always be non-moving!
   int border_element_old[NUM_DIRECTIONS];
   int i;
 
@@ -13522,13 +13173,13 @@ void TestIfElementTouchesCustomElement(int x, int y)
       continue;
 
     if (game.engine_version < VERSION_IDENT(3,0,7,0))
-      border_element = Feld[xx][yy];   /* may be moving! */
+      border_element = Tile[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 = Tile[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 */
+      continue;                        // center and border element do not touch
 
     border_element_old[i] = border_element;
   }
@@ -13543,97 +13194,49 @@ void TestIfElementTouchesCustomElement(int x, int y)
     if (border_element == -1)
       continue;
 
-    /* check for change of border element */
+    // check for change of border element
     CheckElementChangeBySide(xx, yy, border_element, center_element,
                             CE_TOUCHING_X, center_side);
-  }
-
-  for (i = 0; i < NUM_DIRECTIONS; i++)
-  {
-    int border_side = trigger_sides[i][1];
-    int border_element = border_element_old[i];
-
-    if (border_element == -1)
-      continue;
 
-    /* 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);
+    // (center element cannot be player, so we dont have to check this here)
   }
-}
-
-#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;
+    int border_element = border_element_old[i];
 
-    if (!IN_LEV_FIELD(xx, yy))
+    if (border_element == -1)
       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) */
+    // 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);
+    if (IS_PLAYER(xx, yy))
+    {
+      /* use player element that is initially defined in the level playfield,
+        not the player element that corresponds to the runtime player number
+        (example: a level that contains EL_PLAYER_3 as the only player would
+        incorrectly give EL_PLAYER_1 for "player->element_nr") */
+      int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+      CheckElementChangeBySide(x, y, center_element, player_element,
+                              CE_TOUCHING_X, border_side);
+    }
   }
 }
 
-#endif
-
 void TestIfElementHitsCustomElement(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 hitting_element = Tile[x][y];
   int touched_element;
 
   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
@@ -13658,82 +13261,31 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
       CheckElementChangeBySide(x, y, hitting_element, touched_element,
                               CE_HITTING_X, touched_side);
 
-      CheckElementChangeBySide(hitx, hity, touched_element,
-                              hitting_element, CE_HIT_BY_X, hitting_side);
+      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+                              CE_HIT_BY_X, hitting_side);
 
       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
                               CE_HIT_BY_SOMETHING, opposite_direction);
+
+      if (IS_PLAYER(hitx, hity))
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(hitx, hity)->initial_element;
+
+       CheckElementChangeBySide(x, y, hitting_element, player_element,
+                                CE_HITTING_X, touched_side);
+      }
     }
   }
 
-  /* "hitting something" is also true when hitting the playfield border */
+  // "hitting something" is also true when hitting the playfield border
   CheckElementChangeBySide(x, y, hitting_element, touched_element,
                           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;
@@ -13803,7 +13355,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 {
   int i, kill_x = -1, kill_y = -1;
-  int bad_element = Feld[bad_x][bad_y];
+  int bad_element = Tile[bad_x][bad_y];
   static int test_xy[4][2] =
   {
     { 0, -1 },
@@ -13826,7 +13378,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
     MV_DOWN
   };
 
-  if (bad_element == EL_EXPLOSION)     /* skip just exploding bad things */
+  if (bad_element == EL_EXPLOSION)     // skip just exploding bad things
     return;
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
@@ -13835,13 +13387,14 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
     test_x = bad_x + test_xy[i][0];
     test_y = bad_y + test_xy[i][1];
+
     if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
     test_move_dir =
       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
 
-    test_element = Feld[test_x][test_y];
+    test_element = Tile[test_x][test_y];
 
     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
        2nd case: DONT_TOUCH style bad thing does not move away from good thing
@@ -13849,28 +13402,30 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
        (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
     {
-      /* good thing is player or penguin that does not move away */
+      // good thing is player or penguin that does not move away
       if (IS_PLAYER(test_x, test_y))
       {
        struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
 
        if (bad_element == EL_ROBOT && player->is_moving)
-         continue;     /* robot does not kill player if he is moving */
+         continue;     // robot does not kill player if he is moving
 
        if (game.engine_version >= VERSION_IDENT(3,0,7,0))
        {
          if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
-           continue;           /* center and border element do not touch */
+           continue;           // center and border element do not touch
        }
 
        kill_x = test_x;
        kill_y = test_y;
+
        break;
       }
       else if (test_element == EL_PENGUIN)
       {
        kill_x = test_x;
        kill_y = test_y;
+
        break;
       }
     }
@@ -13893,6 +13448,63 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
   }
 }
 
+void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
+{
+  int bad_element = Tile[bad_x][bad_y];
+  int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
+  int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
+  int test_x = bad_x + dx, test_y = bad_y + dy;
+  int test_move_dir, test_element;
+  int kill_x = -1, kill_y = -1;
+
+  if (!IN_LEV_FIELD(test_x, test_y))
+    return;
+
+  test_move_dir =
+    (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
+
+  test_element = Tile[test_x][test_y];
+
+  if (test_move_dir != bad_move_dir)
+  {
+    // good thing can be player or penguin that does not move away
+    if (IS_PLAYER(test_x, test_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
+
+      /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
+        player as being hit when he is moving towards the bad thing, because
+        the "get hit by" condition would be lost after the player stops) */
+      if (player->MovPos != 0 && player->MovDir == bad_move_dir)
+       return;         // player moves away from bad thing
+
+      kill_x = test_x;
+      kill_y = test_y;
+    }
+    else if (test_element == EL_PENGUIN)
+    {
+      kill_x = test_x;
+      kill_y = test_y;
+    }
+  }
+
+  if (kill_x != -1 || kill_y != -1)
+  {
+    if (IS_PLAYER(kill_x, kill_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
+
+      if (player->shield_deadly_time_left > 0 &&
+         !IS_INDESTRUCTIBLE(bad_element))
+       Bang(bad_x, bad_y);
+      else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
+       KillPlayer(player);
+    }
+    else
+      Bang(kill_x, kill_y);
+  }
+}
+
 void TestIfPlayerTouchesBadThing(int x, int y)
 {
   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
@@ -13943,7 +13555,7 @@ void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
     if (!IN_LEV_FIELD(x, y))
       continue;
 
-    element = Feld[x][y];
+    element = Tile[x][y];
     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
        element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
     {
@@ -13964,6 +13576,12 @@ void KillPlayer(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+#if 0
+  Debug("game:playing:KillPlayer",
+       "0: killed == %d, active == %d, reanimated == %d",
+       player->killed, player->active, player->reanimated);
+#endif
+
   /* the following code was introduced to prevent an infinite loop when calling
      -> Bang()
      -> CheckTriggeredElementChangeExt()
@@ -13981,15 +13599,31 @@ void KillPlayer(struct PlayerInfo *player)
 
   player->killed = TRUE;
 
-  /* remove accessible field at the player's position */
-  Feld[jx][jy] = EL_EMPTY;
+  // remove accessible field at the player's position
+  Tile[jx][jy] = EL_EMPTY;
 
-  /* deactivate shield (else Bang()/Explode() would not work right) */
+  // deactivate shield (else Bang()/Explode() would not work right)
   player->shield_normal_time_left = 0;
   player->shield_deadly_time_left = 0;
 
+#if 0
+  Debug("game:playing:KillPlayer",
+       "1: killed == %d, active == %d, reanimated == %d",
+       player->killed, player->active, player->reanimated);
+#endif
+
   Bang(jx, jy);
-  BuryPlayer(player);
+
+#if 0
+  Debug("game:playing:KillPlayer",
+       "2: killed == %d, active == %d, reanimated == %d",
+       player->killed, player->active, player->reanimated);
+#endif
+
+  if (player->reanimated)      // killed player may have been reanimated
+    player->killed = player->reanimated = FALSE;
+  else
+    BuryPlayer(player);
 }
 
 static void KillPlayerUnlessEnemyProtected(int x, int y)
@@ -14014,8 +13648,12 @@ void BuryPlayer(struct PlayerInfo *player)
   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
   PlayLevelSound(jx, jy, SND_GAME_LOSING);
 
-  player->GameOver = TRUE;
   RemovePlayer(player);
+
+  player->buried = TRUE;
+
+  if (game.all_players_gone)
+    game.GameOver = TRUE;
 }
 
 void RemovePlayer(struct PlayerInfo *player)
@@ -14026,6 +13664,9 @@ void RemovePlayer(struct PlayerInfo *player)
   player->present = FALSE;
   player->active = FALSE;
 
+  // required for some CE actions (even if the player is not active anymore)
+  player->MovPos = 0;
+
   if (!ExplodeField[jx][jy])
     StorePlayer[jx][jy] = 0;
 
@@ -14037,13 +13678,24 @@ void RemovePlayer(struct PlayerInfo *player)
       found = TRUE;
 
   if (!found)
-    AllPlayersGone = TRUE;
+  {
+    game.all_players_gone = TRUE;
+    game.GameOver = TRUE;
+  }
+
+  game.exit_x = game.robot_wheel_x = jx;
+  game.exit_y = game.robot_wheel_y = jy;
+}
+
+void ExitPlayer(struct PlayerInfo *player)
+{
+  DrawPlayer(player);  // needed here only to cleanup last field
+  RemovePlayer(player);
 
-  ExitX = ZX = jx;
-  ExitY = ZY = jy;
+  if (game.players_still_needed > 0)
+    game.players_still_needed--;
 }
 
-#if USE_NEW_SNAP_DELAY
 static void setFieldForSnapping(int x, int y, int element, int direction)
 {
   struct ElementInfo *ei = &element_info[element];
@@ -14052,7 +13704,7 @@ static void setFieldForSnapping(int x, int y, int element, int direction)
   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
                IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
 
-  Feld[x][y] = EL_ELEMENT_SNAPPING;
+  Tile[x][y] = EL_ELEMENT_SNAPPING;
   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
 
   ResetGfxAnimation(x, y);
@@ -14062,7 +13714,6 @@ static void setFieldForSnapping(int x, int y, int element, int direction)
   GfxDir[x][y] = direction;
   GfxFrame[x][y] = -1;
 }
-#endif
 
 /*
   =============================================================================
@@ -14078,10 +13729,10 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player,
 {
   int jx, jy, dx, dy, xx, yy;
 
-  if (real_dx == 0 || real_dy == 0)    /* no diagonal direction => push */
+  if (real_dx == 0 || real_dy == 0)    // no diagonal direction => push
     return TRUE;
 
-  /* diagonal direction: check alternative direction */
+  // diagonal direction: check alternative direction
   jx = player->jx;
   jy = player->jy;
   dx = x - jx;
@@ -14089,7 +13740,7 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player,
   xx = jx + (dx == 0 ? real_dx : 0);
   yy = jy + (dy == 0 ? real_dy : 0);
 
-  return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
+  return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
 }
 
 /*
@@ -14118,15 +13769,11 @@ static int DigField(struct PlayerInfo *player,
                        dy == +1 ? MV_DOWN  : MV_NONE);
   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 old_element = Tile[jx][jy];
   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
-#else
-  int element;
-#endif
   int collect_count;
 
-  if (is_player)               /* function can also be called by EL_PENGUIN */
+  if (is_player)               // function can also be called by EL_PENGUIN
   {
     if (player->MovPos == 0)
     {
@@ -14134,10 +13781,10 @@ static int DigField(struct PlayerInfo *player,
       player->is_collecting = FALSE;
     }
 
-    if (player->MovPos == 0)   /* last pushing move finished */
+    if (player->MovPos == 0)   // last pushing move finished
       player->is_pushing = FALSE;
 
-    if (mode == DF_NO_PUSH)    /* player just stopped pushing */
+    if (mode == DF_NO_PUSH)    // player just stopped pushing
     {
       player->is_switching = FALSE;
       player->push_delay = -1;
@@ -14146,31 +13793,25 @@ 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];
 
-  /* in case of element dropped at player position, check background */
+  // in case of element dropped at player position, check background
   else if (Back[jx][jy] != EL_EMPTY &&
           game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
 
   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
-    return MP_NO_ACTION;       /* field has no opening in this direction */
+    return MP_NO_ACTION;       // field has no opening in this direction
 
   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
-    return MP_NO_ACTION;       /* field has no opening in this 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);
 
-    Feld[jx][jy] = player->artwork_element;
+    Tile[jx][jy] = player->artwork_element;
     InitMovingField(jx, jy, MV_DOWN);
     Store[jx][jy] = EL_ACID;
     ContinueMoving(jx, jy);
@@ -14178,29 +13819,20 @@ 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;
 
-  if (!is_player && !IS_COLLECTIBLE(element))  /* penguin cannot collect it */
+  if (!is_player && !IS_COLLECTIBLE(element))  // penguin cannot collect it
     return MP_NO_ACTION;
 
   if (game.engine_version < VERSION_IDENT(2,2,0,0))
@@ -14217,23 +13849,16 @@ static int DigField(struct PlayerInfo *player,
     if (element == EL_DC_LANDMINE)
       Bang(x, y);
 
-    if (Feld[x][y] != element)         /* field changed by snapping */
+    if (Tile[x][y] != element)         // field changed by snapping
       return MP_ACTION;
 
     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
+    return MP_NO_ACTION;       // player cannot walk here due to gravity
 
   if (player_can_move &&
       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
@@ -14258,19 +13883,21 @@ static int DigField(struct PlayerInfo *player,
     }
     else if (element == EL_EXIT_OPEN ||
             element == EL_EM_EXIT_OPEN ||
+            element == EL_EM_EXIT_OPENING ||
             element == EL_STEEL_EXIT_OPEN ||
             element == EL_EM_STEEL_EXIT_OPEN ||
+            element == EL_EM_STEEL_EXIT_OPENING ||
             element == EL_SP_EXIT_OPEN ||
             element == EL_SP_EXIT_OPENING)
     {
-      sound_action = ACTION_PASSING;   /* player is passing exit */
+      sound_action = ACTION_PASSING;   // player is passing exit
     }
     else if (element == EL_EMPTY)
     {
-      sound_action = ACTION_MOVING;            /* nothing to walk on */
+      sound_action = ACTION_MOVING;            // nothing to walk on
     }
 
-    /* play sound from background or player, whatever is available */
+    // play sound from background or player, whatever is available
     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
     else
@@ -14280,9 +13907,9 @@ static int DigField(struct PlayerInfo *player,
           IS_PASSABLE(element) && canPassField(x, y, move_direction))
   {
     if (!ACCESS_FROM(element, opposite_direction))
-      return MP_NO_ACTION;     /* field not accessible from this direction */
+      return MP_NO_ACTION;     // field not accessible from this direction
 
-    if (CAN_MOVE(element))     /* only fixed elements can be passed! */
+    if (CAN_MOVE(element))     // only fixed elements can be passed!
       return MP_NO_ACTION;
 
     if (IS_EM_GATE(element))
@@ -14330,37 +13957,25 @@ 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 */
+    // automatically move to the next field with double speed
     player->programmed_action = move_direction;
 
     if (player->move_delay_reset_counter == 0)
     {
-      player->move_delay_reset_counter = 2;    /* two double speed steps */
+      player->move_delay_reset_counter = 2;    // two double speed steps
 
       DOUBLE_PLAYER_SPEED(player);
     }
@@ -14384,14 +13999,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
+       TestIfElementTouchesCustomElement(x, y);        // for empty space
 
       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
                                          player->index_bit, dig_side);
@@ -14415,13 +14026,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)
     {
@@ -14461,8 +14068,8 @@ static int DigField(struct PlayerInfo *player,
     {
       player->num_white_keys++;
 
-      /* display white keys? */
-      /* DrawGameDoorValues(); */
+      // display white keys?
+      // DrawGameDoorValues();
     }
     else if (IS_ENVELOPE(element))
     {
@@ -14481,7 +14088,7 @@ static int DigField(struct PlayerInfo *player,
       RedrawAllInvisibleElementsForMagnifier();
     }
     else if (IS_DROPPABLE(element) ||
-            IS_THROWABLE(element))     /* can be collected and dropped */
+            IS_THROWABLE(element))     // can be collected and dropped
     {
       int i;
 
@@ -14496,17 +14103,15 @@ static int DigField(struct PlayerInfo *player,
     }
     else if (collect_count > 0)
     {
-      local_player->gems_still_needed -= collect_count;
-      if (local_player->gems_still_needed < 0)
-       local_player->gems_still_needed = 0;
+      game.gems_still_needed -= collect_count;
+      if (game.gems_still_needed < 0)
+       game.gems_still_needed = 0;
 
-#if 1
-      game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+      game.snapshot.collected_item = TRUE;
+
+      game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
 
       DisplayGameControlValues();
-#else
-      DrawGameValue_Emeralds(local_player->gems_still_needed);
-#endif
     }
 
     RaiseScoreElement(element);
@@ -14518,14 +14123,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
+       TestIfElementTouchesCustomElement(x, y);        // for empty space
 
       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
                                          player->index_bit, dig_side);
@@ -14556,7 +14157,7 @@ static int DigField(struct PlayerInfo *player,
            IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
       return MP_NO_ACTION;
 
-    /* do not push elements already moving away faster than player */
+    // do not push elements already moving away faster than player
     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
        ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
       return MP_NO_ACTION;
@@ -14583,7 +14184,7 @@ static int DigField(struct PlayerInfo *player,
     if (!(IN_LEV_FIELD(nextx, nexty) &&
          (IS_FREE(nextx, nexty) ||
           (IS_SB_ELEMENT(element) &&
-           Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
+           Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
           (IS_CUSTOM_ELEMENT(element) &&
            CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
       return MP_NO_ACTION;
@@ -14591,14 +14192,14 @@ static int DigField(struct PlayerInfo *player,
     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
       return MP_NO_ACTION;
 
-    if (player->push_delay == -1)      /* new pushing; restart delay */
+    if (player->push_delay == -1)      // new pushing; restart delay
       player->push_delay = 0;
 
     if (player->push_delay < player->push_delay_value &&
        !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
        element != EL_SPRING && element != EL_BALLOON)
     {
-      /* make sure that there is no move delay before next try to push */
+      // make sure that there is no move delay before next try to push
       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
        player->move_delay = 0;
 
@@ -14614,19 +14215,29 @@ static int DigField(struct PlayerInfo *player,
 
     if (IS_SB_ELEMENT(element))
     {
+      boolean sokoban_task_solved = FALSE;
+
       if (element == EL_SOKOBAN_FIELD_FULL)
       {
        Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
-       local_player->sokobanfields_still_needed++;
+
+       IncrementSokobanFieldsNeeded();
+       IncrementSokobanObjectsNeeded();
       }
 
-      if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
+      if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
       {
        Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
-       local_player->sokobanfields_still_needed--;
+
+       DecrementSokobanFieldsNeeded();
+       DecrementSokobanObjectsNeeded();
+
+       // sokoban object was pushed from empty field to sokoban field
+       if (Back[x][y] == EL_EMPTY)
+         sokoban_task_solved = TRUE;
       }
 
-      Feld[x][y] = EL_SOKOBAN_OBJECT;
+      Tile[x][y] = EL_SOKOBAN_OBJECT;
 
       if (Back[x][y] == Back[nextx][nexty])
        PlayLevelSoundAction(x, y, ACTION_PUSHING);
@@ -14637,10 +14248,14 @@ static int DigField(struct PlayerInfo *player,
        PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
                                    ACTION_FILLING);
 
-      if (local_player->sokobanfields_still_needed == 0 &&
-         game.emulation == EMU_SOKOBAN)
+      if (sokoban_task_solved &&
+         game.sokoban_fields_still_needed == 0 &&
+         game.sokoban_objects_still_needed == 0 &&
+         (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
       {
-       PlayerWins(player);
+       game.players_still_needed = 0;
+
+       LevelSolved();
 
        PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
       }
@@ -14662,9 +14277,9 @@ static int DigField(struct PlayerInfo *player,
     if (game.engine_version < VERSION_IDENT(2,2,0,7))
       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
     else
-      player->push_delay_value = -1;   /* get new value later */
+      player->push_delay_value = -1;   // get new value later
 
-    /* check for element change _after_ element has been pushed */
+    // check for element change _after_ element has been pushed
     if (game.use_change_when_pushing_bug)
     {
       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
@@ -14691,10 +14306,10 @@ static int DigField(struct PlayerInfo *player,
 
     if (element == EL_ROBOT_WHEEL)
     {
-      Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
-      ZX = x;
-      ZY = y;
+      Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
 
+      game.robot_wheel_x = x;
+      game.robot_wheel_y = y;
       game.robot_wheel_active = TRUE;
 
       TEST_DrawLevelField(x, y);
@@ -14705,10 +14320,17 @@ static int DigField(struct PlayerInfo *player,
 
       SCAN_PLAYFIELD(xx, yy)
       {
-       if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
+       if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
+       {
          Bang(xx, yy);
-       else if (Feld[xx][yy] == EL_SP_TERMINAL)
-         Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
+       }
+       else if (Tile[xx][yy] == EL_SP_TERMINAL)
+       {
+         Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
+
+         ResetGfxAnimation(xx, yy);
+         TEST_DrawLevelField(xx, yy);
+       }
       }
     }
     else if (IS_BELT_SWITCH(element))
@@ -14748,27 +14370,24 @@ static int DigField(struct PlayerInfo *player,
     }
     else if (element == EL_LAMP)
     {
-      Feld[x][y] = EL_LAMP_ACTIVE;
-      local_player->lights_still_needed--;
+      Tile[x][y] = EL_LAMP_ACTIVE;
+      game.lights_still_needed--;
 
       ResetGfxAnimation(x, y);
       TEST_DrawLevelField(x, y);
     }
     else if (element == EL_TIME_ORB_FULL)
     {
-      Feld[x][y] = EL_TIME_ORB_EMPTY;
+      Tile[x][y] = EL_TIME_ORB_EMPTY;
 
       if (level.time > 0 || level.use_time_orb_bug)
       {
        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);
@@ -14779,13 +14398,13 @@ static int DigField(struct PlayerInfo *player,
     {
       int xx, yy;
 
-      game.ball_state = !game.ball_state;
+      game.ball_active = !game.ball_active;
 
       SCAN_PLAYFIELD(xx, yy)
       {
-       int e = Feld[xx][yy];
+       int e = Tile[xx][yy];
 
-       if (game.ball_state)
+       if (game.ball_active)
        {
          if (e == EL_EMC_MAGIC_BALL)
            CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
@@ -14842,9 +14461,9 @@ static int DigField(struct PlayerInfo *player,
 
   player->push_delay = -1;
 
-  if (is_player)               /* function can also be called by EL_PENGUIN */
+  if (is_player)               // function can also be called by EL_PENGUIN
   {
-    if (Feld[x][y] != element)         /* really digged/collected something */
+    if (Tile[x][y] != element)         // really digged/collected something
     {
       player->is_collecting = !player->is_digging;
       player->is_active = TRUE;
@@ -14856,7 +14475,7 @@ static int DigField(struct PlayerInfo *player,
 
 static boolean DigFieldByCE(int x, int y, int digging_element)
 {
-  int element = Feld[x][y];
+  int element = Tile[x][y];
 
   if (!IS_FREE(x, y))
   {
@@ -14864,7 +14483,7 @@ static boolean DigFieldByCE(int x, int y, int digging_element)
                  IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
                  ACTION_BREAKING);
 
-    /* no element can dig solid indestructible elements */
+    // no element can dig solid indestructible elements
     if (IS_INDESTRUCTIBLE(element) &&
        !IS_DIGGABLE(element) &&
        !IS_COLLECTIBLE(element))
@@ -14887,7 +14506,7 @@ static boolean DigFieldByCE(int x, int y, int digging_element)
       TEST_DrawLevelField(x, y);
     }
 
-    /* if digged element was about to explode, prevent the explosion */
+    // if digged element was about to explode, prevent the explosion
     ExplodeField[x][y] = EX_TYPE_NONE;
 
     PlayLevelSoundAction(x, y, action);
@@ -14895,20 +14514,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 */
+  // 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;
 }
@@ -14950,14 +14558,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 */
+  // 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;
 
@@ -14985,7 +14588,7 @@ static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
     player->is_collecting = FALSE;
   }
 
-  if (player->MovPos != 0)     /* prevent graphic bugs in versions < 2.2.0 */
+  if (player->MovPos != 0)     // prevent graphic bugs in versions < 2.2.0
     TEST_DrawLevelField(player->last_jx, player->last_jy);
 
   TEST_DrawLevelField(x, y);
@@ -14999,25 +14602,13 @@ 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
      element can be dropped (this is especially important if the next element
      is dynamite, which can be placed on background for historical reasons) */
-  if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
+  if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
     return MP_ACTION;
 
   if (IS_THROWABLE(drop_element))
@@ -15029,31 +14620,34 @@ static boolean DropElement(struct PlayerInfo *player)
       return FALSE;
   }
 
-  old_element = Feld[dropx][dropy];    /* old element at dropping position */
-  new_element = drop_element;          /* default: no change when dropping */
+  old_element = Tile[dropx][dropy];    // old element at dropping position
+  new_element = drop_element;          // default: no change when dropping
 
-  /* check if player is active, not moving and ready to drop */
+  // check if player is active, not moving and ready to drop
   if (!player->active || player->MovPos || player->drop_delay > 0)
     return FALSE;
 
-  /* check if player has anything that can be dropped */
+  // check if player has anything that can be dropped
   if (new_element == EL_UNDEFINED)
     return FALSE;
 
-  /* check if drop key was pressed long enough for EM style dynamite */
+  // 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;
 
-  /* check if anything can be dropped at the current position */
+  // check if anything can be dropped at the current position
   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
     return FALSE;
 
-  /* collected custom elements can only be dropped on empty fields */
+  // collected custom elements can only be dropped on empty fields
   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
     return FALSE;
 
   if (old_element != EL_EMPTY)
-    Back[dropx][dropy] = old_element;  /* store old element on this field */
+    Back[dropx][dropy] = old_element;  // store old element on this field
 
   ResetGfxAnimation(dropx, dropy);
   ResetRandomAnimationValue(dropx, dropy);
@@ -15075,16 +14669,16 @@ static boolean DropElement(struct PlayerInfo *player)
        new_element = EL_SP_DISK_RED_ACTIVE;
     }
 
-    Feld[dropx][dropy] = new_element;
+    Tile[dropx][dropy] = new_element;
 
     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
-                         el2img(Feld[dropx][dropy]), 0);
+                         el2img(Tile[dropx][dropy]), 0);
 
     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
 
-    /* needed if previous element just changed to "empty" in the last frame */
-    ChangeCount[dropx][dropy] = 0;     /* allow at least one more change */
+    // needed if previous element just changed to "empty" in the last frame
+    ChangeCount[dropx][dropy] = 0;     // allow at least one more change
 
     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
                               player->index_bit, drop_side);
@@ -15094,44 +14688,34 @@ static boolean DropElement(struct PlayerInfo *player)
 
     TestIfElementTouchesCustomElement(dropx, dropy);
   }
-  else         /* player is dropping a dyna bomb */
+  else         // player is dropping a dyna bomb
   {
     player->dynabombs_left--;
 
-    Feld[dropx][dropy] = new_element;
+    Tile[dropx][dropy] = new_element;
 
     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
-                         el2img(Feld[dropx][dropy]), 0);
+                         el2img(Tile[dropx][dropy]), 0);
 
     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
   }
 
-  if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
+  if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
     InitField_WithBug1(dropx, dropy, FALSE);
 
-  new_element = Feld[dropx][dropy];    /* element might have changed */
+  new_element = Tile[dropx][dropy];    // element might have changed
 
   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
   {
-    int move_direction, nextx, nexty;
-
     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
       MovDir[dropx][dropy] = drop_direction;
 
-    move_direction = MovDir[dropx][dropy];
-    nextx = dropx + GET_DX_FROM_DIR(move_direction);
-    nexty = dropy + GET_DY_FROM_DIR(move_direction);
-
-    ChangeCount[dropx][dropy] = 0;     /* allow at least one more change */
+    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 */
+    // 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);
@@ -15146,14 +14730,14 @@ static boolean DropElement(struct PlayerInfo *player)
   return TRUE;
 }
 
-/* ------------------------------------------------------------------------- */
-/* game sound playing functions                                              */
-/* ------------------------------------------------------------------------- */
+// ----------------------------------------------------------------------------
+// game sound playing functions
+// ----------------------------------------------------------------------------
 
 static int *loop_sound_frame = NULL;
 static int *loop_sound_volume = NULL;
 
-void InitPlayLevelSound()
+void InitPlayLevelSound(void)
 {
   int num_sounds = getSoundListSize();
 
@@ -15220,7 +14804,7 @@ static void PlayLevelSoundNearest(int x, int y, int sound_action)
 
 static void PlayLevelSoundAction(int x, int y, int action)
 {
-  PlayLevelSoundElementAction(x, y, Feld[x][y], action);
+  PlayLevelSoundElementAction(x, y, Tile[x][y], action);
 }
 
 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
@@ -15242,7 +14826,7 @@ static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
 
 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
 {
-  int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
+  int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
 
   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
     PlayLevelSound(x, y, sound_effect);
@@ -15250,95 +14834,126 @@ static void PlayLevelSoundActionIfLoop(int x, int y, int action)
 
 static void StopLevelSoundActionIfLoop(int x, int y, int action)
 {
-  int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
+  int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
 
   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
     StopSound(sound_effect);
 }
 
-static void PlayLevelMusic()
+static int getLevelMusicNr(void)
 {
   if (levelset.music[level_nr] != MUS_UNDEFINED)
-    PlayMusic(levelset.music[level_nr]);       /* from config file */
+    return levelset.music[level_nr];           // from config file
   else
-    PlayMusic(MAP_NOCONF_MUSIC(level_nr));     /* from music dir */
+    return MAP_NOCONF_MUSIC(level_nr);         // from music dir
+}
+
+static void FadeLevelSounds(void)
+{
+  FadeSounds();
+}
+
+static void FadeLevelMusic(void)
+{
+  int music_nr = getLevelMusicNr();
+  char *curr_music = getCurrentlyPlayingMusicFilename();
+  char *next_music = getMusicInfoEntryFilename(music_nr);
+
+  if (!strEqual(curr_music, next_music))
+    FadeMusic();
+}
+
+void FadeLevelSoundsAndMusic(void)
+{
+  FadeLevelSounds();
+  FadeLevelMusic();
+}
+
+static void PlayLevelMusic(void)
+{
+  int music_nr = getLevelMusicNr();
+  char *curr_music = getCurrentlyPlayingMusicFilename();
+  char *next_music = getMusicInfoEntryFilename(music_nr);
+
+  if (!strEqual(curr_music, next_music))
+    PlayMusicLoop(music_nr);
 }
 
 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
 {
-  int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
-  int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
-  int x = xx - 1 - offset;
-  int y = yy - 1 - offset;
+  int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
+  int offset = 0;
+  int x = xx - offset;
+  int y = yy - offset;
 
   switch (sample)
   {
-    case SAMPLE_blank:
+    case SOUND_blank:
       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
       break;
 
-    case SAMPLE_roll:
+    case SOUND_roll:
       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
       break;
 
-    case SAMPLE_stone:
+    case SOUND_stone:
       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
       break;
 
-    case SAMPLE_nut:
+    case SOUND_nut:
       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
       break;
 
-    case SAMPLE_crack:
+    case SOUND_crack:
       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
       break;
 
-    case SAMPLE_bug:
+    case SOUND_bug:
       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
       break;
 
-    case SAMPLE_tank:
+    case SOUND_tank:
       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
       break;
 
-    case SAMPLE_android_clone:
+    case SOUND_android_clone:
       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
       break;
 
-    case SAMPLE_android_move:
+    case SOUND_android_move:
       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
       break;
 
-    case SAMPLE_spring:
+    case SOUND_spring:
       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
       break;
 
-    case SAMPLE_slurp:
+    case SOUND_slurp:
       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
       break;
 
-    case SAMPLE_eater:
+    case SOUND_eater:
       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
       break;
 
-    case SAMPLE_eater_eat:
+    case SOUND_eater_eat:
       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
       break;
 
-    case SAMPLE_alien:
+    case SOUND_alien:
       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
       break;
 
-    case SAMPLE_collect:
+    case SOUND_collect:
       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
       break;
 
-    case SAMPLE_diamond:
+    case SOUND_diamond:
       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
       break;
 
-    case SAMPLE_squash:
-      /* !!! CHECK THIS !!! */
+    case SOUND_squash:
+      // !!! CHECK THIS !!!
 #if 1
       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
 #else
@@ -15346,75 +14961,75 @@ void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
 #endif
       break;
 
-    case SAMPLE_wonderfall:
+    case SOUND_wonderfall:
       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
       break;
 
-    case SAMPLE_drip:
+    case SOUND_drip:
       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
       break;
 
-    case SAMPLE_push:
+    case SOUND_push:
       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
       break;
 
-    case SAMPLE_dirt:
+    case SOUND_dirt:
       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
       break;
 
-    case SAMPLE_acid:
+    case SOUND_acid:
       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
       break;
 
-    case SAMPLE_ball:
+    case SOUND_ball:
       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
       break;
 
-    case SAMPLE_grow:
+    case SOUND_slide:
       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
       break;
 
-    case SAMPLE_wonder:
+    case SOUND_wonder:
       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
       break;
 
-    case SAMPLE_door:
+    case SOUND_door:
       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
       break;
 
-    case SAMPLE_exit_open:
+    case SOUND_exit_open:
       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
       break;
 
-    case SAMPLE_exit_leave:
+    case SOUND_exit_leave:
       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
       break;
 
-    case SAMPLE_dynamite:
+    case SOUND_dynamite:
       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
       break;
 
-    case SAMPLE_tick:
+    case SOUND_tick:
       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
       break;
 
-    case SAMPLE_press:
+    case SOUND_press:
       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
       break;
 
-    case SAMPLE_wheel:
+    case SOUND_wheel:
       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
       break;
 
-    case SAMPLE_boom:
+    case SOUND_boom:
       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
       break;
 
-    case SAMPLE_die:
+    case SOUND_die:
       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
       break;
 
-    case SAMPLE_time:
+    case SOUND_time:
       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
       break;
 
@@ -15424,42 +15039,68 @@ void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
   }
 }
 
-#if 0
-void ChangeTime(int value)
+void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
 {
-  int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
+  int element = map_element_SP_to_RND(element_sp);
+  int action = map_action_SP_to_RND(action_sp);
+  int offset = (setup.sp_show_border_elements ? 0 : 1);
+  int x = xx - offset;
+  int y = yy - offset;
 
-  *time += value;
+  PlayLevelSoundElementAction(x, y, element, action);
+}
+
+void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
+{
+  int element = map_element_MM_to_RND(element_mm);
+  int action = map_action_MM_to_RND(action_mm);
+  int offset = 0;
+  int x = xx - offset;
+  int y = yy - offset;
 
-  /* EMC game engine uses value from time counter of RND game engine */
-  level.native_em_level->lev->time = *time;
+  if (!IS_MM_ELEMENT(element))
+    element = EL_MM_DEFAULT;
 
-  DrawGameValue_Time(*time);
+  PlayLevelSoundElementAction(x, y, element, action);
 }
 
-void RaiseScore(int value)
+void PlaySound_MM(int sound_mm)
 {
-  /* EMC game engine and RND game engine have separate score counters */
-  int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
-               &level.native_em_level->lev->score : &local_player->score);
+  int sound = map_sound_MM_to_RND(sound_mm);
 
-  *score += value;
+  if (sound == SND_UNDEFINED)
+    return;
 
-  DrawGameValue_Score(*score);
+  PlaySound(sound);
+}
+
+void PlaySoundLoop_MM(int sound_mm)
+{
+  int sound = map_sound_MM_to_RND(sound_mm);
+
+  if (sound == SND_UNDEFINED)
+    return;
+
+  PlaySoundLoop(sound);
+}
+
+void StopSound_MM(int sound_mm)
+{
+  int sound = map_sound_MM_to_RND(sound_mm);
+
+  if (sound == SND_UNDEFINED)
+    return;
+
+  StopSound(sound);
 }
-#endif
 
 void RaiseScore(int value)
 {
-  local_player->score += value;
+  game.score += value;
 
-#if 1
-  game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+  game_panel_controls[GAME_PANEL_SCORE].value = game.score;
 
   DisplayGameControlValues();
-#else
-  DrawGameValue_Score(local_player->score);
-#endif
 }
 
 void RaiseScoreElement(int element)
@@ -15546,47 +15187,28 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
 {
   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
   {
-#if defined(NETWORK_AVALIABLE)
-    if (options.network)
+    // closing door required in case of envelope style request dialogs
+    if (!skip_request)
+    {
+      // prevent short reactivation of overlay buttons while closing door
+      SetOverlayActive(FALSE);
+
+      CloseDoor(DOOR_CLOSE_1);
+    }
+
+    if (network.enabled)
       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
     else
-#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
 
-       game_status = GAME_MODE_MAIN;
+      SetGameStatus(GAME_MODE_MAIN);
 
-       DrawAndFadeInMainMenu(REDRAW_FIELD);
-      }
+      DrawMainMenu();
     }
   }
-  else         /* continue playing the game */
+  else         // continue playing the game
   {
     if (tape.playing && tape.deactivate_display)
       TapeDeactivateDisplayOff(TRUE);
@@ -15601,33 +15223,102 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
 void RequestQuitGame(boolean ask_if_really_quit)
 {
   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
-  boolean skip_request = AllPlayersGone || quick_quit;
+  boolean skip_request = game.all_players_gone || quick_quit;
 
   RequestQuitGameExt(skip_request, quick_quit,
-                    "Do you really want to quit the game ?");
+                    "Do you really want to quit the game?");
 }
 
+void RequestRestartGame(char *message)
+{
+  game.restart_game_message = NULL;
+
+  boolean has_started_game = hasStartedNetworkGame();
+  int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
 
-/* ------------------------------------------------------------------------- */
-/* random generator functions                                                */
-/* ------------------------------------------------------------------------- */
+  if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
+  {
+    StartGameActions(network.enabled, setup.autorecord, level.random_seed);
+  }
+  else
+  {
+    SetGameStatus(GAME_MODE_MAIN);
+
+    DrawMainMenu();
+  }
+}
 
-unsigned int InitEngineRandom_RND(long seed)
+void CheckGameOver(void)
 {
-  game.num_random_calls = 0;
+  static boolean last_game_over = FALSE;
+  static int game_over_delay = 0;
+  int game_over_delay_value = 50;
+  boolean game_over = checkGameFailed();
 
-#if 0
-  unsigned int rnd_seed = InitEngineRandom(seed);
+  // do not handle game over if request dialog is already active
+  if (game.request_active)
+    return;
 
-  printf("::: START RND: %d\n", rnd_seed);
+  // do not ask to play again if game was never actually played
+  if (!game.GamePlayed)
+    return;
 
-  return rnd_seed;
-#else
+  if (!game_over)
+  {
+    last_game_over = FALSE;
+    game_over_delay = game_over_delay_value;
 
-  return InitEngineRandom(seed);
+    return;
+  }
+
+  if (game_over_delay > 0)
+  {
+    game_over_delay--;
+
+    return;
+  }
+
+  if (last_game_over != game_over)
+    game.restart_game_message = (hasStartedNetworkGame() ?
+                                "Game over! Play it again?" :
+                                "Game over!");
+
+  last_game_over = game_over;
+}
+
+boolean checkGameSolved(void)
+{
+  // set for all game engines if level was solved
+  return game.LevelSolved_GameEnd;
+}
+
+boolean checkGameFailed(void)
+{
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    return (game_em.game_over && !game_em.level_solved);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    return (game_sp.game_over && !game_sp.level_solved);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    return (game_mm.game_over && !game_mm.level_solved);
+  else                         // GAME_ENGINE_TYPE_RND
+    return (game.GameOver && !game.LevelSolved);
+}
+
+boolean checkGameEnded(void)
+{
+  return (checkGameSolved() || checkGameFailed());
+}
 
-#endif
 
+// ----------------------------------------------------------------------------
+// random generator functions
+// ----------------------------------------------------------------------------
+
+unsigned int InitEngineRandom_RND(int seed)
+{
+  game.num_random_calls = 0;
+
+  return InitEngineRandom(seed);
 }
 
 unsigned int RND(int max)
@@ -15643,48 +15334,28 @@ unsigned int RND(int max)
 }
 
 
-/* ------------------------------------------------------------------------- */
-/* game engine snapshot handling functions                                   */
-/* ------------------------------------------------------------------------- */
-
-#define ARGS_ADDRESS_AND_SIZEOF(x)             (&(x)), (sizeof(x))
+// ----------------------------------------------------------------------------
+// game engine snapshot handling functions
+// ----------------------------------------------------------------------------
 
 struct EngineSnapshotInfo
 {
-  /* runtime values for custom element collect score */
+  // runtime values for custom element collect score
   int collect_score[NUM_CUSTOM_ELEMENTS];
 
-  /* runtime values for group element choice position */
+  // runtime values for group element choice position
   int choice_pos[NUM_GROUP_ELEMENTS];
 
-  /* runtime values for belt position animations */
-  int belt_graphic[4 * NUM_BELT_PARTS];
-  int belt_anim_mode[4 * NUM_BELT_PARTS];
-};
-
-struct EngineSnapshotNodeInfo
-{
-  void *buffer_orig;
-  void *buffer_copy;
-  int size;
+  // runtime values for belt position animations
+  int belt_graphic[4][NUM_BELT_PARTS];
+  int belt_anim_mode[4][NUM_BELT_PARTS];
 };
 
 static struct EngineSnapshotInfo engine_snapshot_rnd;
-static ListNode *engine_snapshot_list = NULL;
 static char *snapshot_level_identifier = NULL;
 static int snapshot_level_nr = -1;
 
-void FreeEngineSnapshot()
-{
-  while (engine_snapshot_list != NULL)
-    deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
-                      checked_free);
-
-  setString(&snapshot_level_identifier, NULL);
-  snapshot_level_nr = -1;
-}
-
-static void SaveEngineSnapshotValues_RND()
+static void SaveEngineSnapshotValues_RND(void)
 {
   static int belt_base_active_element[4] =
   {
@@ -15717,15 +15388,15 @@ static void SaveEngineSnapshotValues_RND()
       int graphic = el2img(element);
       int anim_mode = graphic_info[graphic].anim_mode;
 
-      engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
-      engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
+      engine_snapshot_rnd.belt_graphic[i][j] = graphic;
+      engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
     }
   }
 }
 
-static void LoadEngineSnapshotValues_RND()
+static void LoadEngineSnapshotValues_RND(void)
 {
-  unsigned long num_random_calls = game.num_random_calls;
+  unsigned int num_random_calls = game.num_random_calls;
   int i, j;
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -15746,8 +15417,8 @@ static void LoadEngineSnapshotValues_RND()
   {
     for (j = 0; j < NUM_BELT_PARTS; j++)
     {
-      int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
-      int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
+      int graphic = engine_snapshot_rnd.belt_graphic[i][j];
+      int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
 
       graphic_info[graphic].anim_mode = anim_mode;
     }
@@ -15762,334 +15433,426 @@ static void LoadEngineSnapshotValues_RND()
 
   if (game.num_random_calls != num_random_calls)
   {
-    Error(ERR_INFO, "number of random calls out of sync");
-    Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
-    Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
-    Error(ERR_EXIT, "this should not happen -- please debug");
+    Error("number of random calls out of sync");
+    Error("number of random calls should be %d", num_random_calls);
+    Error("number of random calls is %d", game.num_random_calls);
+
+    Fail("this should not happen -- please debug");
   }
 }
 
-static void SaveEngineSnapshotBuffer(void *buffer, int size)
+void FreeEngineSnapshotSingle(void)
 {
-  struct EngineSnapshotNodeInfo *bi =
-    checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
-
-  bi->buffer_orig = buffer;
-  bi->buffer_copy = checked_malloc(size);
-  bi->size = size;
-
-  memcpy(bi->buffer_copy, buffer, size);
+  FreeSnapshotSingle();
 
-  addNodeToList(&engine_snapshot_list, NULL, bi);
+  setString(&snapshot_level_identifier, NULL);
+  snapshot_level_nr = -1;
 }
 
-void SaveEngineSnapshot()
+void FreeEngineSnapshotList(void)
 {
-  FreeEngineSnapshot();                /* free previous snapshot, if needed */
-
-  if (level_editor_test_game)  /* do not save snapshots from editor */
-    return;
-
-  /* copy some special values to a structure better suited for the snapshot */
-
-  SaveEngineSnapshotValues_RND();
-  SaveEngineSnapshotValues_EM();
-
-  /* save values stored in special snapshot structure */
+  FreeSnapshotList();
+}
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+static ListNode *SaveEngineSnapshotBuffers(void)
+{
+  ListNode *buffers = NULL;
 
-  /* save further RND engine values */
+  // copy some special values to a structure better suited for the snapshot
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
+  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);
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    SaveEngineSnapshotValues_MM(&buffers);
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
+  // save values stored in special snapshot structure
 
-  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));
+  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));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
+
+  // save further RND engine values
+
+  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(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(AmoebaCnt));
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
+
+  SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
+  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));
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
+#if 0
+  ListNode *node = engine_snapshot_list_rnd;
+  int num_bytes = 0;
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
+  while (node != NULL)
+  {
+    num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
+    node = node->next;
+  }
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
+  Debug("game:playing:SaveEngineSnapshotBuffers",
+       "size of engine snapshot: %d bytes", num_bytes);
+#endif
 
-  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));
+  return buffers;
+}
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
+void SaveEngineSnapshotSingle(void)
+{
+  ListNode *buffers = SaveEngineSnapshotBuffers();
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
+  // finally save all snapshot buffers to single snapshot
+  SaveSnapshotSingle(buffers);
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
+  // save level identification information
+  setString(&snapshot_level_identifier, leveldir_current->identifier);
+  snapshot_level_nr = level_nr;
+}
 
-  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));
+boolean CheckSaveEngineSnapshotToList(void)
+{
+  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));
 
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
-  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
+  game.snapshot.changed_action = FALSE;
+  game.snapshot.collected_item = FALSE;
+  game.snapshot.save_snapshot = save_snapshot;
 
-  /* save level identification information */
+  return save_snapshot;
+}
 
-  setString(&snapshot_level_identifier, leveldir_current->identifier);
-  snapshot_level_nr = level_nr;
+void SaveEngineSnapshotToList(void)
+{
+  if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
+      tape.quick_resume)
+    return;
 
-#if 0
-  ListNode *node = engine_snapshot_list;
-  int num_bytes = 0;
+  ListNode *buffers = SaveEngineSnapshotBuffers();
 
-  while (node != NULL)
-  {
-    num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
+  // finally save all snapshot buffers to snapshot list
+  SaveSnapshotToList(buffers);
+}
 
-    node = node->next;
-  }
+void SaveEngineSnapshotToListInitial(void)
+{
+  FreeEngineSnapshotList();
 
-  printf("::: size of engine snapshot: %d bytes\n", num_bytes);
-#endif
+  SaveEngineSnapshotToList();
 }
 
-static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
+static void LoadEngineSnapshotValues(void)
 {
-  memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
+  // restore special values from snapshot structure
+
+  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();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    LoadEngineSnapshotValues_MM();
 }
 
-void LoadEngineSnapshot()
+void LoadEngineSnapshotSingle(void)
 {
-  ListNode *node = engine_snapshot_list;
+  LoadSnapshotSingle();
 
-  if (engine_snapshot_list == NULL)
-    return;
+  LoadEngineSnapshotValues();
+}
 
-  while (node != NULL)
-  {
-    LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
+static void LoadEngineSnapshot_Undo(int steps)
+{
+  LoadSnapshotFromList_Older(steps);
 
-    node = node->next;
-  }
+  LoadEngineSnapshotValues();
+}
 
-  /* restore special values from snapshot structure */
+static void LoadEngineSnapshot_Redo(int steps)
+{
+  LoadSnapshotFromList_Newer(steps);
 
-  LoadEngineSnapshotValues_RND();
-  LoadEngineSnapshotValues_EM();
+  LoadEngineSnapshotValues();
 }
 
-boolean CheckEngineSnapshot()
+boolean CheckEngineSnapshotSingle(void)
 {
   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
          snapshot_level_nr == level_nr);
 }
 
+boolean CheckEngineSnapshotList(void)
+{
+  return CheckSnapshotList();
+}
 
-/* ---------- new game button stuff ---------------------------------------- */
-
-/* graphic position values for game buttons */
-#define GAME_BUTTON_XSIZE      30
-#define GAME_BUTTON_YSIZE      30
-#define GAME_BUTTON_XPOS       5
-#define GAME_BUTTON_YPOS       215
-#define SOUND_BUTTON_XPOS      5
-#define SOUND_BUTTON_YPOS      (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
 
-#define GAME_BUTTON_STOP_XPOS  (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PLAY_XPOS  (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_MUSIC_XPOS        (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_LOOPS_XPOS        (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
+// ---------- new game button stuff -------------------------------------------
 
 static struct
 {
-  int *x, *y;
-  int gd_x, gd_y;
+  int graphic;
+  struct XY *pos;
   int gadget_id;
+  boolean *setup_value;
+  boolean allowed_on_tape;
+  boolean is_touch_button;
   char *infotext;
 } gamebutton_info[NUM_GAME_BUTTONS] =
 {
-#if 1
   {
-    &game.button.stop.x,       &game.button.stop.y,
-    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_STOP,
-    "stop game"
+    IMG_GFX_GAME_BUTTON_STOP,                  &game.button.stop,
+    GAME_CTRL_ID_STOP,                         NULL,
+    TRUE, FALSE,                               "stop game"
   },
   {
-    &game.button.pause.x,      &game.button.pause.y,
-    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PAUSE,
-    "pause game"
+    IMG_GFX_GAME_BUTTON_PAUSE,                 &game.button.pause,
+    GAME_CTRL_ID_PAUSE,                                NULL,
+    TRUE, FALSE,                               "pause game"
   },
   {
-    &game.button.play.x,       &game.button.play.y,
-    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PLAY,
-    "play game"
+    IMG_GFX_GAME_BUTTON_PLAY,                  &game.button.play,
+    GAME_CTRL_ID_PLAY,                         NULL,
+    TRUE, FALSE,                               "play game"
   },
   {
-    &game.button.sound_music.x,        &game.button.sound_music.y,
-    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_MUSIC,
-    "background music on/off"
+    IMG_GFX_GAME_BUTTON_UNDO,                  &game.button.undo,
+    GAME_CTRL_ID_UNDO,                         NULL,
+    TRUE, FALSE,                               "undo step"
   },
   {
-    &game.button.sound_loops.x,        &game.button.sound_loops.y,
-    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_LOOPS,
-    "sound loops on/off"
+    IMG_GFX_GAME_BUTTON_REDO,                  &game.button.redo,
+    GAME_CTRL_ID_REDO,                         NULL,
+    TRUE, FALSE,                               "redo step"
   },
   {
-    &game.button.sound_simple.x,&game.button.sound_simple.y,
-    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_SIMPLE,
-    "normal sounds on/off"
-  }
-#else
+    IMG_GFX_GAME_BUTTON_SAVE,                  &game.button.save,
+    GAME_CTRL_ID_SAVE,                         NULL,
+    TRUE, FALSE,                               "save game"
+  },
   {
-    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_STOP,
-    "stop game"
+    IMG_GFX_GAME_BUTTON_PAUSE2,                        &game.button.pause2,
+    GAME_CTRL_ID_PAUSE2,                       NULL,
+    TRUE, FALSE,                               "pause game"
   },
   {
-    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PAUSE,
-    "pause game"
+    IMG_GFX_GAME_BUTTON_LOAD,                  &game.button.load,
+    GAME_CTRL_ID_LOAD,                         NULL,
+    TRUE, FALSE,                               "load game"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_PANEL_STOP,            &game.button.panel_stop,
+    GAME_CTRL_ID_PANEL_STOP,                   NULL,
+    FALSE, FALSE,                              "stop game"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_PANEL_PAUSE,           &game.button.panel_pause,
+    GAME_CTRL_ID_PANEL_PAUSE,                  NULL,
+    FALSE, FALSE,                              "pause game"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_PANEL_PLAY,            &game.button.panel_play,
+    GAME_CTRL_ID_PANEL_PLAY,                   NULL,
+    FALSE, FALSE,                              "play game"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_TOUCH_STOP,            &game.button.touch_stop,
+    GAME_CTRL_ID_TOUCH_STOP,                   NULL,
+    FALSE, TRUE,                               "stop game"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,           &game.button.touch_pause,
+    GAME_CTRL_ID_TOUCH_PAUSE,                  NULL,
+    FALSE, TRUE,                               "pause game"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_SOUND_MUSIC,           &game.button.sound_music,
+    SOUND_CTRL_ID_MUSIC,                       &setup.sound_music,
+    TRUE, FALSE,                               "background music on/off"
   },
   {
-    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PLAY,
-    "play game"
+    IMG_GFX_GAME_BUTTON_SOUND_LOOPS,           &game.button.sound_loops,
+    SOUND_CTRL_ID_LOOPS,                       &setup.sound_loops,
+    TRUE, FALSE,                               "sound loops on/off"
   },
   {
-    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_MUSIC,
-    "background music on/off"
+    IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,          &game.button.sound_simple,
+    SOUND_CTRL_ID_SIMPLE,                      &setup.sound_simple,
+    TRUE, FALSE,                               "normal sounds on/off"
   },
   {
-    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_LOOPS,
-    "sound loops on/off"
+    IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,     &game.button.panel_sound_music,
+    SOUND_CTRL_ID_PANEL_MUSIC,                 &setup.sound_music,
+    FALSE, FALSE,                              "background music on/off"
   },
   {
-    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_SIMPLE,
-    "normal sounds on/off"
+    IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,     &game.button.panel_sound_loops,
+    SOUND_CTRL_ID_PANEL_LOOPS,                 &setup.sound_loops,
+    FALSE, FALSE,                              "sound loops on/off"
+  },
+  {
+    IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,    &game.button.panel_sound_simple,
+    SOUND_CTRL_ID_PANEL_SIMPLE,                        &setup.sound_simple,
+    FALSE, FALSE,                              "normal sounds on/off"
   }
-#endif
 };
 
-void CreateGameButtons()
+void CreateGameButtons(void)
 {
   int i;
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
   {
-    Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+    int graphic = gamebutton_info[i].graphic;
+    struct GraphicInfo *gfx = &graphic_info[graphic];
+    struct XY *pos = gamebutton_info[i].pos;
     struct GadgetInfo *gi;
     int button_type;
     boolean checked;
-    unsigned long event_mask;
-    int x, y;
-    int gd_xoffset, gd_yoffset;
-    int gd_x1, gd_x2, gd_y1, gd_y2;
+    unsigned int event_mask;
+    boolean is_touch_button = gamebutton_info[i].is_touch_button;
+    boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
+    boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
+    int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
+    int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
+    int gd_x   = gfx->src_x;
+    int gd_y   = gfx->src_y;
+    int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
+    int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
+    int gd_xa  = gfx->src_x + gfx->active_xoffset;
+    int gd_ya  = gfx->src_y + gfx->active_yoffset;
+    int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
+    int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
+    int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
+    int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
     int id = i;
 
-    x = DX + *gamebutton_info[i].x;
-    y = DY + *gamebutton_info[i].y;
-    gd_xoffset = gamebutton_info[i].gd_x;
-    gd_yoffset = gamebutton_info[i].gd_y;
-    gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
-    gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+    if (gfx->bitmap == NULL)
+    {
+      game_gadget[id] = NULL;
+
+      continue;
+    }
 
     if (id == GAME_CTRL_ID_STOP ||
-       id == GAME_CTRL_ID_PAUSE ||
-       id == GAME_CTRL_ID_PLAY)
+       id == GAME_CTRL_ID_PANEL_STOP ||
+       id == GAME_CTRL_ID_TOUCH_STOP ||
+       id == GAME_CTRL_ID_PLAY ||
+       id == GAME_CTRL_ID_PANEL_PLAY ||
+       id == GAME_CTRL_ID_SAVE ||
+       id == GAME_CTRL_ID_LOAD)
     {
       button_type = GD_TYPE_NORMAL_BUTTON;
       checked = FALSE;
       event_mask = GD_EVENT_RELEASED;
-      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
-      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+    }
+    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;
-      checked =
-       ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
-        (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
-        (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
+      checked = (gamebutton_info[i].setup_value != NULL ?
+                *gamebutton_info[i].setup_value : FALSE);
       event_mask = GD_EVENT_PRESSED;
-      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
-      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
     }
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_IMAGE_ID, graphic,
                      GDI_INFO_TEXT, gamebutton_info[i].infotext,
-#if 1
-                     GDI_X, x,
-                     GDI_Y, y,
-#else
-                     GDI_X, DX + gd_xoffset,
-                     GDI_Y, DY + gd_yoffset,
-#endif
-                     GDI_WIDTH, GAME_BUTTON_XSIZE,
-                     GDI_HEIGHT, GAME_BUTTON_YSIZE,
+                     GDI_X, base_x + x,
+                     GDI_Y, base_y + y,
+                     GDI_WIDTH, gfx->width,
+                     GDI_HEIGHT, gfx->height,
                      GDI_TYPE, button_type,
                      GDI_STATE, GD_BUTTON_UNPRESSED,
                      GDI_CHECKED, checked,
-                     GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
-                     GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
-                     GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
-                     GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+                     GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
+                     GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
+                     GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
+                     GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
                      GDI_DIRECT_DRAW, FALSE,
+                     GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleGameButtons,
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     game_gadget[id] = gi;
   }
 }
 
-void FreeGameButtons()
+void FreeGameButtons(void)
 {
   int i;
 
@@ -16097,79 +15860,298 @@ void FreeGameButtons()
     FreeGadget(game_gadget[i]);
 }
 
-static void MapGameButtons()
+static void UnmapGameButtonsAtSamePosition(int id)
 {
   int i;
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
-    MapGadget(game_gadget[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(void)
+{
+  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);
+
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
+  }
 }
 
-void UnmapGameButtons()
+static void MapGameButtonsAtSamePosition(int id)
 {
   int i;
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
-    UnmapGadget(game_gadget[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(void)
+{
+  UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
+  UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
+
+  MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
+  MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+}
+
+void UnmapUndoRedoButtons(void)
+{
+  UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
+  UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+
+  MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
+  MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
+}
+
+void ModifyPauseButtons(void)
+{
+  static int ids[] =
+  {
+    GAME_CTRL_ID_PAUSE,
+    GAME_CTRL_ID_PAUSE2,
+    GAME_CTRL_ID_PANEL_PAUSE,
+    GAME_CTRL_ID_TOUCH_PAUSE,
+    -1
+  };
+  int i;
+
+  for (i = 0; ids[i] > -1; i++)
+    ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
 }
 
-void RedrawGameButtons()
+static void MapGameButtonsExt(boolean on_tape)
 {
   int i;
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
-    RedrawGadget(game_gadget[i]);
+    if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
+       i != GAME_CTRL_ID_UNDO &&
+       i != GAME_CTRL_ID_REDO)
+      MapGadget(game_gadget[i]);
+
+  UnmapGameButtonsAtSamePosition_All();
+
+  RedrawGameButtons();
 }
 
-static void HandleGameButtons(struct GadgetInfo *gi)
+static void UnmapGameButtonsExt(boolean on_tape)
+{
+  int i;
+
+  for (i = 0; i < NUM_GAME_BUTTONS; i++)
+    if (!on_tape || gamebutton_info[i].allowed_on_tape)
+      UnmapGadget(game_gadget[i]);
+}
+
+static void RedrawGameButtonsExt(boolean on_tape)
+{
+  int i;
+
+  for (i = 0; i < NUM_GAME_BUTTONS; i++)
+    if (!on_tape || gamebutton_info[i].allowed_on_tape)
+      RedrawGadget(game_gadget[i]);
+}
+
+static void SetGadgetState(struct GadgetInfo *gi, boolean state)
+{
+  if (gi == NULL)
+    return;
+
+  gi->checked = state;
+}
+
+static void RedrawSoundButtonGadget(int id)
+{
+  int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
+            id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
+            id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
+            id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
+            id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
+            id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
+            id);
+
+  SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
+  RedrawGadget(game_gadget[id2]);
+}
+
+void MapGameButtons(void)
+{
+  MapGameButtonsExt(FALSE);
+}
+
+void UnmapGameButtons(void)
+{
+  UnmapGameButtonsExt(FALSE);
+}
+
+void RedrawGameButtons(void)
+{
+  RedrawGameButtonsExt(FALSE);
+}
+
+void MapGameButtonsOnTape(void)
+{
+  MapGameButtonsExt(TRUE);
+}
+
+void UnmapGameButtonsOnTape(void)
+{
+  UnmapGameButtonsExt(TRUE);
+}
+
+void RedrawGameButtonsOnTape(void)
+{
+  RedrawGameButtonsExt(TRUE);
+}
+
+static void GameUndoRedoExt(void)
+{
+  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();
+}
+
+static void GameUndo(int steps)
+{
+  if (!CheckEngineSnapshotList())
+    return;
+
+  LoadEngineSnapshot_Undo(steps);
+
+  GameUndoRedoExt();
+}
+
+static void GameRedo(int steps)
+{
+  if (!CheckEngineSnapshotList())
+    return;
+
+  LoadEngineSnapshot_Redo(steps);
+
+  GameUndoRedoExt();
+}
+
+static void HandleGameButtonsExt(int id, int button)
 {
-  int id = gi->custom_id;
+  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 (game_status != GAME_MODE_PLAYING)
+  if (!handle_game_buttons)
     return;
 
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
+    case GAME_CTRL_ID_PANEL_STOP:
+    case GAME_CTRL_ID_TOUCH_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:
+    case GAME_CTRL_ID_PANEL_PAUSE:
+    case GAME_CTRL_ID_TOUCH_PAUSE:
+      if (network.enabled && game_status == GAME_MODE_PLAYING)
       {
-#if defined(NETWORK_AVALIABLE)
        if (tape.pausing)
          SendToServer_ContinuePlaying();
        else
          SendToServer_PausePlaying();
-#endif
       }
       else
        TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
+      game_undo_executed = FALSE;
+
       break;
 
     case GAME_CTRL_ID_PLAY:
-      if (tape.pausing)
+    case GAME_CTRL_ID_PANEL_PLAY:
+      if (game_status == GAME_MODE_MAIN)
+      {
+        StartGameActions(network.enabled, setup.autorecord, level.random_seed);
+      }
+      else if (tape.pausing)
       {
-#if defined(NETWORK_AVALIABLE)
-       if (options.network)
+       if (network.enabled)
          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:
+    case SOUND_CTRL_ID_PANEL_MUSIC:
       if (setup.sound_music)
       { 
        setup.sound_music = FALSE;
+
        FadeMusic();
       }
       else if (audio.music_available)
@@ -16178,31 +16160,60 @@ static void HandleGameButtons(struct GadgetInfo *gi)
 
        SetAudioMode(setup.sound);
 
-       PlayLevelMusic();
+       if (game_status == GAME_MODE_PLAYING)
+         PlayLevelMusic();
       }
+
+      RedrawSoundButtonGadget(id);
+
       break;
 
     case SOUND_CTRL_ID_LOOPS:
+    case SOUND_CTRL_ID_PANEL_LOOPS:
       if (setup.sound_loops)
        setup.sound_loops = FALSE;
       else if (audio.loops_available)
       {
        setup.sound = setup.sound_loops = TRUE;
+
        SetAudioMode(setup.sound);
       }
+
+      RedrawSoundButtonGadget(id);
+
       break;
 
     case SOUND_CTRL_ID_SIMPLE:
+    case SOUND_CTRL_ID_PANEL_SIMPLE:
       if (setup.sound_simple)
        setup.sound_simple = FALSE;
       else if (audio.sound_available)
       {
        setup.sound = setup.sound_simple = TRUE;
+
        SetAudioMode(setup.sound);
       }
+
+      RedrawSoundButtonGadget(id);
+
       break;
 
     default:
       break;
   }
 }
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+  HandleGameButtonsExt(gi->custom_id, gi->event.button);
+}
+
+void HandleSoundButtonKeys(Key key)
+{
+  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);
+}