rnd-20140331-2-src
[rocksndiamonds.git] / src / game.c
index 052239346ffc65f545296b98070c120881393cf3..a5a9396c9d3a6180fa7715accbc002d759800c2b 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2002 Artsoft Entertainment                      *
+* (c) 1995-2006 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "tape.h"
 #include "network.h"
 
+
+/* DEBUG SETTINGS */
+#define DEBUG_INIT_PLAYER      1
+#define DEBUG_PLAYER_ACTIONS   0
+
 /* EXPERIMENTAL STUFF */
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 #define USE_NEW_DELAYED_ACTION         (USE_NEW_STUFF          * 1)
 #define USE_NEW_SNAP_DELAY             (USE_NEW_STUFF          * 1)
 #define USE_ONLY_ONE_CHANGE_PER_FRAME  (USE_NEW_STUFF          * 1)
+#define USE_ONE_MORE_CHANGE_PER_FRAME  (USE_NEW_STUFF          * 1)
+#define USE_FIXED_DONT_RUN_INTO                (USE_NEW_STUFF          * 1)
+#define USE_NEW_SPRING_BUMPER          (USE_NEW_STUFF          * 1)
+#define USE_STOP_CHANGED_ELEMENTS      (USE_NEW_STUFF          * 1)
+#define USE_ELEMENT_TOUCHING_BUGFIX    (USE_NEW_STUFF          * 1)
+#define USE_NEW_CONTINUOUS_SNAPPING    (USE_NEW_STUFF          * 1)
+#define USE_GFX_RESET_GFX_ANIMATION    (USE_NEW_STUFF          * 1)
+#define USE_BOTH_SWITCHGATE_SWITCHES   (USE_NEW_STUFF          * 1)
+#define USE_PLAYER_GRAVITY             (USE_NEW_STUFF          * 1)
+#define USE_FIXED_BORDER_RUNNING_GFX   (USE_NEW_STUFF          * 1)
+#define USE_QUICKSAND_BD_ROCK_BUGFIX   (USE_NEW_STUFF          * 0)
+
 #define USE_QUICKSAND_IMPACT_BUGFIX    (USE_NEW_STUFF          * 0)
 
+#define USE_CODE_THAT_BREAKS_SNAKE_BITE        (USE_NEW_STUFF          * 1)
+
+#define USE_UFAST_PLAYER_EXIT_BUGFIX   (USE_NEW_STUFF          * 1)
+
+#define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF          * 1)
+#define USE_GFX_RESET_PLAYER_ARTWORK   (USE_NEW_STUFF          * 1)
+
+#define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF          * 1)
+#define USE_FIX_IMPACT_COLLISION       (USE_NEW_STUFF          * 1)
+#define USE_FIX_CE_ACTION_WITH_PLAYER  (USE_NEW_STUFF          * 1)
+#define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF          * 1)
+
+#define USE_PLAYER_REANIMATION         (USE_NEW_STUFF          * 1)
+
+#define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
+
+#define USE_NEW_PLAYER_ASSIGNMENTS     (USE_NEW_STUFF          * 1)
+
+#define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 0)
+
+#if USE_DELAYED_GFX_REDRAW
+#define TEST_DrawLevelField(x, y)                              \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE
+#define TEST_DrawLevelFieldCrumbled(x, y)                      \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
+#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_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() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
 #define DF_SNAP                        2
 
 /* for MovePlayer() */
-#define MF_NO_ACTION           0
-#define MF_MOVING              1
-#define MF_ACTION              2
+#define MP_NO_ACTION           0
+#define MP_MOVING              1
+#define MP_ACTION              2
+#define MP_DONT_RUN_INTO       (MP_MOVING | MP_ACTION)
 
 /* for ScrollPlayer() */
 #define SCROLL_INIT            0
 #define EX_TYPE_DYNA           (1 << 4)
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
-/* special positions in the game control window (relative to control window) */
-#define XX_LEVEL               37
-#define YY_LEVEL               20
-#define XX_EMERALDS            29
-#define YY_EMERALDS            54
-#define XX_DYNAMITE            29
-#define YY_DYNAMITE            89
-#define XX_KEYS                        18
-#define YY_KEYS                        123
-#define XX_SCORE               15
-#define YY_SCORE               159
-#define XX_TIME1               29
-#define XX_TIME2               30
-#define YY_TIME                        194
-
-/* special positions in the game control window (relative to main window) */
-#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 DY_TIME                        (DY + YY_TIME)
-
-/* 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) */
-#define MOVE_DELAY_MIN_SPEED   32
-#define MOVE_DELAY_NORMAL_SPEED        8
-#define MOVE_DELAY_HIGH_SPEED  4
-#define MOVE_DELAY_MAX_SPEED   1
-
-#if 0
-#define DOUBLE_MOVE_DELAY(x)   (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
-#define HALVE_MOVE_DELAY(x)    (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
-#else
-#define DOUBLE_MOVE_DELAY(x)   (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
-#define HALVE_MOVE_DELAY(x)    (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
-#endif
-#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 */
-#define MOVE_STEPSIZE_NORMAL   (TILEX / MOVE_DELAY_NORMAL_SPEED)
-#define MOVE_STEPSIZE_MIN      (1)
-#define MOVE_STEPSIZE_MAX      (TILEX)
-
-#define GET_DX_FROM_DIR(d)     ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
-#define GET_DY_FROM_DIR(d)     ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
-
-#define        INIT_GFX_RANDOM()       (SimpleRND(1000000))
-
-#define GET_NEW_PUSH_DELAY(e)  (   (element_info[e].push_delay_fixed) + \
-                                RND(element_info[e].push_delay_random))
-#define GET_NEW_DROP_DELAY(e)  (   (element_info[e].drop_delay_fixed) + \
-                                RND(element_info[e].drop_delay_random))
-#define GET_NEW_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
-                                RND(element_info[e].move_delay_random))
-#define GET_MAX_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
-                                   (element_info[e].move_delay_random))
-#define GET_NEW_CUSTOM_VALUE(e)        (   (element_info[e].ce_value_fixed_initial) +\
-                                RND(element_info[e].ce_value_random_initial))
-#define GET_CHANGE_DELAY(c)    (   ((c)->delay_fixed  * (c)->delay_frames) + \
-                                RND((c)->delay_random * (c)->delay_frames))
-#define GET_CE_DELAY_VALUE(c)  (   ((c)->delay_fixed) + \
-                                RND((c)->delay_random))
-
-#define GET_TARGET_ELEMENT(e, ch)                                      \
-       ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
-        (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
-
-#define CAN_GROW_INTO(e)                                               \
-       ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
-
-#define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                        \
-               (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
-                                       (condition)))
-
-#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) ||      \
-                                       (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) ||      \
-                                       (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) ||      \
-                                       (DONT_COLLIDE_WITH(e) &&        \
-                                        IS_PLAYER(x, y) &&             \
-                                        !PLAYER_ENEMY_PROTECTED(x, y))))
-
-#define ELEMENT_CAN_ENTER_FIELD(e, x, y)                               \
-       ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
-
-#define SATELLITE_CAN_ENTER_FIELD(x, y)                                        \
-       ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
-
-#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)
-
-#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]))
-
-#define PACMAN_CAN_ENTER_FIELD(e, x, y)                                        \
-       ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[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]))
-
-#define PENGUIN_CAN_ENTER_FIELD(e, x, y)                               \
-       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
-                                                IS_FOOD_PENGUIN(Feld[x][y])))
-#define DRAGON_CAN_ENTER_FIELD(e, x, y)                                        \
-       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
-
-#define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                       \
-       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
-
-#define SPRING_CAN_ENTER_FIELD(e, x, y)                                        \
-       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
-
-#define GROUP_NR(e)            ((e) - EL_GROUP_START)
-#define MOVE_ENTER_EL(e)       (element_info[e].move_enter_element)
-#define IS_IN_GROUP(e, nr)     (element_info[e].in_group[nr] == TRUE)
-#define IS_IN_GROUP_EL(e, ge)  (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
-
-#define IS_EQUAL_OR_IN_GROUP(e, ge)                                    \
-       (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
-
-#define CE_ENTER_FIELD_COND(e, x, y)                                   \
-               (!IS_PLAYER(x, y) &&                                    \
-                IS_EQUAL_OR_IN_GROUP(Feld[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))
-
-#define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
-#define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
-
-#define ACCESS_FROM(e, d)              (element_info[e].access_direction &(d))
-#define IS_WALKABLE_FROM(e, d)         (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
-#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 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 */
-
-static void CreateField(int, int, int);
-
-static void SetPlayerWaiting(struct PlayerInfo *, boolean);
-static void AdvanceFrameAndPlayerCounters(int);
-
-static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
-static boolean MovePlayer(struct PlayerInfo *, int, int);
-static void ScrollPlayer(struct PlayerInfo *, int);
-static void ScrollScreen(struct PlayerInfo *, int);
-
-int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
-
-static void InitBeltMovement(void);
-static void CloseAllOpenTimegates(void);
-static void CheckGravityMovement(struct PlayerInfo *);
-static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
-static void KillPlayerUnlessEnemyProtected(int, int);
-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 boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
-#define CheckTriggeredElementChange(x, y, e, ev)                       \
-       CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
-#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)         \
-       CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
-#define CheckTriggeredElementChangeBySide(x, y, e, ev, s)              \
-       CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
-#define CheckTriggeredElementChangeByPage(x, y, e, ev, p)              \
-       CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
-
-static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
-#define CheckElementChange(x, y, e, te, ev)                            \
-       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
-#define CheckElementChangeByPlayer(x, y, e, ev, p, s)                  \
-       CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
-#define CheckElementChangeBySide(x, y, e, te, ev, s)                   \
-       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
-
-static void PlayLevelSound(int, int, int);
-static void PlayLevelSoundNearest(int, int, int);
-static void PlayLevelSoundAction(int, int, int);
-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 MapGameButtons();
-static void HandleGameButtons(struct GadgetInfo *);
-
-static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
-
-
-/* ------------------------------------------------------------------------- */
-/* definition of elements that automatically change to other elements after  */
-/* a specified time, eventually calling a function when changing             */
-/* ------------------------------------------------------------------------- */
-
-/* forward declaration for changer functions */
-static void InitBuggyBase(int, int);
-static void WarnBuggyBase(int, int);
-
-static void InitTrap(int, int);
-static void ActivateTrap(int, int);
-static void ChangeActiveTrap(int, int);
+#define PANEL_OFF()            (local_player->LevelSolved_PanelOff)
+#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))
+
+/* 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_INVENTORY_FIRST_1           3
+#define GAME_PANEL_INVENTORY_FIRST_2           4
+#define GAME_PANEL_INVENTORY_FIRST_3           5
+#define GAME_PANEL_INVENTORY_FIRST_4           6
+#define GAME_PANEL_INVENTORY_FIRST_5           7
+#define GAME_PANEL_INVENTORY_FIRST_6           8
+#define GAME_PANEL_INVENTORY_FIRST_7           9
+#define GAME_PANEL_INVENTORY_FIRST_8           10
+#define GAME_PANEL_INVENTORY_LAST_1            11
+#define GAME_PANEL_INVENTORY_LAST_2            12
+#define GAME_PANEL_INVENTORY_LAST_3            13
+#define GAME_PANEL_INVENTORY_LAST_4            14
+#define GAME_PANEL_INVENTORY_LAST_5            15
+#define GAME_PANEL_INVENTORY_LAST_6            16
+#define GAME_PANEL_INVENTORY_LAST_7            17
+#define GAME_PANEL_INVENTORY_LAST_8            18
+#define GAME_PANEL_KEY_1                       19
+#define GAME_PANEL_KEY_2                       20
+#define GAME_PANEL_KEY_3                       21
+#define GAME_PANEL_KEY_4                       22
+#define GAME_PANEL_KEY_5                       23
+#define GAME_PANEL_KEY_6                       24
+#define GAME_PANEL_KEY_7                       25
+#define GAME_PANEL_KEY_8                       26
+#define GAME_PANEL_KEY_WHITE                   27
+#define GAME_PANEL_KEY_WHITE_COUNT             28
+#define GAME_PANEL_SCORE                       29
+#define GAME_PANEL_HIGHSCORE                   30
+#define GAME_PANEL_TIME                                31
+#define GAME_PANEL_TIME_HH                     32
+#define GAME_PANEL_TIME_MM                     33
+#define GAME_PANEL_TIME_SS                     34
+#define GAME_PANEL_FRAME                       35
+#define GAME_PANEL_SHIELD_NORMAL               36
+#define GAME_PANEL_SHIELD_NORMAL_TIME          37
+#define GAME_PANEL_SHIELD_DEADLY               38
+#define GAME_PANEL_SHIELD_DEADLY_TIME          39
+#define GAME_PANEL_EXIT                                40
+#define GAME_PANEL_EMC_MAGIC_BALL              41
+#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       42
+#define GAME_PANEL_LIGHT_SWITCH                        43
+#define GAME_PANEL_LIGHT_SWITCH_TIME           44
+#define GAME_PANEL_TIMEGATE_SWITCH             45
+#define GAME_PANEL_TIMEGATE_SWITCH_TIME                46
+#define GAME_PANEL_SWITCHGATE_SWITCH           47
+#define GAME_PANEL_EMC_LENSES                  48
+#define GAME_PANEL_EMC_LENSES_TIME             49
+#define GAME_PANEL_EMC_MAGNIFIER               50
+#define GAME_PANEL_EMC_MAGNIFIER_TIME          51
+#define GAME_PANEL_BALLOON_SWITCH              52
+#define GAME_PANEL_DYNABOMB_NUMBER             53
+#define GAME_PANEL_DYNABOMB_SIZE               54
+#define GAME_PANEL_DYNABOMB_POWER              55
+#define GAME_PANEL_PENGUINS                    56
+#define GAME_PANEL_SOKOBAN_OBJECTS             57
+#define GAME_PANEL_SOKOBAN_FIELDS              58
+#define GAME_PANEL_ROBOT_WHEEL                 59
+#define GAME_PANEL_CONVEYOR_BELT_1             60
+#define GAME_PANEL_CONVEYOR_BELT_2             61
+#define GAME_PANEL_CONVEYOR_BELT_3             62
+#define GAME_PANEL_CONVEYOR_BELT_4             63
+#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      64
+#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      65
+#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      66
+#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      67
+#define GAME_PANEL_MAGIC_WALL                  68
+#define GAME_PANEL_MAGIC_WALL_TIME             69
+#define GAME_PANEL_GRAVITY_STATE               70
+#define GAME_PANEL_GRAPHIC_1                   71
+#define GAME_PANEL_GRAPHIC_2                   72
+#define GAME_PANEL_GRAPHIC_3                   73
+#define GAME_PANEL_GRAPHIC_4                   74
+#define GAME_PANEL_GRAPHIC_5                   75
+#define GAME_PANEL_GRAPHIC_6                   76
+#define GAME_PANEL_GRAPHIC_7                   77
+#define GAME_PANEL_GRAPHIC_8                   78
+#define GAME_PANEL_ELEMENT_1                   79
+#define GAME_PANEL_ELEMENT_2                   80
+#define GAME_PANEL_ELEMENT_3                   81
+#define GAME_PANEL_ELEMENT_4                   82
+#define GAME_PANEL_ELEMENT_5                   83
+#define GAME_PANEL_ELEMENT_6                   84
+#define GAME_PANEL_ELEMENT_7                   85
+#define GAME_PANEL_ELEMENT_8                   86
+#define GAME_PANEL_ELEMENT_COUNT_1             87
+#define GAME_PANEL_ELEMENT_COUNT_2             88
+#define GAME_PANEL_ELEMENT_COUNT_3             89
+#define GAME_PANEL_ELEMENT_COUNT_4             90
+#define GAME_PANEL_ELEMENT_COUNT_5             91
+#define GAME_PANEL_ELEMENT_COUNT_6             92
+#define GAME_PANEL_ELEMENT_COUNT_7             93
+#define GAME_PANEL_ELEMENT_COUNT_8             94
+#define GAME_PANEL_CE_SCORE_1                  95
+#define GAME_PANEL_CE_SCORE_2                  96
+#define GAME_PANEL_CE_SCORE_3                  97
+#define GAME_PANEL_CE_SCORE_4                  98
+#define GAME_PANEL_CE_SCORE_5                  99
+#define GAME_PANEL_CE_SCORE_6                  100
+#define GAME_PANEL_CE_SCORE_7                  101
+#define GAME_PANEL_CE_SCORE_8                  102
+#define GAME_PANEL_CE_SCORE_1_ELEMENT          103
+#define GAME_PANEL_CE_SCORE_2_ELEMENT          104
+#define GAME_PANEL_CE_SCORE_3_ELEMENT          105
+#define GAME_PANEL_CE_SCORE_4_ELEMENT          106
+#define GAME_PANEL_CE_SCORE_5_ELEMENT          107
+#define GAME_PANEL_CE_SCORE_6_ELEMENT          108
+#define GAME_PANEL_CE_SCORE_7_ELEMENT          109
+#define GAME_PANEL_CE_SCORE_8_ELEMENT          110
+#define GAME_PANEL_PLAYER_NAME                 111
+#define GAME_PANEL_LEVEL_NAME                  112
+#define GAME_PANEL_LEVEL_AUTHOR                        113
+
+#define NUM_GAME_PANEL_CONTROLS                        114
+
+struct GamePanelOrderInfo
+{
+  int nr;
+  int sort_priority;
+};
 
-static void InitRobotWheel(int, int);
-static void RunRobotWheel(int, int);
-static void StopRobotWheel(int, int);
+static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
 
-static void InitTimegateWheel(int, int);
-static void RunTimegateWheel(int, int);
+struct GamePanelControlInfo
+{
+  int nr;
 
-static void InitMagicBallDelay(int, int);
-static void ActivateMagicBall(int, int);
+  struct TextPosInfo *pos;
+  int type;
 
-struct ChangingElementInfo
-{
-  int element;
-  int target_element;
-  int change_delay;
-  void (*pre_change_function)(int x, int y);
-  void (*change_function)(int x, int y);
-  void (*post_change_function)(int x, int y);
+  int value, last_value;
+  int frame, last_frame;
+  int gfx_frame;
+  int gfx_random;
 };
 
-static struct ChangingElementInfo change_delay_list[] =
+static struct GamePanelControlInfo game_panel_controls[] =
 {
   {
-    EL_NUT_BREAKING,
-    EL_EMERALD,
-    6,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_LEVEL_NUMBER,
+    &game.panel.level_number,
+    TYPE_INTEGER,
   },
   {
-    EL_PEARL_BREAKING,
-    EL_EMPTY,
-    8,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_GEMS,
+    &game.panel.gems,
+    TYPE_INTEGER,
   },
   {
-    EL_EXIT_OPENING,
-    EL_EXIT_OPEN,
-    29,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_COUNT,
+    &game.panel.inventory_count,
+    TYPE_INTEGER,
   },
   {
-    EL_EXIT_CLOSING,
-    EL_EXIT_CLOSED,
-    29,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_FIRST_1,
+    &game.panel.inventory_first[0],
+    TYPE_ELEMENT,
   },
   {
-    EL_SP_EXIT_OPENING,
-    EL_SP_EXIT_OPEN,
-    29,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_FIRST_2,
+    &game.panel.inventory_first[1],
+    TYPE_ELEMENT,
   },
   {
-    EL_SP_EXIT_CLOSING,
-    EL_SP_EXIT_CLOSED,
-    29,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_FIRST_3,
+    &game.panel.inventory_first[2],
+    TYPE_ELEMENT,
   },
   {
-    EL_SWITCHGATE_OPENING,
-    EL_SWITCHGATE_OPEN,
-    29,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_FIRST_4,
+    &game.panel.inventory_first[3],
+    TYPE_ELEMENT,
   },
   {
-    EL_SWITCHGATE_CLOSING,
-    EL_SWITCHGATE_CLOSED,
-    29,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_FIRST_5,
+    &game.panel.inventory_first[4],
+    TYPE_ELEMENT,
   },
   {
-    EL_TIMEGATE_OPENING,
-    EL_TIMEGATE_OPEN,
-    29,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_FIRST_6,
+    &game.panel.inventory_first[5],
+    TYPE_ELEMENT,
   },
   {
-    EL_TIMEGATE_CLOSING,
-    EL_TIMEGATE_CLOSED,
-    29,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_FIRST_7,
+    &game.panel.inventory_first[6],
+    TYPE_ELEMENT,
   },
-
   {
-    EL_ACID_SPLASH_LEFT,
-    EL_EMPTY,
-    8,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_FIRST_8,
+    &game.panel.inventory_first[7],
+    TYPE_ELEMENT,
   },
   {
-    EL_ACID_SPLASH_RIGHT,
-    EL_EMPTY,
-    8,
-    NULL,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_LAST_1,
+    &game.panel.inventory_last[0],
+    TYPE_ELEMENT,
   },
   {
-    EL_SP_BUGGY_BASE,
-    EL_SP_BUGGY_BASE_ACTIVATING,
-    0,
-    InitBuggyBase,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_LAST_2,
+    &game.panel.inventory_last[1],
+    TYPE_ELEMENT,
   },
   {
-    EL_SP_BUGGY_BASE_ACTIVATING,
-    EL_SP_BUGGY_BASE_ACTIVE,
-    0,
-    InitBuggyBase,
-    NULL,
-    NULL
+    GAME_PANEL_INVENTORY_LAST_3,
+    &game.panel.inventory_last[2],
+    TYPE_ELEMENT,
   },
   {
-    EL_SP_BUGGY_BASE_ACTIVE,
-    EL_SP_BUGGY_BASE,
-    0,
-    InitBuggyBase,
-    WarnBuggyBase,
-    NULL
+    GAME_PANEL_INVENTORY_LAST_4,
+    &game.panel.inventory_last[3],
+    TYPE_ELEMENT,
   },
   {
-    EL_TRAP,
-    EL_TRAP_ACTIVE,
-    0,
-    InitTrap,
-    NULL,
-    ActivateTrap
+    GAME_PANEL_INVENTORY_LAST_5,
+    &game.panel.inventory_last[4],
+    TYPE_ELEMENT,
   },
   {
-    EL_TRAP_ACTIVE,
-    EL_TRAP,
-    31,
-    NULL,
-    ChangeActiveTrap,
-    NULL
+    GAME_PANEL_INVENTORY_LAST_6,
+    &game.panel.inventory_last[5],
+    TYPE_ELEMENT,
   },
   {
-    EL_ROBOT_WHEEL_ACTIVE,
-    EL_ROBOT_WHEEL,
-    0,
-    InitRobotWheel,
-    RunRobotWheel,
-    StopRobotWheel
+    GAME_PANEL_INVENTORY_LAST_7,
+    &game.panel.inventory_last[6],
+    TYPE_ELEMENT,
   },
   {
-    EL_TIMEGATE_SWITCH_ACTIVE,
-    EL_TIMEGATE_SWITCH,
-    0,
-    InitTimegateWheel,
-    RunTimegateWheel,
-    NULL
+    GAME_PANEL_INVENTORY_LAST_8,
+    &game.panel.inventory_last[7],
+    TYPE_ELEMENT,
   },
   {
-    EL_EMC_MAGIC_BALL_ACTIVE,
-    EL_EMC_MAGIC_BALL_ACTIVE,
-    0,
-    InitMagicBallDelay,
-    NULL,
-    ActivateMagicBall
+    GAME_PANEL_KEY_1,
+    &game.panel.key[0],
+    TYPE_ELEMENT,
   },
-
   {
-    EL_UNDEFINED,
-    EL_UNDEFINED,
-    -1,
-    NULL,
-    NULL,
-    NULL
-  }
-};
-
-struct
-{
-  int element;
-  int push_delay_fixed, push_delay_random;
-}
-push_delay_list[] =
-{
-  { EL_SPRING,                 0, 0 },
-  { EL_BALLOON,                        0, 0 },
-
-  { EL_SOKOBAN_OBJECT,         2, 0 },
-  { EL_SOKOBAN_FIELD_FULL,     2, 0 },
-  { EL_SATELLITE,              2, 0 },
-  { EL_SP_DISK_YELLOW,         2, 0 },
-
-  { EL_UNDEFINED,              0, 0 },
-};
-
-struct
-{
-  int element;
-  int move_stepsize;
-}
-move_stepsize_list[] =
-{
-  { EL_AMOEBA_DROP,            2 },
-  { EL_AMOEBA_DROPPING,                2 },
-  { EL_QUICKSAND_FILLING,      1 },
-  { EL_QUICKSAND_EMPTYING,     1 },
-  { EL_MAGIC_WALL_FILLING,     2 },
-  { EL_BD_MAGIC_WALL_FILLING,  2 },
-  { EL_MAGIC_WALL_EMPTYING,    2 },
-  { EL_BD_MAGIC_WALL_EMPTYING, 2 },
-
-  { EL_UNDEFINED,              0 },
-};
-
-struct
-{
-  int element;
-  int count;
-}
-collect_count_list[] =
-{
-  { EL_EMERALD,                        1 },
-  { EL_BD_DIAMOND,             1 },
-  { EL_EMERALD_YELLOW,         1 },
-  { EL_EMERALD_RED,            1 },
-  { EL_EMERALD_PURPLE,         1 },
-  { EL_DIAMOND,                        3 },
-  { EL_SP_INFOTRON,            1 },
-  { EL_PEARL,                  5 },
-  { EL_CRYSTAL,                        8 },
-
-  { EL_UNDEFINED,              0 },
-};
-
-struct
-{
-  int element;
-  int direction;
-}
-access_direction_list[] =
-{
-  { EL_TUBE_ANY,                       MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
-  { EL_TUBE_VERTICAL,                                       MV_UP | MV_DOWN },
-  { EL_TUBE_HORIZONTAL,                        MV_LEFT | MV_RIGHT                   },
-  { EL_TUBE_VERTICAL_LEFT,             MV_LEFT |            MV_UP | MV_DOWN },
-  { EL_TUBE_VERTICAL_RIGHT,                      MV_RIGHT | MV_UP | MV_DOWN },
-  { EL_TUBE_HORIZONTAL_UP,             MV_LEFT | MV_RIGHT | MV_UP           },
-  { EL_TUBE_HORIZONTAL_DOWN,           MV_LEFT | MV_RIGHT |         MV_DOWN },
-  { EL_TUBE_LEFT_UP,                   MV_LEFT |            MV_UP           },
-  { EL_TUBE_LEFT_DOWN,                 MV_LEFT |                    MV_DOWN },
-  { EL_TUBE_RIGHT_UP,                            MV_RIGHT | MV_UP           },
-  { EL_TUBE_RIGHT_DOWN,                                  MV_RIGHT |         MV_DOWN },
-
-  { EL_SP_PORT_LEFT,                             MV_RIGHT                   },
-  { EL_SP_PORT_RIGHT,                  MV_LEFT                              },
-  { EL_SP_PORT_UP,                                                  MV_DOWN },
-  { EL_SP_PORT_DOWN,                                        MV_UP           },
-  { EL_SP_PORT_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
-  { EL_SP_PORT_VERTICAL,                                    MV_UP | MV_DOWN },
-  { EL_SP_PORT_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
-  { EL_SP_GRAVITY_PORT_LEFT,                     MV_RIGHT                   },
-  { EL_SP_GRAVITY_PORT_RIGHT,          MV_LEFT                              },
-  { EL_SP_GRAVITY_PORT_UP,                                          MV_DOWN },
-  { EL_SP_GRAVITY_PORT_DOWN,                                MV_UP           },
-  { EL_SP_GRAVITY_ON_PORT_LEFT,                          MV_RIGHT                   },
-  { EL_SP_GRAVITY_ON_PORT_RIGHT,       MV_LEFT                              },
-  { EL_SP_GRAVITY_ON_PORT_UP,                                       MV_DOWN },
-  { EL_SP_GRAVITY_ON_PORT_DOWN,                                     MV_UP           },
-  { EL_SP_GRAVITY_OFF_PORT_LEFT,                 MV_RIGHT                   },
-  { EL_SP_GRAVITY_OFF_PORT_RIGHT,      MV_LEFT                              },
-  { EL_SP_GRAVITY_OFF_PORT_UP,                                      MV_DOWN },
-  { EL_SP_GRAVITY_OFF_PORT_DOWN,                            MV_UP           },
-
-  { EL_UNDEFINED,                      MV_NONE                              }
-};
-
-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]) || \
-                                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 int playfield_scan_start_x = 0;
-static int playfield_scan_start_y = 0;
-static int playfield_scan_delta_x = 1;
-static int playfield_scan_delta_y = 1;
-
-#define SCAN_PLAYFIELD(x, y)   for ((y) = playfield_scan_start_y;      \
-                                    (y) >= 0 && (y) <= lev_fieldy - 1; \
-                                    (y) += playfield_scan_delta_y)     \
-                               for ((x) = playfield_scan_start_x;      \
-                                    (x) >= 0 && (x) <= lev_fieldx - 1; \
-                                    (x) += playfield_scan_delta_x)     \
+    GAME_PANEL_KEY_2,
+    &game.panel.key[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_3,
+    &game.panel.key[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_4,
+    &game.panel.key[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_5,
+    &game.panel.key[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_6,
+    &game.panel.key[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_7,
+    &game.panel.key[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_8,
+    &game.panel.key[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_WHITE,
+    &game.panel.key_white,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_WHITE_COUNT,
+    &game.panel.key_white_count,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_SCORE,
+    &game.panel.score,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_HIGHSCORE,
+    &game.panel.highscore,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_TIME,
+    &game.panel.time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_TIME_HH,
+    &game.panel.time_hh,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_TIME_MM,
+    &game.panel.time_mm,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_TIME_SS,
+    &game.panel.time_ss,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_FRAME,
+    &game.panel.frame,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_SHIELD_NORMAL,
+    &game.panel.shield_normal,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_SHIELD_NORMAL_TIME,
+    &game.panel.shield_normal_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_SHIELD_DEADLY,
+    &game.panel.shield_deadly,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_SHIELD_DEADLY_TIME,
+    &game.panel.shield_deadly_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_EXIT,
+    &game.panel.exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_EMC_MAGIC_BALL,
+    &game.panel.emc_magic_ball,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
+    &game.panel.emc_magic_ball_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_LIGHT_SWITCH,
+    &game.panel.light_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_LIGHT_SWITCH_TIME,
+    &game.panel.light_switch_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_TIMEGATE_SWITCH,
+    &game.panel.timegate_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_TIMEGATE_SWITCH_TIME,
+    &game.panel.timegate_switch_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_SWITCHGATE_SWITCH,
+    &game.panel.switchgate_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_EMC_LENSES,
+    &game.panel.emc_lenses,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_EMC_LENSES_TIME,
+    &game.panel.emc_lenses_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_EMC_MAGNIFIER,
+    &game.panel.emc_magnifier,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_EMC_MAGNIFIER_TIME,
+    &game.panel.emc_magnifier_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_BALLOON_SWITCH,
+    &game.panel.balloon_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_DYNABOMB_NUMBER,
+    &game.panel.dynabomb_number,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_DYNABOMB_SIZE,
+    &game.panel.dynabomb_size,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_DYNABOMB_POWER,
+    &game.panel.dynabomb_power,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_PENGUINS,
+    &game.panel.penguins,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_SOKOBAN_OBJECTS,
+    &game.panel.sokoban_objects,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_SOKOBAN_FIELDS,
+    &game.panel.sokoban_fields,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ROBOT_WHEEL,
+    &game.panel.robot_wheel,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_1,
+    &game.panel.conveyor_belt[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_2,
+    &game.panel.conveyor_belt[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_3,
+    &game.panel.conveyor_belt[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_4,
+    &game.panel.conveyor_belt[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
+    &game.panel.conveyor_belt_switch[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
+    &game.panel.conveyor_belt_switch[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
+    &game.panel.conveyor_belt_switch[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
+    &game.panel.conveyor_belt_switch[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_MAGIC_WALL,
+    &game.panel.magic_wall,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_MAGIC_WALL_TIME,
+    &game.panel.magic_wall_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_GRAVITY_STATE,
+    &game.panel.gravity_state,
+    TYPE_STRING,
+  },
+  {
+    GAME_PANEL_GRAPHIC_1,
+    &game.panel.graphic[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_2,
+    &game.panel.graphic[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_3,
+    &game.panel.graphic[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_4,
+    &game.panel.graphic[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_5,
+    &game.panel.graphic[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_6,
+    &game.panel.graphic[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_7,
+    &game.panel.graphic[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_8,
+    &game.panel.graphic[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_1,
+    &game.panel.element[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_2,
+    &game.panel.element[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_3,
+    &game.panel.element[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_4,
+    &game.panel.element[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_5,
+    &game.panel.element[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_6,
+    &game.panel.element[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_7,
+    &game.panel.element[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_8,
+    &game.panel.element[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_1,
+    &game.panel.element_count[0],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_2,
+    &game.panel.element_count[1],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_3,
+    &game.panel.element_count[2],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_4,
+    &game.panel.element_count[3],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_5,
+    &game.panel.element_count[4],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_6,
+    &game.panel.element_count[5],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_7,
+    &game.panel.element_count[6],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_8,
+    &game.panel.element_count[7],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_1,
+    &game.panel.ce_score[0],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_2,
+    &game.panel.ce_score[1],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_3,
+    &game.panel.ce_score[2],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_4,
+    &game.panel.ce_score[3],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_5,
+    &game.panel.ce_score[4],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_6,
+    &game.panel.ce_score[5],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_7,
+    &game.panel.ce_score[6],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_8,
+    &game.panel.ce_score[7],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_CE_SCORE_1_ELEMENT,
+    &game.panel.ce_score_element[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_2_ELEMENT,
+    &game.panel.ce_score_element[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_3_ELEMENT,
+    &game.panel.ce_score_element[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_4_ELEMENT,
+    &game.panel.ce_score_element[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_5_ELEMENT,
+    &game.panel.ce_score_element[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_6_ELEMENT,
+    &game.panel.ce_score_element[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_7_ELEMENT,
+    &game.panel.ce_score_element[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_8_ELEMENT,
+    &game.panel.ce_score_element[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_PLAYER_NAME,
+    &game.panel.player_name,
+    TYPE_STRING,
+  },
+  {
+    GAME_PANEL_LEVEL_NAME,
+    &game.panel.level_name,
+    TYPE_STRING,
+  },
+  {
+    GAME_PANEL_LEVEL_AUTHOR,
+    &game.panel.level_author,
+    TYPE_STRING,
+  },
+
+  {
+    -1,
+    NULL,
+    -1,
+  }
+};
+
+/* 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) */
+#define INITIAL_MOVE_DELAY_OFF -1
+#define INITIAL_MOVE_DELAY_ON  0
+
+/* 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
+#define MOVE_DELAY_MAX_SPEED   1
+
+#define DOUBLE_MOVE_DELAY(x)   (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
+#define HALVE_MOVE_DELAY(x)    (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
+
+#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 */
+#define MOVE_STEPSIZE_NORMAL   (TILEX / MOVE_DELAY_NORMAL_SPEED)
+#define MOVE_STEPSIZE_MIN      (1)
+#define MOVE_STEPSIZE_MAX      (TILEX)
+
+#define GET_DX_FROM_DIR(d)     ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
+#define GET_DY_FROM_DIR(d)     ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
+
+#define        INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
+
+#define GET_NEW_PUSH_DELAY(e)  (   (element_info[e].push_delay_fixed) + \
+                                RND(element_info[e].push_delay_random))
+#define GET_NEW_DROP_DELAY(e)  (   (element_info[e].drop_delay_fixed) + \
+                                RND(element_info[e].drop_delay_random))
+#define GET_NEW_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
+                                RND(element_info[e].move_delay_random))
+#define GET_MAX_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
+                                   (element_info[e].move_delay_random))
+#define GET_NEW_CE_VALUE(e)    (   (element_info[e].ce_value_fixed_initial) +\
+                                RND(element_info[e].ce_value_random_initial))
+#define GET_CE_SCORE(e)                (   (element_info[e].collect_score))
+#define GET_CHANGE_DELAY(c)    (   ((c)->delay_fixed  * (c)->delay_frames) + \
+                                RND((c)->delay_random * (c)->delay_frames))
+#define GET_CE_DELAY_VALUE(c)  (   ((c)->delay_fixed) + \
+                                RND((c)->delay_random))
+
+
+#define GET_VALID_RUNTIME_ELEMENT(e)                                   \
+        ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
+
+#define RESOLVED_REFERENCE_ELEMENT(be, e)                              \
+       ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
+        (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
+        (be) + (e) - EL_SELF)
+
+#define GET_PLAYER_FROM_BITS(p)                                                \
+       (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
+
+#define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                          \
+       ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
+        (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
+        (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
+        (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
+        (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
+        (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
+        (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
+        RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
+        (e))
+
+#define CAN_GROW_INTO(e)                                               \
+       ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
+
+#define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                        \
+               (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
+                                       (condition)))
+
+#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) ||      \
+                                       (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) ||      \
+                                       (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) ||      \
+                                       (DONT_COLLIDE_WITH(e) &&        \
+                                        IS_PLAYER(x, y) &&             \
+                                        !PLAYER_ENEMY_PROTECTED(x, y))))
+
+#define ELEMENT_CAN_ENTER_FIELD(e, x, y)                               \
+       ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
+
+#define SATELLITE_CAN_ENTER_FIELD(x, y)                                        \
+       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)
+
+#define ANDROID_CAN_CLONE_FIELD(x, y)                                  \
+       (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[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)
+
+#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]))
+
+#define PACMAN_CAN_ENTER_FIELD(e, x, y)                                        \
+       ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[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]))
+
+#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])))
+#define DRAGON_CAN_ENTER_FIELD(e, x, y)                                        \
+       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
+
+#define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                       \
+       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
+
+#define SPRING_CAN_ENTER_FIELD(e, x, y)                                        \
+       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))
+
+#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)))
+
+#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))
+
+#define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
+#define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
+
+#define ACCESS_FROM(e, d)              (element_info[e].access_direction &(d))
+#define IS_WALKABLE_FROM(e, d)         (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
+#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 GAME_CTRL_ID_STOP              0
+#define GAME_CTRL_ID_PAUSE             1
+#define GAME_CTRL_ID_PLAY              2
+#define SOUND_CTRL_ID_MUSIC            3
+#define SOUND_CTRL_ID_LOOPS            4
+#define SOUND_CTRL_ID_SIMPLE           5
+#define GAME_CTRL_ID_SAVE              6
+#define GAME_CTRL_ID_LOAD              7
+
+#define NUM_GAME_BUTTONS               8
+
+
+/* forward declaration for internal use */
+
+static void CreateField(int, int, int);
+
+static void ResetGfxAnimation(int, int);
+
+static void SetPlayerWaiting(struct PlayerInfo *, boolean);
+static void AdvanceFrameAndPlayerCounters(int);
+
+static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
+static boolean MovePlayer(struct PlayerInfo *, int, int);
+static void ScrollPlayer(struct PlayerInfo *, int);
+static void ScrollScreen(struct PlayerInfo *, int);
+
+static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static boolean DigFieldByCE(int, int, int);
+static boolean SnapField(struct PlayerInfo *, int, int);
+static boolean DropElement(struct PlayerInfo *);
+
+static void InitBeltMovement(void);
+static void CloseAllOpenTimegates(void);
+static void CheckGravityMovement(struct PlayerInfo *);
+static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
+static void KillPlayerUnlessEnemyProtected(int, int);
+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);
+static boolean ChangeElement(int, int, int, int);
+
+static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
+#define CheckTriggeredElementChange(x, y, e, ev)                       \
+       CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
+#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)         \
+       CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
+#define CheckTriggeredElementChangeBySide(x, y, e, ev, s)              \
+       CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
+#define CheckTriggeredElementChangeByPage(x, y, e, ev, p)              \
+       CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
+
+static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
+#define CheckElementChange(x, y, e, te, ev)                            \
+       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
+#define CheckElementChangeByPlayer(x, y, e, ev, p, s)                  \
+       CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
+#define CheckElementChangeBySide(x, y, e, te, ev, s)                   \
+       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
+
+static void PlayLevelSound(int, int, int);
+static void PlayLevelSoundNearest(int, int, int);
+static void PlayLevelSoundAction(int, int, int);
+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 HandleGameButtons(struct GadgetInfo *);
+
+int AmoebeNachbarNr(int, int);
+void AmoebeUmwandeln(int, int);
+void ContinueMoving(int, int);
+void Bang(int, int);
+void InitMovDir(int, int);
+void InitAmoebaNr(int, int);
+int NewHiScore(void);
+
+void TestIfGoodThingHitsBadThing(int, int, int);
+void TestIfBadThingHitsGoodThing(int, int, int);
+void TestIfPlayerTouchesBadThing(int, int);
+void TestIfPlayerRunsIntoBadThing(int, int, int);
+void TestIfBadThingTouchesPlayer(int, int);
+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 *);
+
+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) */
+#define MAX_ELEMENT_CHANGE_RECURSION_DEPTH     (MAX_PLAYFIELD_WIDTH * 10)
+
+#define RECURSION_LOOP_DETECTION_START(e, rc)                          \
+{                                                                      \
+  if (recursion_loop_detected)                                         \
+    return (rc);                                                       \
+                                                                       \
+  if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)       \
+  {                                                                    \
+    recursion_loop_detected = TRUE;                                    \
+    recursion_loop_element = (e);                                      \
+  }                                                                    \
+                                                                       \
+  recursion_loop_depth++;                                              \
+}
+
+#define RECURSION_LOOP_DETECTION_END()                                 \
+{                                                                      \
+  recursion_loop_depth--;                                              \
+}
+
+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             */
+/* ------------------------------------------------------------------------- */
+
+/* forward declaration for changer functions */
+static void InitBuggyBase(int, int);
+static void WarnBuggyBase(int, int);
+
+static void InitTrap(int, int);
+static void ActivateTrap(int, int);
+static void ChangeActiveTrap(int, int);
+
+static void InitRobotWheel(int, int);
+static void RunRobotWheel(int, int);
+static void StopRobotWheel(int, int);
+
+static void InitTimegateWheel(int, int);
+static void RunTimegateWheel(int, int);
+
+static void InitMagicBallDelay(int, int);
+static void ActivateMagicBall(int, int);
+
+struct ChangingElementInfo
+{
+  int element;
+  int target_element;
+  int change_delay;
+  void (*pre_change_function)(int x, int y);
+  void (*change_function)(int x, int y);
+  void (*post_change_function)(int x, int y);
+};
+
+static struct ChangingElementInfo change_delay_list[] =
+{
+  {
+    EL_NUT_BREAKING,
+    EL_EMERALD,
+    6,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_PEARL_BREAKING,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EXIT_OPENING,
+    EL_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EXIT_CLOSING,
+    EL_EXIT_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_STEEL_EXIT_OPENING,
+    EL_STEEL_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_STEEL_EXIT_CLOSING,
+    EL_STEEL_EXIT_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EM_EXIT_OPENING,
+    EL_EM_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EM_EXIT_CLOSING,
+#if 1
+    EL_EMPTY,
+#else
+    EL_EM_EXIT_CLOSED,
+#endif
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EM_STEEL_EXIT_OPENING,
+    EL_EM_STEEL_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EM_STEEL_EXIT_CLOSING,
+#if 1
+    EL_STEELWALL,
+#else
+    EL_EM_STEEL_EXIT_CLOSED,
+#endif
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_EXIT_OPENING,
+    EL_SP_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_EXIT_CLOSING,
+    EL_SP_EXIT_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SWITCHGATE_OPENING,
+    EL_SWITCHGATE_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SWITCHGATE_CLOSING,
+    EL_SWITCHGATE_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_TIMEGATE_OPENING,
+    EL_TIMEGATE_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_TIMEGATE_CLOSING,
+    EL_TIMEGATE_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+
+  {
+    EL_ACID_SPLASH_LEFT,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_ACID_SPLASH_RIGHT,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE,
+    EL_SP_BUGGY_BASE_ACTIVATING,
+    0,
+    InitBuggyBase,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE_ACTIVATING,
+    EL_SP_BUGGY_BASE_ACTIVE,
+    0,
+    InitBuggyBase,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE_ACTIVE,
+    EL_SP_BUGGY_BASE,
+    0,
+    InitBuggyBase,
+    WarnBuggyBase,
+    NULL
+  },
+  {
+    EL_TRAP,
+    EL_TRAP_ACTIVE,
+    0,
+    InitTrap,
+    NULL,
+    ActivateTrap
+  },
+  {
+    EL_TRAP_ACTIVE,
+    EL_TRAP,
+    31,
+    NULL,
+    ChangeActiveTrap,
+    NULL
+  },
+  {
+    EL_ROBOT_WHEEL_ACTIVE,
+    EL_ROBOT_WHEEL,
+    0,
+    InitRobotWheel,
+    RunRobotWheel,
+    StopRobotWheel
+  },
+  {
+    EL_TIMEGATE_SWITCH_ACTIVE,
+    EL_TIMEGATE_SWITCH,
+    0,
+    InitTimegateWheel,
+    RunTimegateWheel,
+    NULL
+  },
+  {
+    EL_DC_TIMEGATE_SWITCH_ACTIVE,
+    EL_DC_TIMEGATE_SWITCH,
+    0,
+    InitTimegateWheel,
+    RunTimegateWheel,
+    NULL
+  },
+  {
+    EL_EMC_MAGIC_BALL_ACTIVE,
+    EL_EMC_MAGIC_BALL_ACTIVE,
+    0,
+    InitMagicBallDelay,
+    NULL,
+    ActivateMagicBall
+  },
+  {
+    EL_EMC_SPRING_BUMPER_ACTIVE,
+    EL_EMC_SPRING_BUMPER,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_DIAGONAL_SHRINKING,
+    EL_UNDEFINED,
+    0,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_DIAGONAL_GROWING,
+    EL_UNDEFINED,
+    0,
+    NULL,
+    NULL,
+    NULL,
+  },
+
+  {
+    EL_UNDEFINED,
+    EL_UNDEFINED,
+    -1,
+    NULL,
+    NULL,
+    NULL
+  }
+};
+
+struct
+{
+  int element;
+  int push_delay_fixed, push_delay_random;
+}
+push_delay_list[] =
+{
+  { EL_SPRING,                 0, 0 },
+  { EL_BALLOON,                        0, 0 },
+
+  { EL_SOKOBAN_OBJECT,         2, 0 },
+  { EL_SOKOBAN_FIELD_FULL,     2, 0 },
+  { EL_SATELLITE,              2, 0 },
+  { EL_SP_DISK_YELLOW,         2, 0 },
+
+  { EL_UNDEFINED,              0, 0 },
+};
+
+struct
+{
+  int element;
+  int move_stepsize;
+}
+move_stepsize_list[] =
+{
+  { EL_AMOEBA_DROP,            2 },
+  { EL_AMOEBA_DROPPING,                2 },
+  { EL_QUICKSAND_FILLING,      1 },
+  { EL_QUICKSAND_EMPTYING,     1 },
+  { EL_QUICKSAND_FAST_FILLING, 2 },
+  { EL_QUICKSAND_FAST_EMPTYING,        2 },
+  { EL_MAGIC_WALL_FILLING,     2 },
+  { EL_MAGIC_WALL_EMPTYING,    2 },
+  { EL_BD_MAGIC_WALL_FILLING,  2 },
+  { EL_BD_MAGIC_WALL_EMPTYING, 2 },
+  { EL_DC_MAGIC_WALL_FILLING,  2 },
+  { EL_DC_MAGIC_WALL_EMPTYING, 2 },
+
+  { EL_UNDEFINED,              0 },
+};
+
+struct
+{
+  int element;
+  int count;
+}
+collect_count_list[] =
+{
+  { EL_EMERALD,                        1 },
+  { EL_BD_DIAMOND,             1 },
+  { EL_EMERALD_YELLOW,         1 },
+  { EL_EMERALD_RED,            1 },
+  { EL_EMERALD_PURPLE,         1 },
+  { EL_DIAMOND,                        3 },
+  { EL_SP_INFOTRON,            1 },
+  { EL_PEARL,                  5 },
+  { EL_CRYSTAL,                        8 },
+
+  { EL_UNDEFINED,              0 },
+};
+
+struct
+{
+  int element;
+  int direction;
+}
+access_direction_list[] =
+{
+  { EL_TUBE_ANY,                       MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_TUBE_VERTICAL,                                       MV_UP | MV_DOWN },
+  { EL_TUBE_HORIZONTAL,                        MV_LEFT | MV_RIGHT                   },
+  { EL_TUBE_VERTICAL_LEFT,             MV_LEFT |            MV_UP | MV_DOWN },
+  { EL_TUBE_VERTICAL_RIGHT,                      MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_TUBE_HORIZONTAL_UP,             MV_LEFT | MV_RIGHT | MV_UP           },
+  { EL_TUBE_HORIZONTAL_DOWN,           MV_LEFT | MV_RIGHT |         MV_DOWN },
+  { EL_TUBE_LEFT_UP,                   MV_LEFT |            MV_UP           },
+  { EL_TUBE_LEFT_DOWN,                 MV_LEFT |                    MV_DOWN },
+  { EL_TUBE_RIGHT_UP,                            MV_RIGHT | MV_UP           },
+  { EL_TUBE_RIGHT_DOWN,                                  MV_RIGHT |         MV_DOWN },
+
+  { EL_SP_PORT_LEFT,                             MV_RIGHT                   },
+  { EL_SP_PORT_RIGHT,                  MV_LEFT                              },
+  { EL_SP_PORT_UP,                                                  MV_DOWN },
+  { EL_SP_PORT_DOWN,                                        MV_UP           },
+  { EL_SP_PORT_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
+  { EL_SP_PORT_VERTICAL,                                    MV_UP | MV_DOWN },
+  { EL_SP_PORT_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_SP_GRAVITY_PORT_LEFT,                     MV_RIGHT                   },
+  { EL_SP_GRAVITY_PORT_RIGHT,          MV_LEFT                              },
+  { EL_SP_GRAVITY_PORT_UP,                                          MV_DOWN },
+  { EL_SP_GRAVITY_PORT_DOWN,                                MV_UP           },
+  { EL_SP_GRAVITY_ON_PORT_LEFT,                          MV_RIGHT                   },
+  { EL_SP_GRAVITY_ON_PORT_RIGHT,       MV_LEFT                              },
+  { EL_SP_GRAVITY_ON_PORT_UP,                                       MV_DOWN },
+  { EL_SP_GRAVITY_ON_PORT_DOWN,                                     MV_UP           },
+  { EL_SP_GRAVITY_OFF_PORT_LEFT,                 MV_RIGHT                   },
+  { EL_SP_GRAVITY_OFF_PORT_RIGHT,      MV_LEFT                              },
+  { EL_SP_GRAVITY_OFF_PORT_UP,                                      MV_DOWN },
+  { EL_SP_GRAVITY_OFF_PORT_DOWN,                            MV_UP           },
+
+  { EL_UNDEFINED,                      MV_NONE                              }
+};
+
+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]) || \
+                                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 int playfield_scan_start_x = 0;
+static int playfield_scan_start_y = 0;
+static int playfield_scan_delta_x = 1;
+static int playfield_scan_delta_y = 1;
+
+#define SCAN_PLAYFIELD(x, y)   for ((y) = playfield_scan_start_y;      \
+                                    (y) >= 0 && (y) <= lev_fieldy - 1; \
+                                    (y) += playfield_scan_delta_y)     \
+                               for ((x) = playfield_scan_start_x;      \
+                                    (x) >= 0 && (x) <= lev_fieldx - 1; \
+                                    (x) += playfield_scan_delta_x)
+
+#ifdef DEBUG
+void DEBUG_SetMaximumDynamite()
+{
+  int i;
+
+  for (i = 0; i < MAX_INVENTORY_SIZE; i++)
+    if (local_player->inventory_size < MAX_INVENTORY_SIZE)
+      local_player->inventory_element[local_player->inventory_size++] =
+       EL_DYNAMITE;
+}
+#endif
+
+static void InitPlayfieldScanModeVars()
+{
+  if (game.use_reverse_scan_direction)
+  {
+    playfield_scan_start_x = lev_fieldx - 1;
+    playfield_scan_start_y = lev_fieldy - 1;
+
+    playfield_scan_delta_x = -1;
+    playfield_scan_delta_y = -1;
+  }
+  else
+  {
+    playfield_scan_start_x = 0;
+    playfield_scan_start_y = 0;
+
+    playfield_scan_delta_x = 1;
+    playfield_scan_delta_y = 1;
+  }
+}
+
+static void InitPlayfieldScanMode(int mode)
+{
+  game.use_reverse_scan_direction =
+    (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
+
+  InitPlayfieldScanModeVars();
+}
+
+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 */
+  move_stepsize = (1 << log_2(move_stepsize));
+
+  return TILEX / move_stepsize;
+}
+
+static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
+                              boolean init_game)
+{
+  int player_nr = player->index_nr;
+  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 */
+  player->move_delay_value_next = move_delay;
+
+  /* information if player can move must be set separately */
+  player->cannot_move = cannot_move;
+
+  if (init_game)
+  {
+    player->move_delay       = game.initial_move_delay[player_nr];
+    player->move_delay_value = game.initial_move_delay_value[player_nr];
+
+    player->move_delay_value_next = -1;
+
+    player->move_delay_reset_counter = 0;
+  }
+}
+
+void GetPlayerConfig()
+{
+  GameFrameDelay = setup.game_frame_delay;
+
+  if (!audio.sound_available)
+    setup.sound_simple = FALSE;
+
+  if (!audio.loops_available)
+    setup.sound_loops = FALSE;
+
+  if (!audio.music_available)
+    setup.sound_music = FALSE;
+
+  if (!video.fullscreen_available)
+    setup.fullscreen = FALSE;
+
+  setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
+
+  SetAudioMode(setup.sound);
+  InitJoysticks();
+}
+
+int GetElementFromGroupElement(int element)
+{
+  if (IS_GROUP_ELEMENT(element))
+  {
+    struct ElementGroupInfo *group = element_info[element].group;
+    int last_anim_random_frame = gfx.anim_random_frame;
+    int element_pos;
+
+    if (group->choice_mode == ANIM_RANDOM)
+      gfx.anim_random_frame = RND(group->num_elements_resolved);
+
+    element_pos = getAnimationFrame(group->num_elements_resolved, 1,
+                                   group->choice_mode, 0,
+                                   group->choice_pos);
+
+    if (group->choice_mode == ANIM_RANDOM)
+      gfx.anim_random_frame = last_anim_random_frame;
+
+    group->choice_pos++;
+
+    element = group->element_resolved[element_pos];
+  }
+
+  return element;
+}
+
+static void InitPlayerField(int x, int y, int element, boolean init_game)
+{
+  if (element == EL_SP_MURPHY)
+  {
+    if (init_game)
+    {
+      if (stored_player[0].present)
+      {
+       Feld[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;
+    }
+  }
+
+  if (init_game)
+  {
+    struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
+    int jx = player->jx, jy = player->jy;
+
+    player->present = TRUE;
+
+    player->block_last_field = (element == EL_SP_MURPHY ?
+                               level.sp_block_last_field :
+                               level.block_last_field);
+
+    /* ---------- initialize player's last field block delay --------------- */
+
+    /* 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 */
+    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 */
+    if (game.use_block_last_field_bug)
+      player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
+
+    if (!options.network || player->connected)
+    {
+      player->active = TRUE;
+
+      /* remove potentially duplicate players */
+      if (StorePlayer[jx][jy] == Feld[x][y])
+       StorePlayer[jx][jy] = 0;
+
+      StorePlayer[x][y] = Feld[x][y];
+
+#if DEBUG_INIT_PLAYER
+      if (options.debug)
+      {
+       printf("- player element %d activated", player->element_nr);
+       printf(" (local player is %d and currently %s)\n",
+              local_player->element_nr,
+              local_player->active ? "active" : "not active");
+      }
+    }
+#endif
+
+    Feld[x][y] = EL_EMPTY;
+
+    player->jx = player->last_jx = x;
+    player->jy = player->last_jy = y;
+  }
+
+#if USE_PLAYER_REANIMATION
+  if (!init_game)
+  {
+    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 */
+  }
+#endif
+}
+
+static void InitField(int x, int y, boolean init_game)
+{
+  int element = Feld[x][y];
+
+  switch (element)
+  {
+    case EL_SP_MURPHY:
+    case EL_PLAYER_1:
+    case EL_PLAYER_2:
+    case EL_PLAYER_3:
+    case EL_PLAYER_4:
+      InitPlayerField(x, y, element, init_game);
+      break;
+
+    case EL_SOKOBAN_FIELD_PLAYER:
+      element = Feld[x][y] = EL_PLAYER_1;
+      InitField(x, y, init_game);
+
+      element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
+      InitField(x, y, init_game);
+      break;
+
+    case EL_SOKOBAN_FIELD_EMPTY:
+      local_player->sokobanfields_still_needed++;
+      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;
+      break;
+
+    case EL_BUG:
+    case EL_BUG_RIGHT:
+    case EL_BUG_UP:
+    case EL_BUG_LEFT:
+    case EL_BUG_DOWN:
+    case EL_SPACESHIP:
+    case EL_SPACESHIP_RIGHT:
+    case EL_SPACESHIP_UP:
+    case EL_SPACESHIP_LEFT:
+    case EL_SPACESHIP_DOWN:
+    case EL_BD_BUTTERFLY:
+    case EL_BD_BUTTERFLY_RIGHT:
+    case EL_BD_BUTTERFLY_UP:
+    case EL_BD_BUTTERFLY_LEFT:
+    case EL_BD_BUTTERFLY_DOWN:
+    case EL_BD_FIREFLY:
+    case EL_BD_FIREFLY_RIGHT:
+    case EL_BD_FIREFLY_UP:
+    case EL_BD_FIREFLY_LEFT:
+    case EL_BD_FIREFLY_DOWN:
+    case EL_PACMAN_RIGHT:
+    case EL_PACMAN_UP:
+    case EL_PACMAN_LEFT:
+    case EL_PACMAN_DOWN:
+    case EL_YAMYAM:
+    case EL_YAMYAM_LEFT:
+    case EL_YAMYAM_RIGHT:
+    case EL_YAMYAM_UP:
+    case EL_YAMYAM_DOWN:
+    case EL_DARK_YAMYAM:
+    case EL_ROBOT:
+    case EL_PACMAN:
+    case EL_SP_SNIKSNAK:
+    case EL_SP_ELECTRON:
+    case EL_MOLE:
+    case EL_MOLE_LEFT:
+    case EL_MOLE_RIGHT:
+    case EL_MOLE_UP:
+    case EL_MOLE_DOWN:
+      InitMovDir(x, y);
+      break;
+
+    case EL_AMOEBA_FULL:
+    case EL_BD_AMOEBA:
+      InitAmoebaNr(x, y);
+      break;
+
+    case EL_AMOEBA_DROP:
+      if (y == lev_fieldy - 1)
+      {
+       Feld[x][y] = EL_AMOEBA_GROWING;
+       Store[x][y] = EL_AMOEBA_WET;
+      }
+      break;
+
+    case EL_DYNAMITE_ACTIVE:
+    case EL_SP_DISK_RED_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:
+      MovDelay[x][y] = 96;
+      break;
+
+    case EL_EM_DYNAMITE_ACTIVE:
+      MovDelay[x][y] = 32;
+      break;
+
+    case EL_LAMP:
+      local_player->lights_still_needed++;
+      break;
+
+    case EL_PENGUIN:
+      local_player->friends_still_needed++;
+      break;
+
+    case EL_PIG:
+    case EL_DRAGON:
+      GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
+      break;
+
+    case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
+    case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
+    case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
+    case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
+    case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
+    case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
+    case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
+    case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
+    case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
+    case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
+    case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
+    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]);
+
+       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 */
+       {
+         Feld[x][y] = Feld[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;
+      break;
+
+    case EL_INVISIBLE_STEELWALL:
+    case EL_INVISIBLE_WALL:
+    case EL_INVISIBLE_SAND:
+      if (game.light_time_left > 0 ||
+         game.lenses_time_left > 0)
+        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+      break;
+
+    case EL_EMC_MAGIC_BALL:
+      if (game.ball_state)
+       Feld[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;
+      break;
+
+    case EL_TRIGGER_PLAYER:
+    case EL_TRIGGER_ELEMENT:
+    case EL_TRIGGER_CE_VALUE:
+    case EL_TRIGGER_CE_SCORE:
+    case EL_SELF:
+    case EL_ANY_ELEMENT:
+    case EL_CURRENT_CE_VALUE:
+    case EL_CURRENT_CE_SCORE:
+    case EL_PREV_CE_1:
+    case EL_PREV_CE_2:
+    case EL_PREV_CE_3:
+    case EL_PREV_CE_4:
+    case EL_PREV_CE_5:
+    case EL_PREV_CE_6:
+    case EL_PREV_CE_7:
+    case EL_PREV_CE_8:
+    case EL_NEXT_CE_1:
+    case EL_NEXT_CE_2:
+    case EL_NEXT_CE_3:
+    case EL_NEXT_CE_4:
+    case EL_NEXT_CE_5:
+    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;
+      break;
+
+    default:
+      if (IS_CUSTOM_ELEMENT(element))
+      {
+       if (CAN_MOVE(element))
+         InitMovDir(x, y);
+
+#if USE_NEW_CUSTOM_VALUE
+       if (!element_info[element].use_last_ce_value || init_game)
+         CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
+#endif
+      }
+      else if (IS_GROUP_ELEMENT(element))
+      {
+       Feld[x][y] = GetElementFromGroupElement(element);
+
+       InitField(x, y, init_game);
+      }
+
+      break;
+  }
+
+  if (!init_game)
+    CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
+}
+
+static inline void InitField_WithBug1(int x, int y, boolean init_game)
+{
+  InitField(x, y, init_game);
+
+  /* not needed to call InitMovDir() -- already done by InitField()! */
+  if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
+      CAN_MOVE(Feld[x][y]))
+    InitMovDir(x, y);
+}
+
+static inline void InitField_WithBug2(int x, int y, boolean init_game)
+{
+  int old_element = Feld[x][y];
+
+  InitField(x, y, init_game);
+
+  /* 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))
+    InitMovDir(x, y);
+
+  /* this case is in fact a combination of not less than three bugs:
+     first, it calls InitMovDir() for elements that can move, although this is
+     already done by InitField(); then, it checks the element that was at this
+     field _before_ the call to InitField() (which can change it); lastly, it
+     was not called for "mole with direction" elements, which were treated as
+     "cannot move" due to (fixed) wrong element initialization in "src/init.c"
+  */
+}
+
+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 :
+                         level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                         EL_EM_KEY_1 : EL_KEY_1);
 
-static void InitPlayfieldScanModeVars()
+  return key_base_element + key_nr;
+}
+
+static int get_next_dropped_element(struct PlayerInfo *player)
 {
-  if (game.use_reverse_scan_direction)
+  return (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);
+}
+
+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 */
+
+  if (pos < 0)
   {
-    playfield_scan_start_x = lev_fieldx - 1;
-    playfield_scan_start_y = lev_fieldy - 1;
+    int min_inventory_size = -pos;
+    int inventory_pos = player->inventory_size - min_inventory_size;
+    int min_dynabombs_left = min_inventory_size - player->inventory_size;
 
-    playfield_scan_delta_x = -1;
-    playfield_scan_delta_y = -1;
+    return (player->inventory_size >= min_inventory_size ?
+           player->inventory_element[inventory_pos] :
+           player->inventory_infinite_element != EL_UNDEFINED ?
+           player->inventory_infinite_element :
+           player->dynabombs_left >= min_dynabombs_left ?
+           EL_DYNABOMB_PLAYER_1 + player->index_nr :
+           EL_UNDEFINED);
   }
   else
   {
-    playfield_scan_start_x = 0;
-    playfield_scan_start_y = 0;
+    int min_dynabombs_left = pos + 1;
+    int min_inventory_size = pos + 1 - player->dynabombs_left;
+    int inventory_pos = pos - player->dynabombs_left;
 
-    playfield_scan_delta_x = 1;
-    playfield_scan_delta_y = 1;
+    return (player->inventory_infinite_element != EL_UNDEFINED ?
+           player->inventory_infinite_element :
+           player->dynabombs_left >= min_dynabombs_left ?
+           EL_DYNABOMB_PLAYER_1 + player->index_nr :
+           player->inventory_size >= min_inventory_size ?
+           player->inventory_element[inventory_pos] :
+           EL_UNDEFINED);
   }
 }
 
-static void InitPlayfieldScanMode(int mode)
+static int compareGamePanelOrderInfo(const void *object1, const void *object2)
 {
-  game.use_reverse_scan_direction =
-    (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
+  const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
+  const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
+  int compare_result;
 
-  InitPlayfieldScanModeVars();
+  if (gpo1->sort_priority != gpo2->sort_priority)
+    compare_result = gpo1->sort_priority - gpo2->sort_priority;
+  else
+    compare_result = gpo1->nr - gpo2->nr;
+
+  return compare_result;
 }
 
-void GetPlayerConfig()
+void InitGameControlValues()
 {
-  if (!audio.sound_available)
-    setup.sound_simple = FALSE;
-
-  if (!audio.loops_available)
-    setup.sound_loops = FALSE;
+  int i;
 
-  if (!audio.music_available)
-    setup.sound_music = FALSE;
+  for (i = 0; game_panel_controls[i].nr != -1; i++)
+  {
+    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+    struct GamePanelOrderInfo *gpo = &game_panel_order[i];
+    struct TextPosInfo *pos = gpc->pos;
+    int nr = gpc->nr;
+    int type = gpc->type;
 
-  if (!video.fullscreen_available)
-    setup.fullscreen = FALSE;
+    if (nr != i)
+    {
+      Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
+      Error(ERR_EXIT, "this should not happen -- please debug");
+    }
 
-  setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
+    /* force update of game controls after initialization */
+    gpc->value = gpc->last_value = -1;
+    gpc->frame = gpc->last_frame = -1;
+    gpc->gfx_frame = -1;
 
-  SetAudioMode(setup.sound);
-  InitJoysticks();
-}
+    /* determine panel value width for later calculation of alignment */
+    if (type == TYPE_INTEGER || type == TYPE_STRING)
+    {
+      pos->width = pos->size * getFontWidth(pos->font);
+      pos->height = getFontHeight(pos->font);
+    }
+    else if (type == TYPE_ELEMENT)
+    {
+      pos->width = pos->size;
+      pos->height = pos->size;
+    }
 
-static int getBeltNrFromBeltElement(int element)
-{
-  return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
-         element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
-         element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
-}
+    /* fill structure for game panel draw order */
+    gpo->nr = gpc->nr;
+    gpo->sort_priority = pos->sort_priority;
+  }
 
-static int getBeltNrFromBeltActiveElement(int element)
-{
-  return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
-         element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
-         element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
+  /* sort game panel controls according to sort_priority and control number */
+  qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
+       sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
 }
 
-static int getBeltNrFromBeltSwitchElement(int element)
+void UpdatePlayfieldElementCount()
 {
-  return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
-         element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
-         element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
-}
+  boolean use_element_count = FALSE;
+  int i, j, x, y;
 
-static int getBeltDirNrFromBeltSwitchElement(int element)
-{
-  static int belt_base_element[4] =
-  {
-    EL_CONVEYOR_BELT_1_SWITCH_LEFT,
-    EL_CONVEYOR_BELT_2_SWITCH_LEFT,
-    EL_CONVEYOR_BELT_3_SWITCH_LEFT,
-    EL_CONVEYOR_BELT_4_SWITCH_LEFT
-  };
+  /* 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;
 
-  int belt_nr = getBeltNrFromBeltSwitchElement(element);
-  int belt_dir_nr = element - belt_base_element[belt_nr];
+  if (!use_element_count)
+    return;
 
-  return (belt_dir_nr % 3);
-}
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    element_info[i].element_count = 0;
 
-static int getBeltDirFromBeltSwitchElement(int element)
-{
-  static int belt_move_dir[3] =
+  SCAN_PLAYFIELD(x, y)
   {
-    MV_LEFT,
-    MV_NONE,
-    MV_RIGHT
-  };
-
-  int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
+    element_info[Feld[x][y]].element_count++;
+  }
 
-  return belt_move_dir[belt_dir_nr];
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+    for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+      if (IS_IN_GROUP(j, i))
+       element_info[EL_GROUP_START + i].element_count +=
+         element_info[j].element_count;
 }
 
-static void InitPlayerField(int x, int y, int element, boolean init_game)
+void UpdateGameControlValues()
 {
-  if (element == EL_SP_MURPHY)
+  int i, k;
+  int time = (local_player->LevelSolved ?
+             local_player->LevelSolved_CountingTime :
+             level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+             level.native_em_level->lev->time :
+             level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+             level.native_sp_level->game_sp->time_played :
+             game.no_time_limit ? TimePlayed : TimeLeft);
+  int score = (local_player->LevelSolved ?
+              local_player->LevelSolved_CountingScore :
+              level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+              level.native_em_level->lev->score :
+              level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+              level.native_sp_level->game_sp->score :
+              local_player->score);
+  int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+             level.native_em_level->lev->required :
+             level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+             level.native_sp_level->game_sp->infotrons_still_needed :
+             local_player->gems_still_needed);
+  int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                    level.native_em_level->lev->required > 0 :
+                    level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+                    level.native_sp_level->game_sp->infotrons_still_needed > 0 :
+                    local_player->gems_still_needed > 0 ||
+                    local_player->sokobanfields_still_needed > 0 ||
+                    local_player->lights_still_needed > 0);
+
+  UpdatePlayfieldElementCount();
+
+  /* update game panel control values */
+
+  game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
+  game_panel_controls[GAME_PANEL_GEMS].value = gems;
+
+  game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
+  for (i = 0; i < MAX_NUM_KEYS; i++)
+    game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
+  game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
+  game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
+
+  if (game.centered_player_nr == -1)
   {
-    if (init_game)
+    for (i = 0; i < MAX_PLAYERS; i++)
     {
-      if (stored_player[0].present)
-      {
-       Feld[x][y] = EL_SP_MURPHY_CLONE;
+      /* only one player in Supaplex game engine */
+      if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
+       break;
 
-       return;
+      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))
+           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+             get_key_element_from_nr(k);
+       }
+       else if (stored_player[i].key[k])
+         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+           get_key_element_from_nr(k);
       }
+
+      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         level.native_em_level->ply[i]->dynamite;
+      else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         level.native_sp_level->game_sp->red_disk_count;
       else
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         stored_player[i].inventory_size;
+
+      if (stored_player[i].num_white_keys > 0)
+       game_panel_controls[GAME_PANEL_KEY_WHITE].value =
+         EL_DC_KEY_WHITE;
+
+      game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
+       stored_player[i].num_white_keys;
+    }
+  }
+  else
+  {
+    int player_nr = game.centered_player_nr;
+
+    for (k = 0; k < MAX_NUM_KEYS; k++)
+    {
+      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
       {
-       stored_player[0].use_murphy = TRUE;
+       if (level.native_em_level->ply[player_nr]->keys & (1 << k))
+         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+           get_key_element_from_nr(k);
       }
-
-      Feld[x][y] = EL_PLAYER_1;
+      else if (stored_player[player_nr].key[k])
+       game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+         get_key_element_from_nr(k);
     }
+
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       level.native_em_level->ply[player_nr]->dynamite;
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       level.native_sp_level->game_sp->red_disk_count;
+    else
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       stored_player[player_nr].inventory_size;
+
+    if (stored_player[player_nr].num_white_keys > 0)
+      game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
+
+    game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
+      stored_player[player_nr].num_white_keys;
   }
 
-  if (init_game)
+  for (i = 0; i < NUM_PANEL_INVENTORY; i++)
   {
-    struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
-    int jx = player->jx, jy = player->jy;
+    game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
+      get_inventory_element_from_pos(local_player, i);
+    game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
+      get_inventory_element_from_pos(local_player, -i - 1);
+  }
 
-    player->present = TRUE;
+  game_panel_controls[GAME_PANEL_SCORE].value = score;
+  game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
 
-    player->block_last_field = (element == EL_SP_MURPHY ?
-                               level.sp_block_last_field :
-                               level.block_last_field);
+  game_panel_controls[GAME_PANEL_TIME].value = time;
 
-    /* ---------- initialize player's last field block delay --------------- */
+  game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
+  game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
+  game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
 
-    /* always start with reliable default value (no adjustment needed) */
-    player->block_delay_adjustment = 0;
+  game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
 
-    /* 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;
+  game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
+    (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
+     EL_EMPTY);
+  game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
+    local_player->shield_normal_time_left;
+  game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
+    (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
+     EL_EMPTY);
+  game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
+    local_player->shield_deadly_time_left;
+
+  game_panel_controls[GAME_PANEL_EXIT].value =
+    (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_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
+    (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
+     EL_EMC_MAGIC_BALL_SWITCH);
+
+  game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
+    (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
+  game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
+    game.light_time_left;
+
+  game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
+    (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
+  game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
+    game.timegate_time_left;
+
+  game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
+    EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
+
+  game_panel_controls[GAME_PANEL_EMC_LENSES].value =
+    (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
+  game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
+    game.lenses_time_left;
+
+  game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
+    (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
+  game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
+    game.magnify_time_left;
+
+  game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
+    (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
+     game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
+     game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
+     game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
+     EL_BALLOON_SWITCH_NONE);
+
+  game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
+    local_player->dynabomb_count;
+  game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
+    local_player->dynabomb_size;
+  game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
+    (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
+
+  game_panel_controls[GAME_PANEL_PENGUINS].value =
+    local_player->friends_still_needed;
+
+  game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
+    local_player->sokobanfields_still_needed;
+  game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
+    local_player->sokobanfields_still_needed;
+
+  game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
+    (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
 
-    /* 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);
+  for (i = 0; i < NUM_BELTS; i++)
+  {
+    game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
+      (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
+       EL_CONVEYOR_BELT_1_MIDDLE) + i;
+    game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
+      getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
+  }
 
-    if (!options.network || player->connected)
-    {
-      player->active = TRUE;
+  game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
+    (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
+  game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
+    game.magic_wall_time_left;
 
-      /* remove potentially duplicate players */
-      if (StorePlayer[jx][jy] == Feld[x][y])
-       StorePlayer[jx][jy] = 0;
+#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
 
-      StorePlayer[x][y] = Feld[x][y];
+  for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
+    game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
 
-      if (options.debug)
+  for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+    game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
+      (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
+       game.panel.element[i].id : EL_UNDEFINED);
+
+  for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+    game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
+      (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
+       element_info[game.panel.element_count[i].id].element_count : 0);
+
+  for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+    game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
+      (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
+       element_info[game.panel.ce_score[i].id].collect_score : 0);
+
+  for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+    game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
+      (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
+       element_info[game.panel.ce_score_element[i].id].collect_score :
+       EL_UNDEFINED);
+
+  game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
+  game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
+  game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
+
+  /* update game panel control frames */
+
+  for (i = 0; game_panel_controls[i].nr != -1; i++)
+  {
+    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+
+    if (gpc->type == TYPE_ELEMENT)
+    {
+      if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
       {
-       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");
-      }
-    }
+       int last_anim_random_frame = gfx.anim_random_frame;
+       int element = gpc->value;
+       int graphic = el2panelimg(element);
 
-    Feld[x][y] = EL_EMPTY;
+       if (gpc->value != gpc->last_value)
+       {
+         gpc->gfx_frame = 0;
+         gpc->gfx_random = INIT_GFX_RANDOM();
+       }
+       else
+       {
+         gpc->gfx_frame++;
 
-    player->jx = player->last_jx = x;
-    player->jy = player->last_jy = y;
+         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;
+
+       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+         gpc->gfx_frame = element_info[element].collect_score;
+
+       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
+                                             gpc->gfx_frame);
+
+       if (ANIM_MODE(graphic) == ANIM_RANDOM)
+         gfx.anim_random_frame = last_anim_random_frame;
+      }
+    }
   }
 }
 
-static void InitField(int x, int y, boolean init_game)
+void DisplayGameControlValues()
 {
-  int element = Feld[x][y];
+  boolean redraw_panel = FALSE;
+  int i;
 
-  switch (element)
+  for (i = 0; game_panel_controls[i].nr != -1; i++)
   {
-    case EL_SP_MURPHY:
-    case EL_PLAYER_1:
-    case EL_PLAYER_2:
-    case EL_PLAYER_3:
-    case EL_PLAYER_4:
-      InitPlayerField(x, y, element, init_game);
-      break;
+    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
 
-    case EL_SOKOBAN_FIELD_PLAYER:
-      element = Feld[x][y] = EL_PLAYER_1;
-      InitField(x, y, init_game);
+    if (PANEL_DEACTIVATED(gpc->pos))
+      continue;
 
-      element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
-      InitField(x, y, init_game);
-      break;
+    if (gpc->value == gpc->last_value &&
+       gpc->frame == gpc->last_frame)
+      continue;
 
-    case EL_SOKOBAN_FIELD_EMPTY:
-      local_player->sokobanfields_still_needed++;
-      break;
+    redraw_panel = TRUE;
+  }
 
-    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;
-      break;
+  if (!redraw_panel)
+    return;
 
-    case EL_BUG_RIGHT:
-    case EL_BUG_UP:
-    case EL_BUG_LEFT:
-    case EL_BUG_DOWN:
-    case EL_BUG:
-    case EL_SPACESHIP_RIGHT:
-    case EL_SPACESHIP_UP:
-    case EL_SPACESHIP_LEFT:
-    case EL_SPACESHIP_DOWN:
-    case EL_SPACESHIP:
-    case EL_BD_BUTTERFLY_RIGHT:
-    case EL_BD_BUTTERFLY_UP:
-    case EL_BD_BUTTERFLY_LEFT:
-    case EL_BD_BUTTERFLY_DOWN:
-    case EL_BD_BUTTERFLY:
-    case EL_BD_FIREFLY_RIGHT:
-    case EL_BD_FIREFLY_UP:
-    case EL_BD_FIREFLY_LEFT:
-    case EL_BD_FIREFLY_DOWN:
-    case EL_BD_FIREFLY:
-    case EL_PACMAN_RIGHT:
-    case EL_PACMAN_UP:
-    case EL_PACMAN_LEFT:
-    case EL_PACMAN_DOWN:
-    case EL_YAMYAM:
-    case EL_DARK_YAMYAM:
-    case EL_ROBOT:
-    case EL_PACMAN:
-    case EL_SP_SNIKSNAK:
-    case EL_SP_ELECTRON:
-    case EL_MOLE_LEFT:
-    case EL_MOLE_RIGHT:
-    case EL_MOLE_UP:
-    case EL_MOLE_DOWN:
-    case EL_MOLE:
-      InitMovDir(x, y);
-      break;
+  /* copy default game door content to main double buffer */
+#if 1
+  /* !!! CHECK AGAIN !!! */
+  SetPanelBackground();
+  // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+  DrawBackground(DX, DY, DXSIZE, DYSIZE);
+#else
+  BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+            DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+#endif
 
-    case EL_AMOEBA_FULL:
-    case EL_BD_AMOEBA:
-      InitAmoebaNr(x, y);
-      break;
+  /* redraw game control buttons */
+#if 1
+  RedrawGameButtons();
+#else
+  UnmapGameButtons();
+  MapGameButtons();
+#endif
 
-    case EL_AMOEBA_DROP:
-      if (y == lev_fieldy - 1)
-      {
-       Feld[x][y] = EL_AMOEBA_GROWING;
-       Store[x][y] = EL_AMOEBA_WET;
-      }
-      break;
+  game_status = 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;
+    int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
 
-    case EL_DYNAMITE_ACTIVE:
-    case EL_SP_DISK_RED_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:
-      MovDelay[x][y] = 96;
-      break;
+    if (PANEL_DEACTIVATED(pos))
+      continue;
 
-    case EL_LAMP:
-      local_player->lights_still_needed++;
-      break;
+#if 0
+    if (value == last_value && frame == last_frame)
+      continue;
+#endif
 
-    case EL_PENGUIN:
-      local_player->friends_still_needed++;
-      break;
+    gpc->last_value = value;
+    gpc->last_frame = frame;
 
-    case EL_PIG:
-    case EL_DRAGON:
-      GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
-      break;
+#if 0
+    printf("::: value %d changed from %d to %d\n", nr, last_value, value);
+#endif
 
-    case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
-    case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
-    case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
-    case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
-    case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
-    case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
-    case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
-    case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
-    case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
-    case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
-    case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
-    case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
-      if (init_game)
+    if (type == TYPE_INTEGER)
+    {
+      if (nr == GAME_PANEL_LEVEL_NUMBER ||
+         nr == GAME_PANEL_TIME)
       {
-       int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
-       int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
-       int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
+       boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
 
-       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 */
+       if (use_dynamic_size)           /* use dynamic number of digits */
        {
-         Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
+         int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
+         int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
+         int size2 = size1 + 1;
+         int font1 = pos->font;
+         int font2 = pos->font_alt;
+
+         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
        }
       }
-      break;
-
-    case EL_SWITCHGATE_SWITCH_DOWN:    /* always start with same switch pos */
-      if (init_game)
-       Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
-      break;
 
-    case EL_LIGHT_SWITCH_ACTIVE:
-      if (init_game)
-       game.light_time_left = level.time_light * FRAMES_PER_SECOND;
-      break;
+#if 1
+      /* correct text size if "digits" is zero or less */
+      if (size <= 0)
+       size = strlen(int2str(value, size));
 
-    case EL_EMC_MAGIC_BALL:
-      if (game.ball_state)
-       Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
-      break;
+      /* dynamically correct text alignment */
+      pos->width = size * getFontWidth(font);
+#endif
 
-    case EL_EMC_MAGIC_BALL_SWITCH:
-      if (game.ball_state)
-       Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
-      break;
+      DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                 int2str(value, size), font, mask_mode);
+    }
+    else if (type == TYPE_ELEMENT)
+    {
+      int element, graphic;
+      Bitmap *src_bitmap;
+      int src_x, src_y;
+      int width, height;
+      int dst_x = PANEL_XPOS(pos);
+      int dst_y = PANEL_YPOS(pos);
 
-    default:
 #if 1
-      if (IS_CUSTOM_ELEMENT(element))
+      if (value != EL_UNDEFINED && value != EL_EMPTY)
       {
-       if (CAN_MOVE(element))
-         InitMovDir(x, y);
+       element = value;
+       graphic = el2panelimg(value);
 
-#if USE_NEW_CUSTOM_VALUE
-       if (!element_info[element].use_last_ce_value || init_game)
-         CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
+       // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
+
+#if 1
+       if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
+         size = TILESIZE;
 #endif
+
+       getSizedGraphicSource(graphic, frame, size, &src_bitmap,
+                             &src_x, &src_y);
+
+       width  = graphic_info[graphic].width  * size / TILESIZE;
+       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 (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
-       InitMovDir(x, y);
-#endif
-      else if (IS_GROUP_ELEMENT(element))
+      if (value == EL_UNDEFINED || value == EL_EMPTY)
       {
-       struct ElementGroupInfo *group = element_info[element].group;
-       int last_anim_random_frame = gfx.anim_random_frame;
-       int element_pos;
+       element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
+       graphic = el2panelimg(element);
 
-       if (group->choice_mode == ANIM_RANDOM)
-         gfx.anim_random_frame = RND(group->num_elements_resolved);
+       src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+       src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
+       src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
+      }
+      else
+      {
+       element = value;
+       graphic = el2panelimg(value);
 
-       element_pos = getAnimationFrame(group->num_elements_resolved, 1,
-                                       group->choice_mode, 0,
-                                       group->choice_pos);
+       getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
+      }
 
-       if (group->choice_mode == ANIM_RANDOM)
-         gfx.anim_random_frame = last_anim_random_frame;
+      width  = graphic_info[graphic].width  * size / TILESIZE;
+      height = graphic_info[graphic].height * size / TILESIZE;
 
-       group->choice_pos++;
+      BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
+#endif
+    }
+    else if (type == TYPE_STRING)
+    {
+      boolean active = (value != 0);
+      char *state_normal = "off";
+      char *state_active = "on";
+      char *state = (active ? state_active : state_normal);
+      char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
+                nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
+                nr == GAME_PANEL_LEVEL_NAME    ? level.name :
+                nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
 
-       Feld[x][y] = group->element_resolved[element_pos];
+      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
 
-       InitField(x, y, init_game);
+       font = (active ? font2 : font1);
       }
-      break;
-  }
+
+      if (s != NULL)
+      {
+       char *s_cut;
 
 #if 1
-  if (!init_game)
-    CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
+       if (size <= 0)
+       {
+         /* don't truncate output if "chars" is zero or less */
+         size = strlen(s);
+
+         /* dynamically correct text alignment */
+         pos->width = size * getFontWidth(font);
+       }
 #endif
 
-#if 0
+       s_cut = getStringCopyN(s, size);
 
-#if USE_NEW_CUSTOM_VALUE
+       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                   s_cut, font, mask_mode);
 
-#if 1
-  CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
-#else
-  CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
-#endif
+       free(s_cut);
+      }
+    }
 
-#endif
+    redraw_mask |= REDRAW_DOOR_1;
+  }
 
-#endif
+  game_status = GAME_MODE_PLAYING;
 }
 
-static inline void InitField_WithBug1(int x, int y, boolean init_game)
+void UpdateAndDisplayGameControlValues()
 {
-  InitField(x, y, init_game);
+  if (tape.warp_forward)
+    return;
 
-  /* not needed to call InitMovDir() -- already done by InitField()! */
-  if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
-      CAN_MOVE(Feld[x][y]))
-    InitMovDir(x, y);
+  UpdateGameControlValues();
+  DisplayGameControlValues();
 }
 
-static inline void InitField_WithBug2(int x, int y, boolean init_game)
+void DrawGameValue_Emeralds(int value)
 {
-  int old_element = Feld[x][y];
+  struct TextPosInfo *pos = &game.panel.gems;
+  int font_nr = pos->font;
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->size;
 
-  InitField(x, y, init_game);
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-  /* 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))
-    InitMovDir(x, y);
+  if (PANEL_DEACTIVATED(pos))
+    return;
 
-  /* this case is in fact a combination of not less than three bugs:
-     first, it calls InitMovDir() for elements that can move, although this is
-     already done by InitField(); then, it checks the element that was at this
-     field _before_ the call to InitField() (which can change it); lastly, it
-     was not called for "mole with direction" elements, which were treated as
-     "cannot move" due to (fixed) wrong element initialization in "src/init.c"
-  */
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
-inline void DrawGameValue_Emeralds(int value)
+void DrawGameValue_Dynamite(int value)
 {
-  DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
+  struct TextPosInfo *pos = &game.panel.inventory_count;
+  int font_nr = pos->font;
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->size;
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
+    return;
+
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
-inline void DrawGameValue_Dynamite(int value)
+void DrawGameValue_Score(int value)
 {
-  DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
+  struct TextPosInfo *pos = &game.panel.score;
+  int font_nr = pos->font;
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->size;
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
+    return;
+
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
-inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+void DrawGameValue_Time(int value)
 {
-  int i;
+  struct TextPosInfo *pos = &game.panel.time;
+  static int last_value = -1;
+  int chars1 = 3;
+  int chars2 = 4;
+  int chars = pos->size;
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+  int font_nr = font1_nr;
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
+    return;
 
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
+  if (use_dynamic_chars)               /* use dynamic number of chars */
   {
-    if (key[i])
-      DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
-                        el2edimg(EL_KEY_1 + i));
-    else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-                DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
-                MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
+    chars   = (value < 1000 ? chars1   : chars2);
+    font_nr = (value < 1000 ? font1_nr : font2_nr);
   }
-}
 
-inline void DrawGameValue_Score(int value)
-{
-  DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
+  /* 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;
 }
 
-inline void DrawGameValue_Time(int value)
+void DrawGameValue_Level(int value)
 {
-  if (value < 1000)
-    DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
-  else
-    DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
+  struct TextPosInfo *pos = &game.panel.level_number;
+  int chars1 = 2;
+  int chars2 = 3;
+  int chars = pos->size;
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+  int font_nr = font1_nr;
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
+    return;
+
+  if (use_dynamic_chars)               /* use dynamic number of chars */
+  {
+    chars   = (level_nr < 100 ? chars1   : chars2);
+    font_nr = (level_nr < 100 ? font1_nr : font2_nr);
+  }
+
+  pos->width = chars * getFontWidth(font_nr);
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
-inline void DrawGameValue_Level(int value)
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
 {
-  if (level_nr < 100)
-    DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
-  else
+  int i;
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  for (i = 0; i < MAX_NUM_KEYS; i++)
   {
-    /* misuse area for displaying emeralds to draw bigger level number */
-    DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
-               int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
+    struct TextPosInfo *pos = &game.panel.key[i];
+    int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
+    int src_y = DOOR_GFX_PAGEY1 + 123;
+    int dst_x = PANEL_XPOS(pos);
+    int dst_y = PANEL_YPOS(pos);
 
-    /* now copy it to the area for displaying level number */
-    BlitBitmap(drawto, drawto,
-              DX_EMERALDS, DY_EMERALDS + 1,
-              getFontWidth(FONT_LEVEL_NUMBER) * 3,
-              getFontHeight(FONT_LEVEL_NUMBER) - 1,
-              DX_LEVEL - 1, DY_LEVEL + 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);
 
-    /* restore the area for displaying emeralds */
-    DrawGameValue_Emeralds(local_player->gems_still_needed);
+    if (PANEL_DEACTIVATED(pos))
+      continue;
+
+#if 0
+    /* masked blit with tiles from half-size scaled bitmap does not work yet
+       (no mask bitmap created for these sizes after loading and scaling) --
+       solution: load without creating mask, scale, then create final mask */
+
+    BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+              MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+
+    if (key[i])
+    {
+      Bitmap *src_bitmap;
+      int src_x, src_y;
+
+      getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
 
-    /* yes, this is all really ugly :-) */
+      SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+                   dst_x - src_x, dst_y - src_y);
+      BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
+                      dst_x, dst_y);
+    }
+#else
+    if (key[i])
+      DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
+    else
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+#endif
   }
 }
 
@@ -1120,6 +2943,11 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
   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);
 
@@ -1133,70 +2961,57 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
   DrawGameValue_Keys(key);
 }
 
-void DrawGameDoorValues()
+void UpdateGameDoorValues()
 {
-  int i;
-
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-  {
-    DrawGameDoorValues_EM();
-
-    return;
-  }
-
-  DrawGameValue_Level(level_nr);
-
-  DrawGameValue_Emeralds(local_player->gems_still_needed);
-  DrawGameValue_Dynamite(local_player->inventory_size);
-  DrawGameValue_Score(local_player->score);
-  DrawGameValue_Time(TimeLeft);
+  UpdateGameControlValues();
+}
 
-  for (i = 0; i < MAX_PLAYERS; i++)
-    DrawGameValue_Keys(stored_player[i].key);
+void DrawGameDoorValues()
+{
+  DisplayGameControlValues();
 }
 
-static void resolve_group_element(int group_element, int recursion_depth)
+void DrawGameDoorValues_OLD()
 {
-  static int group_nr;
-  static struct ElementGroupInfo *group;
-  struct ElementGroupInfo *actual_group = element_info[group_element].group;
-  int i;
+  int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
+  int dynamite_value = 0;
+  int score_value = (local_player->LevelSolved ? local_player->score_final :
+                    local_player->score);
+  int gems_value = local_player->gems_still_needed;
+  int key_bits = 0;
+  int i, j;
 
-  if (recursion_depth > NUM_GROUP_ELEMENTS)    /* recursion too deep */
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
-    Error(ERR_WARN, "recursion too deep when resolving group element %d",
-         group_element - EL_GROUP_START + 1);
-
-    /* replace element which caused too deep recursion by question mark */
-    group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
+    DrawGameDoorValues_EM();
 
     return;
   }
 
-  if (recursion_depth == 0)                    /* initialization */
+  if (game.centered_player_nr == -1)
   {
-    group = element_info[group_element].group;
-    group_nr = group_element - EL_GROUP_START;
+    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);
 
-    group->num_elements_resolved = 0;
-    group->choice_pos = 0;
+      dynamite_value += stored_player[i].inventory_size;
+    }
   }
-
-  for (i = 0; i < actual_group->num_elements; i++)
+  else
   {
-    int element = actual_group->element[i];
-
-    if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
-      break;
-
-    if (IS_GROUP_ELEMENT(element))
-      resolve_group_element(element, recursion_depth + 1);
-    else
-    {
-      group->element_resolved[group->num_elements_resolved++] = element;
-      element_info[element].in_group[group_nr] = TRUE;
-    }
+    int player_nr = game.centered_player_nr;
+
+    for (i = 0; i < MAX_NUM_KEYS; i++)
+      if (stored_player[player_nr].key[i])
+       key_bits |= (1 << i);
+
+    dynamite_value = stored_player[player_nr].inventory_size;
   }
+
+  DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
+                   key_bits);
 }
 
 
@@ -1216,6 +3031,21 @@ static void InitGameEngine()
   game.engine_version = (tape.playing ? tape.engine_version :
                         level.game_version);
 
+  /* set single or multi-player game mode (needed for re-playing tapes) */
+  game.team_mode = setup.team_mode;
+
+  if (tape.playing)
+  {
+    int num_players = 0;
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (tape.player_participates[i])
+       num_players++;
+
+    /* multi-player tapes contain input data for more than one player */
+    game.team_mode = (num_players > 1);
+  }
+
   /* ---------------------------------------------------------------------- */
   /* set flags for bugs and changes according to active game engine version */
   /* ---------------------------------------------------------------------- */
@@ -1330,24 +3160,18 @@ static void InitGameEngine()
   printf("       => game.engine_version == %06d\n", game.engine_version);
 #endif
 
-  /* ---------- recursively resolve group elements ------------------------- */
-
-  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
-    for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
-      element_info[i].in_group[j] = FALSE;
-
-  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
-    resolve_group_element(EL_GROUP_START + i, 0);
-
   /* ---------- initialize player's initial move delay --------------------- */
 
   /* dynamically adjust player properties according to level information */
-  game.initial_move_delay_value =
-    (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+  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 */
-  game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
-                            game.initial_move_delay_value : 0);
+  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 --------------------- */
 
@@ -1404,7 +3228,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++)
   {
@@ -1442,6 +3266,27 @@ static void InitGameEngine()
     }
   }
 
+#if 1
+  /* ---------- initialize reference elements in change conditions --------- */
+
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+    struct ElementInfo *ei = &element_info[element];
+
+    for (j = 0; j < ei->num_change_pages; j++)
+    {
+      int trigger_element = ei->change_page[j].initial_trigger_element;
+
+      if (trigger_element >= EL_PREV_CE_8 &&
+         trigger_element <= EL_NEXT_CE_8)
+       trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
+
+      ei->change_page[j].trigger_element = trigger_element;
+    }
+  }
+#endif
+
   /* ---------- initialize run-time trigger player and element ------------- */
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -1451,9 +3296,11 @@ 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 = 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;
     }
   }
 
@@ -1490,6 +3337,9 @@ static void InitGameEngine()
              for (l = 0; l < group->num_elements_resolved; l++)
                trigger_events[group->element_resolved[l]][k] = TRUE;
            }
+           else if (trigger_element == EL_ANY_ELEMENT)
+             for (l = 0; l < MAX_NUM_ELEMENTS; l++)
+               trigger_events[l][k] = TRUE;
            else
              trigger_events[trigger_element][k] = TRUE;
          }
@@ -1505,8 +3355,17 @@ static void InitGameEngine()
   {
     if (!IS_CUSTOM_ELEMENT(i))
     {
-      element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
-      element_info[i].push_delay_random = game.default_push_delay_random;
+      /* 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;
+       element_info[i].push_delay_random = 8;
+      }
+      else
+      {
+       element_info[i].push_delay_fixed = 8;
+       element_info[i].push_delay_random = 8;
+      }
     }
   }
 
@@ -1613,6 +3472,18 @@ static void InitGameEngine()
         EL_EMPTY);
     }
   }
+
+  /* ---------- initialize recursion detection ------------------------------ */
+  recursion_loop_depth = 0;
+  recursion_loop_detected = FALSE;
+  recursion_loop_element = EL_UNDEFINED;
+
+  /* ---------- initialize graphics engine ---------------------------------- */
+  game.scroll_delay_value =
+    (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
+     setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
+  game.scroll_delay_value =
+    MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
 }
 
 int get_num_special_action(int element, int action_first, int action_last)
@@ -1638,6 +3509,7 @@ int get_num_special_action(int element, int action_first, int action_last)
   return num_special_action;
 }
 
+
 /*
   =============================================================================
   InitGame()
@@ -1651,9 +3523,68 @@ void InitGame()
   boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
+#if 0
+  boolean do_fading = (game_status == GAME_MODE_MAIN);
+#endif
+#if 1
+  int initial_move_dir = MV_DOWN;
+#else
+  int initial_move_dir = MV_NONE;
+#endif
   int i, j, x, y;
 
+#if 1
+  game_status = GAME_MODE_PLAYING;
+#endif
+
+#if 1
+
+  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
+
+#endif
+
+#if 0
+  printf("::: FADING OUT: DONE\n");
+  Delay(1000);
+#endif
+
+#if 0
+  game_status = GAME_MODE_PLAYING;
+#endif
+
+#if 1
+  /* needed if different viewport properties defined for playing */
+  ChangeViewportPropertiesIfNeeded();
+#endif
+
+#if 1
+  DrawCompleteVideoDisplay();
+#endif
+
   InitGameEngine();
+  InitGameControlValues();
 
   /* don't play tapes over network */
   network_playing = (options.network && !tape.playing);
@@ -1668,12 +3599,18 @@ void InitGame()
 
     player->present = FALSE;
     player->active = FALSE;
+    player->mapped = FALSE;
+
+    player->killed = FALSE;
+    player->reanimated = FALSE;
 
     player->action = 0;
     player->effective_action = 0;
     player->programmed_action = 0;
 
     player->score = 0;
+    player->score_final = 0;
+
     player->gems_still_needed = level.gems_needed;
     player->sokobanfields_still_needed = 0;
     player->lights_still_needed = 0;
@@ -1682,34 +3619,41 @@ void InitGame()
     for (j = 0; j < MAX_NUM_KEYS; j++)
       player->key[j] = FALSE;
 
+    player->num_white_keys = 0;
+
     player->dynabomb_count = 0;
     player->dynabomb_size = 1;
     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->gravity = level.initial_player_gravity[i];
+
     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
 
     player->actual_frame_counter = 0;
 
     player->step_counter = 0;
 
-    player->last_move_dir = MV_NONE;
+    player->last_move_dir = initial_move_dir;
+
+    player->is_active = FALSE;
 
     player->is_waiting = FALSE;
     player->is_moving = FALSE;
@@ -1720,31 +3664,23 @@ void InitGame()
     player->is_pushing = FALSE;
     player->is_switching = FALSE;
     player->is_dropping = FALSE;
+    player->is_dropping_pressed = FALSE;
 
     player->is_bored = FALSE;
     player->is_sleeping = FALSE;
 
-    player->cannot_move = FALSE;
-
     player->frame_counter_bored = -1;
     player->frame_counter_sleeping = -1;
 
     player->anim_delay_counter = 0;
     player->post_delay_counter = 0;
 
+    player->dir_waiting = initial_move_dir;
     player->action_waiting = ACTION_DEFAULT;
     player->last_action_waiting = ACTION_DEFAULT;
     player->special_action_bored = ACTION_DEFAULT;
     player->special_action_sleeping = ACTION_DEFAULT;
 
-    /* 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);
-    player->num_special_action_sleeping =
-      get_num_special_action(player->artwork_element,
-                            ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
-
     player->switch_x = -1;
     player->switch_y = -1;
 
@@ -1753,20 +3689,18 @@ void InitGame()
 
     player->show_envelope = 0;
 
-    player->move_delay       = game.initial_move_delay;
-    player->move_delay_value = game.initial_move_delay_value;
-
-    player->move_delay_value_next = -1;
-
-    player->move_delay_reset_counter = 0;
+    SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
 
     player->push_delay       = -1;     /* initialized when pushing starts */
     player->push_delay_value = game.initial_push_delay_value;
 
     player->drop_delay = 0;
+    player->drop_pressed_delay = 0;
 
-    player->last_jx = player->last_jy = 0;
-    player->jx = player->jy = 0;
+    player->last_jx = -1;
+    player->last_jy = -1;
+    player->jx = -1;
+    player->jy = -1;
 
     player->shield_normal_time_left = 0;
     player->shield_deadly_time_left = 0;
@@ -1774,11 +3708,41 @@ 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;
@@ -1806,15 +3770,21 @@ void InitGame()
 
   AllPlayersGone = FALSE;
 
+  game.no_time_limit = (level.time == 0);
+
   game.yamyam_content_nr = 0;
+  game.robot_wheel_active = FALSE;
   game.magic_wall_active = FALSE;
   game.magic_wall_time_left = 0;
   game.light_time_left = 0;
   game.timegate_time_left = 0;
   game.switchgate_pos = 0;
   game.wind_direction = level.wind_direction_initial;
-  game.gravity = level.initial_gravity;
+
+#if !USE_PLAYER_GRAVITY
+  game.gravity = FALSE;
   game.explosions_delayed = TRUE;
+#endif
 
   game.lenses_time_left = 0;
   game.magnify_time_left = 0;
@@ -1824,6 +3794,18 @@ void InitGame()
 
   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;
+
+  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;
+  }
+
   for (i = 0; i < NUM_BELTS; i++)
   {
     game.belt_dir[i] = MV_NONE;
@@ -1833,11 +3815,14 @@ void InitGame()
   for (i = 0; i < MAX_NUM_AMOEBA; i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
 
-#if 1
-  SCAN_PLAYFIELD(x, y)
-#else
-  for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+  {
+    printf("Player status at level initialization:\n");
+  }
 #endif
+
+  SCAN_PLAYFIELD(x, y)
   {
     Feld[x][y] = level.field[x][y];
     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
@@ -1851,6 +3836,7 @@ void InitGame()
     WasJustMoving[x][y] = 0;
     WasJustFalling[x][y] = 0;
     CheckCollision[x][y] = 0;
+    CheckImpact[x][y] = 0;
     Stop[x][y] = FALSE;
     Pushed[x][y] = FALSE;
 
@@ -1869,13 +3855,10 @@ void InitGame()
     GfxElement[x][y] = EL_UNDEFINED;
     GfxAction[x][y] = ACTION_DEFAULT;
     GfxDir[x][y] = MV_NONE;
+    GfxRedraw[x][y] = GFX_REDRAW_NONE;
   }
 
-#if 1
   SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
   {
     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
       emulate_bd = FALSE;
@@ -1885,10 +3868,25 @@ void InitGame()
       emulate_sp = FALSE;
 
     InitField(x, y, TRUE);
+
+    ResetGfxAnimation(x, y);
   }
 
   InitBeltMovement();
 
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    /* 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);
+    player->num_special_action_sleeping =
+      get_num_special_action(player->artwork_element,
+                            ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+  }
+
   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
                    emulate_sb ? EMU_SOKOBAN :
                    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
@@ -1911,40 +3909,228 @@ void InitGame()
        element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
     }
   }
-#endif
+#endif
+
+  /* initialize explosion and ignition delay */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    if (!IS_CUSTOM_ELEMENT(i))
+    {
+      int num_phase = 8;
+      int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
+                   game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
+                  game.emulation == EMU_SUPAPLEX ? 3 : 2);
+      int last_phase = (num_phase + 1) * delay;
+      int half_phase = (num_phase / 2) * delay;
+
+      element_info[i].explosion_delay = last_phase - 1;
+      element_info[i].ignition_delay = half_phase;
+
+      if (i == EL_BLACK_ORB)
+       element_info[i].ignition_delay = 1;
+    }
+
+#if 0
+    if (element_info[i].explosion_delay < 1)   /* !!! check again !!! */
+      element_info[i].explosion_delay = 1;
+
+    if (element_info[i].ignition_delay < 1)    /* !!! check again !!! */
+      element_info[i].ignition_delay = 1;
+#endif
+  }
+
+  /* correct non-moving belts to start moving left */
+  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
+  /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
+  /* choose default local player */
+  local_player = &stored_player[0];
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    stored_player[i].connected = FALSE;
+
+  local_player->connected = TRUE;
+  /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
+
+#if 0
+  printf("::: TEAM MODE: %d\n", game.team_mode);
+#endif
+
+  if (tape.playing)
+  {
+#if 1
+    for (i = 0; i < MAX_PLAYERS; i++)
+      stored_player[i].connected = tape.player_participates[i];
+#else
+    /* try to guess locally connected team mode players (needed for correct
+       assignment of player figures from level to locally playing players) */
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (tape.player_participates[i])
+       stored_player[i].connected = TRUE;
+#endif
+  }
+  else if (game.team_mode && !options.network)
+  {
+    /* 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
+  if (options.debug)
+  {
+    printf("Player status after level initialization:\n");
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
+
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
+            player->present,
+            player->connected,
+            player->active);
+
+      if (local_player == player)
+       printf(" (local player)");
+
+      printf("\n");
+    }
+  }
+#endif
+
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+    printf("Reassigning players ...\n");
+#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
+      if (options.debug)
+       printf("- looking for field player for player %d ...\n", i + 1);
+#endif
+
+      /* assign first free player found that is present in the playfield */
+
+#if 1
+      /* first try: look for unmapped playfield player that is not connected */
+      for (j = 0; j < MAX_PLAYERS; j++)
+       if (field_player == NULL &&
+           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];
+#else
+      /* first try: look for unmapped playfield player that is not connected */
+      if (field_player == NULL)
+       for (j = 0; j < MAX_PLAYERS; j++)
+         if (stored_player[j].present &&
+             !stored_player[j].mapped &&
+             !stored_player[j].connected)
+           field_player = &stored_player[j];
+
+      /* second try: look for *any* unmapped playfield player */
+      if (field_player == NULL)
+       for (j = 0; j < MAX_PLAYERS; j++)
+         if (stored_player[j].present &&
+             !stored_player[j].mapped)
+           field_player = &stored_player[j];
+#endif
+
+      if (field_player != NULL)
+      {
+       int jx = field_player->jx, jy = field_player->jy;
+
+#if DEBUG_INIT_PLAYER
+       if (options.debug)
+         printf("- found player %d\n", 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;
+
+       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
+       if (options.debug)
+         printf("- map_player_action[%d] == %d\n",
+                field_player->index_nr + 1, i + 1);
+#endif
+      }
+    }
+
+    if (player->connected && player->present)
+      player->mapped = TRUE;
+  }
 
-  /* initialize explosion and ignition delay */
-  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
   {
-    if (!IS_CUSTOM_ELEMENT(i))
-    {
-      int num_phase = 8;
-      int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
-                   game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
-                  game.emulation == EMU_SUPAPLEX ? 3 : 2);
-      int last_phase = (num_phase + 1) * delay;
-      int half_phase = (num_phase / 2) * delay;
+    printf("Player status after player assignment (first stage):\n");
 
-      element_info[i].explosion_delay = last_phase - 1;
-      element_info[i].ignition_delay = half_phase;
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
 
-      if (i == EL_BLACK_ORB)
-       element_info[i].ignition_delay = 1;
-    }
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
+            player->present,
+            player->connected,
+            player->active);
 
-#if 0
-    if (element_info[i].explosion_delay < 1)   /* !!! check again !!! */
-      element_info[i].explosion_delay = 1;
+      if (local_player == player)
+       printf(" (local player)");
 
-    if (element_info[i].ignition_delay < 1)    /* !!! check again !!! */
-      element_info[i].ignition_delay = 1;
-#endif
+      printf("\n");
+    }
   }
+#endif
 
-  /* 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 */
+#else
 
   /* check if any connected player was not found in playfield */
   for (i = 0; i < MAX_PLAYERS; i++)
@@ -1955,28 +4141,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)
+       if (field_player->present && !field_player->connected)
        {
          player->present = TRUE;
          player->active = TRUE;
 
-         some_player->present = FALSE;
-         some_player->active = FALSE;
-
-#if 0
-         player->element_nr = some_player->element_nr;
-#endif
+         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;
 
@@ -1985,14 +4169,47 @@ void InitGame()
       }
     }
   }
+#endif
+
+#if 0
+  printf("::: local_player->present == %d\n", local_player->present);
+#endif
 
   if (tape.playing)
   {
-    /* when playing a tape, eliminate all players which do not participate */
+    /* when playing a tape, eliminate all players who do not participate */
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+
+#if 1
+    if (!game.team_mode)
+#endif
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      if (stored_player[i].active &&
+         !tape.player_participates[map_player_action[i]])
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int jx = player->jx, jy = player->jy;
+
+#if DEBUG_INIT_PLAYER
+       if (options.debug)
+         printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
+#endif
+
+       player->active = FALSE;
+       StorePlayer[jx][jy] = 0;
+       Feld[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;
@@ -2002,8 +4219,9 @@ void InitGame()
        Feld[jx][jy] = EL_EMPTY;
       }
     }
+#endif
   }
-  else if (!options.network && !setup.team_mode)       /* && !tape.playing */
+  else if (!options.network && !game.team_mode)                /* && !tape.playing */
   {
     /* when in single player mode, eliminate all but the first active player */
 
@@ -2032,26 +4250,39 @@ void InitGame()
   /* 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 DEBUG_INIT_PLAYER
   if (options.debug)
   {
+    printf("Player status after player assignment (final stage):\n");
+
     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,
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
             player->present,
             player->connected,
             player->active);
+
       if (local_player == player)
-       printf("Player  %d is local player.\n", i+1);
+       printf(" (local player)");
+
+      printf("\n");
     }
   }
+#endif
 
   if (BorderElement == EL_EMPTY)
   {
@@ -2068,11 +4299,27 @@ void InitGame()
     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
   }
 
+#if NEW_TILESIZE
+
+  if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
+    SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+
+  if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
+    SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+
+  if (EVEN(SCR_FIELDX))
+    SBX_Left--;
+  if (EVEN(SCR_FIELDY))
+    SBY_Upper--;
+
+#else
+
   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
 
   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+#endif
 
   /* if local player not found, look for custom element that might create
      the player (make some assumptions about the right custom element) */
@@ -2083,11 +4330,7 @@ void InitGame()
     int found_element = EL_UNDEFINED;
     int player_nr = local_player->index_nr;
 
-#if 1
     SCAN_PLAYFIELD(x, y)
-#else
-    for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
     {
       int element = Feld[x][y];
       int content;
@@ -2116,7 +4359,8 @@ void InitGame()
          content = element_info[element].change_page[i].target_element;
          is_player = ELEM_IS_PLAYER(content);
 
-         if (is_player && (found_rating < 3 || element < found_element))
+         if (is_player && (found_rating < 3 ||
+                           (found_rating == 3 && element < found_element)))
          {
            start_x = x;
            start_y = y;
@@ -2133,7 +4377,8 @@ void InitGame()
        content = element_info[element].content.e[xx][yy];
        is_player = ELEM_IS_PLAYER(content);
 
-       if (is_player && (found_rating < 2 || element < found_element))
+       if (is_player && (found_rating < 2 ||
+                         (found_rating == 2 && element < found_element)))
        {
          start_x = x + xx - 1;
          start_y = y + yy - 1;
@@ -2153,7 +4398,8 @@ void InitGame()
 
          is_player = ELEM_IS_PLAYER(content);
 
-         if (is_player && (found_rating < 1 || element < found_element))
+         if (is_player && (found_rating < 1 ||
+                           (found_rating == 1 && element < found_element)))
          {
            start_x = x + xx - 1;
            start_y = y + yy - 1;
@@ -2184,13 +4430,61 @@ void InitGame()
                local_player->jy - MIDPOSY);
   }
 
+#if 0
+  printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
+#endif
+
+#if 0
+  /* do not use PLAYING mask for fading out from main screen */
+  game_status = GAME_MODE_MAIN;
+#endif
+
+#if 0
+
+  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
+
+#endif
+
+#if 0
+  game_status = GAME_MODE_PLAYING;
+#endif
+
   /* !!! FIX THIS (START) !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     InitGameEngine_EM();
+
+    /* blit playfield from scroll buffer to normal back buffer for fading in */
+    BlitScreenToBitmap_EM(backbuffer);
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    InitGameEngine_SP();
+
+    /* blit playfield from scroll buffer to normal back buffer for fading in */
+    BlitScreenToBitmap_SP(backbuffer);
   }
   else
   {
@@ -2201,22 +4495,59 @@ void InitGame()
     if (game.timegate_time_left == 0)
       CloseAllOpenTimegates();
 
+#if NEW_TILESIZE
+    BlitScreenToBitmap(backbuffer);
+#else
+    /* blit playfield from scroll buffer to normal back buffer for fading in */
     if (setup.soft_scrolling)
       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+#endif
 
     redraw_mask |= REDRAW_FROM_BACKBUFFER;
-    FadeToFront();
   }
   /* !!! FIX THIS (END) !!! */
 
+#if 1
+  FadeIn(REDRAW_FIELD);
+#else
+  if (do_fading)
+    FadeIn(REDRAW_FIELD);
+
+  BackToFront();
+#endif
+
   if (!game.restart_level)
   {
     /* copy default game door content to main double buffer */
+#if 1
+#if 1
+    /* !!! CHECK AGAIN !!! */
+    SetPanelBackground();
+    // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+    DrawBackground(DX, DY, DXSIZE, DYSIZE);
+#else
+    struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
+
+    /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
+    ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
+    BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
+              MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
+#endif
+#else
     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
               DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+#endif
   }
 
+  SetPanelBackground();
+  SetDrawBackgroundMask(REDRAW_DOOR_1);
+
+#if 1
+  UpdateAndDisplayGameControlValues();
+#else
+  UpdateGameDoorValues();
   DrawGameDoorValues();
+#endif
 
   if (!game.restart_level)
   {
@@ -2229,24 +4560,62 @@ void InitGame()
     MapTapeButtons();
 
     /* copy actual game door content to door double buffer for OpenDoor() */
+#if 1
+    BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
+#else
     BlitBitmap(drawto, bitmap_db_door,
               DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+#endif
 
     OpenDoor(DOOR_OPEN_ALL);
 
-    PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
+    PlaySound(SND_GAME_STARTING);
 
     if (setup.sound_music)
       PlayLevelMusic();
 
     KeyboardAutoRepeatOffUnlessAutoplay();
 
+#if DEBUG_INIT_PLAYER
     if (options.debug)
     {
+      printf("Player status (final):\n");
+
       for (i = 0; i < MAX_PLAYERS; i++)
-       printf("Player %d %sactive.\n",
-              i + 1, (stored_player[i].active ? "" : "not "));
+      {
+       struct PlayerInfo *player = &stored_player[i];
+
+       printf("- player %d: present == %d, connected == %d, active == %d",
+              i + 1,
+              player->present,
+              player->connected,
+              player->active);
+
+       if (local_player == player)
+         printf(" (local player)");
+
+       printf("\n");
+      }
     }
+#endif
+  }
+
+#if 1
+  UnmapAllGadgets();
+
+  MapGameButtons();
+  MapTapeButtons();
+#endif
+
+  if (!game.restart_level && !tape.playing)
+  {
+    LevelStats_incPlayed(level_nr);
+
+    SaveLevelSetup_SeriesInfo();
+
+#if 0
+    printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
+#endif
   }
 
   game.restart_level = FALSE;
@@ -2278,7 +4647,7 @@ void InitMovDir(int x, int y)
     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
   };
 
-  switch(element)
+  switch (element)
   {
     case EL_BUG_RIGHT:
     case EL_BUG_UP:
@@ -2320,6 +4689,14 @@ void InitMovDir(int x, int y)
       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
       break;
 
+    case EL_YAMYAM_LEFT:
+    case EL_YAMYAM_RIGHT:
+    case EL_YAMYAM_UP:
+    case EL_YAMYAM_DOWN:
+      Feld[x][y] = EL_YAMYAM;
+      MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
+      break;
+
     case EL_SP_SNIKSNAK:
       MovDir[x][y] = MV_UP;
       break;
@@ -2404,180 +4781,315 @@ void InitMovDir(int x, int y)
 
        for (i = 0; i < NUM_DIRECTIONS; i++)
        {
-         int x1 = x + xy[i][0];
-         int y1 = y + xy[i][1];
+         int x1 = x + xy[i][0];
+         int y1 = y + xy[i][1];
+
+         if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+         {
+           if (element == EL_BUG || element == EL_BD_BUTTERFLY)
+           {
+             MovDir[x][y] = direction[0][i];
+             break;
+           }
+           else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
+                    element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
+           {
+             MovDir[x][y] = direction[1][i];
+             break;
+           }
+         }
+       }
+      }
+      break;
+  }
+
+  GfxDir[x][y] = MovDir[x][y];
+}
+
+void InitAmoebaNr(int x, int y)
+{
+  int i;
+  int group_nr = AmoebeNachbarNr(x, y);
+
+  if (group_nr == 0)
+  {
+    for (i = 1; i < MAX_NUM_AMOEBA; i++)
+    {
+      if (AmoebaCnt[i] == 0)
+      {
+       group_nr = i;
+       break;
+      }
+    }
+  }
+
+  AmoebaNr[x][y] = group_nr;
+  AmoebaCnt[group_nr]++;
+  AmoebaCnt2[group_nr]++;
+}
+
+static void PlayerWins(struct PlayerInfo *player)
+{
+  player->LevelSolved = TRUE;
+  player->GameOver = TRUE;
+
+  player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                        level.native_em_level->lev->score : player->score);
+
+  player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
+                                     TimeLeft);
+  player->LevelSolved_CountingScore = player->score_final;
+}
+
+void GameWon()
+{
+  static int time, time_final;
+  static int score, score_final;
+  static int game_over_delay_1 = 0;
+  static int game_over_delay_2 = 0;
+  int game_over_delay_value_1 = 50;
+  int game_over_delay_value_2 = 50;
+
+  if (!local_player->LevelSolved_GameWon)
+  {
+    int i;
+
+    /* do not start end game actions before the player stops moving (to exit) */
+    if (local_player->MovPos)
+      return;
+
+    local_player->LevelSolved_GameWon = TRUE;
+    local_player->LevelSolved_SaveTape = tape.recording;
+    local_player->LevelSolved_SaveScore = !tape.playing;
+
+    if (!tape.playing)
+    {
+      LevelStats_incSolved(level_nr);
+
+      SaveLevelSetup_SeriesInfo();
+
+#if 0
+      printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
+#endif
+    }
+
+    if (tape.auto_play)                /* tape might already be stopped here */
+      tape.auto_play_level_solved = TRUE;
+
+#if 1
+    TapeStop();
+#endif
+
+    game_over_delay_1 = game_over_delay_value_1;
+    game_over_delay_2 = game_over_delay_value_2;
+
+    time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
+    score = score_final = local_player->score_final;
+
+    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];
+    }
+
+    local_player->score_final = score_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_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 */
+      {
+       /* 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 element = Feld[ExitX][ExitY];
 
-         if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+#if 0
+         if (element == EL_EM_EXIT_OPEN ||
+             element == EL_EM_STEEL_EXIT_OPEN)
          {
-           if (element == EL_BUG || element == EL_BD_BUTTERFLY)
-           {
-             MovDir[x][y] = direction[0][i];
-             break;
-           }
-           else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
-                    element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
-           {
-             MovDir[x][y] = direction[1][i];
-             break;
-           }
+           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);
          }
        }
+
+       /* player disappears */
+       DrawLevelField(ExitX, ExitY);
       }
-      break;
-  }
 
-  GfxDir[x][y] = MovDir[x][y];
-}
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
 
-void InitAmoebaNr(int x, int y)
-{
-  int i;
-  int group_nr = AmoebeNachbarNr(x, y);
+       if (player->present)
+       {
+         RemovePlayer(player);
 
-  if (group_nr == 0)
-  {
-    for (i = 1; i < MAX_NUM_AMOEBA; i++)
-    {
-      if (AmoebaCnt[i] == 0)
-      {
-       group_nr = i;
-       break;
+         /* player disappears */
+         DrawLevelField(player->jx, player->jy);
+       }
       }
     }
-  }
 
-  AmoebaNr[x][y] = group_nr;
-  AmoebaCnt[group_nr]++;
-  AmoebaCnt2[group_nr]++;
-}
+    PlaySound(SND_GAME_WINNING);
+  }
 
-void GameWon()
-{
-  int hi_pos;
-  boolean raise_level = FALSE;
+  if (game_over_delay_1 > 0)
+  {
+    game_over_delay_1--;
 
-  if (local_player->MovPos)
     return;
+  }
 
-  if (tape.auto_play)          /* tape might already be stopped here */
-    tape.auto_play_level_solved = TRUE;
+  if (time != time_final)
+  {
+    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);
 
-  local_player->LevelSolved = FALSE;
+    time  += time_count_steps * time_count_dir;
+    score += time_count_steps * level.score[SC_TIME_BONUS];
 
-  PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
+#if 1
+    local_player->LevelSolved_CountingTime = time;
+    local_player->LevelSolved_CountingScore = score;
 
-  if (TimeLeft)
-  {
-    if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
-                  SND_CTRL_PLAY_LOOP);
+    game_panel_controls[GAME_PANEL_TIME].value = time;
+    game_panel_controls[GAME_PANEL_SCORE].value = score;
 
-    while (TimeLeft > 0)
-    {
-      if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+    DisplayGameControlValues();
+#else
+    DrawGameValue_Time(time);
+    DrawGameValue_Score(score);
+#endif
 
-      if (TimeLeft > 100 && TimeLeft % 10 == 0)
-      {
-       TimeLeft -= 10;
-       RaiseScore(level.score[SC_TIME_BONUS] * 10);
-      }
-      else
-      {
-       TimeLeft--;
-       RaiseScore(level.score[SC_TIME_BONUS]);
-      }
+    if (time == time_final)
+      StopSound(SND_GAME_LEVELTIME_BONUS);
+    else if (setup.sound_loops)
+      PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
+    else
+      PlaySound(SND_GAME_LEVELTIME_BONUS);
 
-      DrawGameValue_Time(TimeLeft);
+    return;
+  }
 
-      BackToFront();
+  local_player->LevelSolved_PanelOff = TRUE;
 
-      if (!tape.playing)
-       Delay(10);
-    }
+  if (game_over_delay_2 > 0)
+  {
+    game_over_delay_2--;
 
-    if (!tape.playing && setup.sound_loops)
-      StopSound(SND_GAME_LEVELTIME_BONUS);
+    return;
   }
-  else if (level.time == 0)            /* level without time limit */
-  {
-    if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
-                  SND_CTRL_PLAY_LOOP);
 
-    while (TimePlayed < 999)
-    {
-      if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+#if 1
+  GameEnd();
+#endif
+}
 
-      if (TimePlayed < 900 && TimePlayed % 10 == 0)
-      {
-       TimePlayed += 10;
-       RaiseScore(level.score[SC_TIME_BONUS] * 10);
-      }
-      else
-      {
-       TimePlayed++;
-       RaiseScore(level.score[SC_TIME_BONUS]);
-      }
+void GameEnd()
+{
+  int hi_pos;
+  boolean raise_level = FALSE;
 
-      DrawGameValue_Time(TimePlayed);
+  local_player->LevelSolved_GameEnd = TRUE;
 
-      BackToFront();
+  CloseDoor(DOOR_CLOSE_1);
 
-      if (!tape.playing)
-       Delay(10);
-    }
+  if (local_player->LevelSolved_SaveTape)
+  {
+#if 0
+    TapeStop();
+#endif
 
-    if (!tape.playing && setup.sound_loops)
-      StopSound(SND_GAME_LEVELTIME_BONUS);
+#if 1
+    SaveTapeChecked(tape.level_nr);    /* ask to save tape */
+#else
+    SaveTape(tape.level_nr);           /* ask to save tape */
+#endif
   }
 
-  /* close exit door after last player */
-  if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
-      (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
-       Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+  if (level_editor_test_game)
   {
-    int element = Feld[ExitX][ExitY];
+    game_status = GAME_MODE_MAIN;
 
-    Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
-                         EL_SP_EXIT_CLOSING);
+#if 1
+    DrawAndFadeInMainMenu(REDRAW_FIELD);
+#else
+    DrawMainMenu();
+#endif
 
-    PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+    return;
   }
 
-  /* player disappears */
-  if (ExitX >= 0 && ExitY >= 0)
-    DrawLevelField(ExitX, ExitY);
-
-  BackToFront();
+  if (!local_player->LevelSolved_SaveScore)
+  {
+#if 1
+    FadeOut(REDRAW_FIELD);
+#endif
 
-  if (tape.playing)
-    return;
+    game_status = GAME_MODE_MAIN;
 
-  CloseDoor(DOOR_CLOSE_1);
+    DrawAndFadeInMainMenu(REDRAW_FIELD);
 
-  if (tape.recording)
-  {
-    TapeStop();
-    SaveTape(tape.level_nr);           /* Ask to save tape */
+    return;
   }
 
   if (level_nr == leveldir_current->handicap_level)
   {
     leveldir_current->handicap_level++;
+
     SaveLevelSetup_SeriesInfo();
   }
 
-  if (level_editor_test_game)
-    local_player->score = -1;  /* no highscore when playing from editor */
-  else if (level_nr < leveldir_current->last_level)
-    raise_level = TRUE;                /* advance to next level */
+  if (level_nr < leveldir_current->last_level)
+    raise_level = TRUE;                        /* advance to next level */
 
   if ((hi_pos = NewHiScore()) >= 0) 
   {
     game_status = GAME_MODE_SCORES;
+
     DrawHallOfFame(hi_pos);
+
     if (raise_level)
     {
       level_nr++;
@@ -2586,16 +5098,20 @@ void GameWon()
   }
   else
   {
+#if 1
+    FadeOut(REDRAW_FIELD);
+#endif
+
     game_status = GAME_MODE_MAIN;
+
     if (raise_level)
     {
       level_nr++;
       TapeErase();
     }
-    DrawMainMenu();
-  }
 
-  BackToFront();
+    DrawAndFadeInMainMenu(REDRAW_FIELD);
+  }
 }
 
 int NewHiScore()
@@ -2605,13 +5121,13 @@ int NewHiScore()
 
   LoadScore(level_nr);
 
-  if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
-      local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
+  if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
+      local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
     return -1;
 
   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
   {
-    if (local_player->score > highscore[k].Score)
+    if (local_player->score_final > highscore[k].Score)
     {
       /* player has made it to the hall of fame */
 
@@ -2621,7 +5137,7 @@ int NewHiScore()
 
 #ifdef ONE_PER_NAME
        for (l = k; l < MAX_SCORE_ENTRIES; l++)
-         if (!strcmp(setup.player_name, highscore[l].Name))
+         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;
@@ -2639,7 +5155,7 @@ int NewHiScore()
 #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; 
+      highscore[k].Score = local_player->score_final
       position = k;
       break;
     }
@@ -2658,10 +5174,9 @@ int NewHiScore()
   return position;
 }
 
-inline static int getElementMoveStepsize(int x, int y)
+inline static int getElementMoveStepsizeExt(int x, int y, int direction)
 {
   int element = Feld[x][y];
-  int direction = MovDir[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);
@@ -2671,24 +5186,21 @@ inline static int getElementMoveStepsize(int x, int y)
   /* special values for move stepsize for spring and things on conveyor belt */
   if (horiz_move)
   {
-#if 0
-    if (element == EL_SPRING)
-      step = sign * MOVE_STEPSIZE_NORMAL * 2;
-    else if (CAN_FALL(element) && !CAN_MOVE(element) &&
-            y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
-      step = sign * MOVE_STEPSIZE_NORMAL / 2;
-#else
     if (CAN_FALL(element) &&
        y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
       step = sign * MOVE_STEPSIZE_NORMAL / 2;
     else if (element == EL_SPRING)
       step = sign * MOVE_STEPSIZE_NORMAL * 2;
-#endif
   }
 
   return step;
 }
 
+inline static int getElementMoveStepsize(int x, int y)
+{
+  return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
+}
+
 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
 {
   if (player->GfxAction != action || player->GfxDir != dir)
@@ -2705,16 +5217,41 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
   }
 }
 
-static void ResetRandomAnimationValue(int x, int y)
+#if USE_GFX_RESET_GFX_ANIMATION
+static void ResetGfxFrame(int x, int y, boolean redraw)
 {
-  GfxRandom[x][y] = INIT_GFX_RANDOM();
+  int element = Feld[x][y];
+  int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+  int last_gfx_frame = GfxFrame[x][y];
+
+  if (graphic_info[graphic].anim_global_sync)
+    GfxFrame[x][y] = FrameCounter;
+  else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+    GfxFrame[x][y] = CustomValue[x][y];
+  else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+    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)
 {
-  GfxFrame[x][y] = 0;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MovDir[x][y];
+  GfxFrame[x][y] = 0;
+
+#if USE_GFX_RESET_GFX_ANIMATION
+  ResetGfxFrame(x, y, FALSE);
+#endif
+}
+
+static void ResetRandomAnimationValue(int x, int y)
+{
+  GfxRandom[x][y] = INIT_GFX_RANDOM();
 }
 
 void InitMovingField(int x, int y, int direction)
@@ -2724,18 +5261,56 @@ void InitMovingField(int x, int y, int direction)
   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
+  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);
 
-  if (!WasJustMoving[x][y] || direction != MovDir[x][y])
+  /* reset animation only for moving elements which change direction of moving
+     or which just started or stopped moving
+     (else CEs with property "can move" / "not moving" are reset each frame) */
+#if USE_GFX_RESET_ONLY_WHEN_MOVING
+#if 1
+  if (is_moving_before != is_moving_after ||
+      direction != MovDir[x][y])
+    ResetGfxAnimation(x, y);
+#else
+  if ((is_moving_before || is_moving_after) && !continues_moving)
+    ResetGfxAnimation(x, y);
+#endif
+#else
+  if (!continues_moving)
     ResetGfxAnimation(x, y);
+#endif
 
   MovDir[x][y] = direction;
   GfxDir[x][y] = direction;
+
+#if USE_GFX_RESET_ONLY_WHEN_MOVING
+  GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
+                    direction == MV_DOWN && CAN_FALL(element) ?
+                    ACTION_FALLING : ACTION_MOVING);
+#else
   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
                     ACTION_FALLING : ACTION_MOVING);
+#endif
 
   /* this is needed for CEs with property "can move" / "not moving" */
 
-  if (getElementMoveStepsize(x, y) != 0)       /* moving or being moved */
+  if (is_moving_after)
   {
     if (Feld[newx][newy] == EL_EMPTY)
       Feld[newx][newy] = EL_BLOCKED;
@@ -2756,8 +5331,8 @@ void InitMovingField(int x, int y, int direction)
 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
 {
   int direction = MovDir[x][y];
-  int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
-  int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+  int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
+  int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
 
   *goes_to_x = newx;
   *goes_to_y = newy;
@@ -2843,6 +5418,10 @@ static void RemoveField(int x, int y)
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MV_NONE;
+#if 0
+  /* !!! this would prevent the removed tile from being redrawn !!! */
+  GfxRedraw[x][y] = GFX_REDRAW_NONE;
+#endif
 }
 
 void RemoveMovingField(int x, int y)
@@ -2868,7 +5447,7 @@ void RemoveMovingField(int x, int y)
 
       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
 
-      DrawLevelField(oldx, oldy);
+      TEST_DrawLevelField(oldx, oldy);
 
       return;
     }
@@ -2882,8 +5461,10 @@ 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]);
 
@@ -2895,8 +5476,8 @@ void RemoveMovingField(int x, int y)
   if (next_element != EL_UNDEFINED)
     Feld[oldx][oldy] = next_element;
 
-  DrawLevelField(oldx, oldy);
-  DrawLevelField(newx, newy);
+  TEST_DrawLevelField(oldx, oldy);
+  TEST_DrawLevelField(newx, newy);
 }
 
 void DrawDynamite(int x, int y)
@@ -2944,38 +5525,154 @@ void CheckDynamite(int x, int y)
   Bang(x, y);
 }
 
-void DrawRelocatePlayer(struct PlayerInfo *player)
+static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
+{
+  boolean num_checked_players = 0;
+  int i;
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    if (stored_player[i].active)
+    {
+      int sx = stored_player[i].jx;
+      int sy = stored_player[i].jy;
+
+      if (num_checked_players == 0)
+      {
+       *sx1 = *sx2 = sx;
+       *sy1 = *sy2 = sy;
+      }
+      else
+      {
+       *sx1 = MIN(*sx1, sx);
+       *sy1 = MIN(*sy1, sy);
+       *sx2 = MAX(*sx2, sx);
+       *sy2 = MAX(*sy2, sy);
+      }
+
+      num_checked_players++;
+    }
+  }
+}
+
+static boolean checkIfAllPlayersFitToScreen_RND()
+{
+  int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
+
+  setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+  return (sx2 - sx1 < SCR_FIELDX &&
+         sy2 - sy1 < SCR_FIELDY);
+}
+
+static void setScreenCenteredToAllPlayers(int *sx, int *sy)
+{
+  int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
+
+  setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+  *sx = (sx1 + sx2) / 2;
+  *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)
 {
   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 jx = player->jx;
-  int jy = player->jy;
 
-  if (level.instant_relocation)
+  if (quick_relocation)
   {
-    int offset = (setup.scroll_delay ? 3 : 0);
-
-    if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
+    if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
     {
-      scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-                 local_player->jx - MIDPOSX);
+      if (!level.shifted_relocation || center_screen)
+      {
+       /* quick relocation (without scrolling), with centering of screen */
 
-      scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-                 local_player->jy - MIDPOSY);
+       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                   x > SBX_Right + MIDPOSX ? SBX_Right :
+                   x - MIDPOSX);
+
+       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
     {
-      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);
+#if 1
+      if (!level.shifted_relocation || center_screen)
+      {
+       /* quick relocation (without scrolling), with centering of screen */
+
+       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                   x > SBX_Right + MIDPOSX ? SBX_Right :
+                   x - MIDPOSX);
+
+       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 ((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 = game.scroll_delay_value;
+
+      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)
@@ -2984,29 +5681,72 @@ void DrawRelocatePlayer(struct PlayerInfo *player)
       /* don't scroll over playfield boundaries */
       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
        scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+#endif
+    }
+
+    RedrawPlayfield(TRUE, 0,0,0,0);
+  }
+  else
+  {
+#if 1
+    int scroll_xx, scroll_yy;
+
+    if (!level.shifted_relocation || center_screen)
+    {
+      /* visible relocation (with scrolling), with centering of screen */
+
+      scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                  x > SBX_Right + MIDPOSX ? SBX_Right :
+                  x - MIDPOSX);
+
+      scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                  y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                  y - MIDPOSY);
+    }
+    else
+    {
+      /* visible relocation (with scrolling), but do not center screen */
+
+      int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
+                            old_x - MIDPOSX);
+
+      int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                            old_y - MIDPOSY);
+
+      int offset_x = x + (scroll_x - center_scroll_x);
+      int offset_y = y + (scroll_y - center_scroll_y);
+
+      scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                  offset_x > SBX_Right + MIDPOSX ? SBX_Right :
+                  offset_x - MIDPOSX);
+
+      scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                  offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                  offset_y - MIDPOSY);
     }
 
-    RedrawPlayfield(TRUE, 0,0,0,0);
-  }
-  else
-  {
-    int scroll_xx = -999, scroll_yy = -999;
+#else
+
+    /* visible relocation (with scrolling), with centering of screen */
+
+    int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                    x > SBX_Right + MIDPOSX ? SBX_Right :
+                    x - MIDPOSX);
+
+    int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                    y - MIDPOSY);
+#endif
 
     ScrollScreen(NULL, SCROLL_GO_ON);  /* scroll last frame to full tile */
 
-    while (scroll_xx != scroll_x || scroll_yy != scroll_y)
+    while (scroll_x != scroll_xx || scroll_y != scroll_yy)
     {
       int dx = 0, dy = 0;
       int fx = FX, fy = FY;
 
-      scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-                  local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-                  local_player->jx - MIDPOSX);
-
-      scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-                  local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-                  local_player->jy - MIDPOSY);
-
       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
 
@@ -3032,7 +5772,7 @@ void DrawRelocatePlayer(struct PlayerInfo *player)
       Delay(wait_delay_value);
     }
 
-    DrawPlayer(player);
+    DrawAllPlayers();
     BackToFront();
     Delay(wait_delay_value);
   }
@@ -3107,14 +5847,24 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   Feld[jx][jy] = el_player;
   InitPlayerField(jx, jy, el_player, TRUE);
 
+  /* "InitPlayerField()" above sets Feld[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;
+    Feld[jx][jy] = element;    /* restore previously existing element */
+#if 0
+    /* !!! do not initialize already initialized element a second time !!! */
+    /* (this causes at least problems with "element creation" CE trigger for
+       already existing elements, and existing Sokoban fields counted twice) */
     InitField(jx, jy, FALSE);
+#endif
   }
 
-  if (player == local_player)  /* only visually relocate local player */
-    DrawRelocatePlayer(player);
+  /* only visually relocate centered player */
+  DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
+                    FALSE, level.instant_relocation);
 
   TestIfPlayerTouchesBadThing(jx, jy);
   TestIfPlayerTouchesCustomElement(jx, jy);
@@ -3125,6 +5875,21 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
 
   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
                                      player->index_bit, enter_side);
+
+#if 1
+  if (player->is_switching)
+  {
+    /* ensure that relocation while still switching an element does not cause
+       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;
+  }
+#endif
 }
 
 void Explode(int ex, int ey, int phase, int mode)
@@ -3200,11 +5965,7 @@ void Explode(int ex, int ey, int phase, int mode)
       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
 #endif
 
-#if 1
     last_phase = element_info[explosion_element].explosion_delay + 1;
-#else
-    last_phase = element_info[center_element].explosion_delay + 1;
-#endif
 
     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
     {
@@ -3277,33 +6038,14 @@ void Explode(int ex, int ey, int phase, int mode)
 
       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
       {
-#if 1
        int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
 
        Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
-#else
-       switch(StorePlayer[ex][ey])
-       {
-         case EL_PLAYER_2:
-           Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
-           break;
-         case EL_PLAYER_3:
-           Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
-           break;
-         case EL_PLAYER_4:
-           Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
-           break;
-         case EL_PLAYER_1:
-         default:
-           Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
-           break;
-       }
-#endif
 
        if (PLAYERINFO(ex, ey)->use_murphy)
          Store[x][y] = EL_EMPTY;
       }
-#if 1
+
       /* !!! 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))
@@ -3324,45 +6066,6 @@ void Explode(int ex, int ey, int phase, int mode)
 #endif
       else
        Store[x][y] = EL_EMPTY;
-#else
-      else if (center_element == EL_MOLE)
-       Store[x][y] = EL_EMERALD_RED;
-      else if (center_element == EL_PENGUIN)
-       Store[x][y] = EL_EMERALD_PURPLE;
-      else if (center_element == EL_BUG)
-       Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
-      else if (center_element == EL_BD_BUTTERFLY)
-       Store[x][y] = EL_BD_DIAMOND;
-      else if (center_element == EL_SP_ELECTRON)
-       Store[x][y] = EL_SP_INFOTRON;
-      else if (center_element == EL_AMOEBA_TO_DIAMOND)
-       Store[x][y] = level.amoeba_content;
-      else if (center_element == EL_YAMYAM)
-       Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
-      else if (IS_CUSTOM_ELEMENT(center_element) &&
-              element_info[center_element].content.e[xx][yy] != EL_EMPTY)
-       Store[x][y] = element_info[center_element].content.e[xx][yy];
-      else if (element == EL_WALL_EMERALD)
-       Store[x][y] = EL_EMERALD;
-      else if (element == EL_WALL_DIAMOND)
-       Store[x][y] = EL_DIAMOND;
-      else if (element == EL_WALL_BD_DIAMOND)
-       Store[x][y] = EL_BD_DIAMOND;
-      else if (element == EL_WALL_EMERALD_YELLOW)
-       Store[x][y] = EL_EMERALD_YELLOW;
-      else if (element == EL_WALL_EMERALD_RED)
-       Store[x][y] = EL_EMERALD_RED;
-      else if (element == EL_WALL_EMERALD_PURPLE)
-       Store[x][y] = EL_EMERALD_PURPLE;
-      else if (element == EL_WALL_PEARL)
-       Store[x][y] = EL_PEARL;
-      else if (element == EL_WALL_CRYSTAL)
-       Store[x][y] = EL_CRYSTAL;
-      else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
-       Store[x][y] = element_info[element].content.e[1][1];
-      else
-       Store[x][y] = EL_EMPTY;
-#endif
 
       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
          center_element == EL_AMOEBA_TO_DIAMOND)
@@ -3405,6 +6108,11 @@ void Explode(int ex, int ey, int phase, int mode)
 #endif
 #if 1
 
+#if 1
+  /* this can happen if the player leaves an explosion just in time */
+  if (GfxElement[x][y] == EL_UNDEFINED)
+    GfxElement[x][y] = EL_EMPTY;
+#else
   if (GfxElement[x][y] == EL_UNDEFINED)
   {
     printf("\n\n");
@@ -3414,6 +6122,8 @@ void Explode(int ex, int ey, int phase, int mode)
 
     GfxElement[x][y] = EL_EMPTY;
   }
+#endif
+
 #endif
 
   border_element = Store2[x][y];
@@ -3492,12 +6202,12 @@ void Explode(int ex, int ey, int phase, int mode)
 
     InitField_WithBug2(x, y, FALSE);
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     TestIfElementTouchesCustomElement(x, y);
 
     if (GFX_CRUMBLED(element))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
       StorePlayer[x][y] = 0;
@@ -3511,7 +6221,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
 
     if (phase == delay)
-      DrawLevelFieldCrumbledSand(x, y);
+      TEST_DrawLevelFieldCrumbled(x, y);
 
     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
@@ -3588,8 +6298,12 @@ void Bang(int x, int y)
   {
     struct PlayerInfo *player = PLAYERINFO(x, y);
 
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+    element = Feld[x][y] = player->initial_element;
+#else
     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
                            player->element_nr);
+#endif
 
     if (level.use_explosion_element[player->index_nr])
     {
@@ -3602,7 +6316,7 @@ void Bang(int x, int y)
     }
   }
 
-  switch(element)
+  switch (element)
   {
     case EL_BUG:
     case EL_SPACESHIP:
@@ -3626,6 +6340,14 @@ void Bang(int x, int y)
       explosion_type = EX_TYPE_DYNA;
       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;
+
     case EL_PENGUIN:
     case EL_LAMP:
     case EL_LAMP_ACTIVE:
@@ -3692,20 +6414,23 @@ static void InitBeltMovement()
     for (j = 0; j < NUM_BELT_PARTS; j++)
     {
       int element = belt_base_active_element[belt_nr] + j;
-      int graphic = el2img(element);
+      int graphic_1 = el2img(element);
+      int graphic_2 = el2panelimg(element);
 
       if (game.belt_dir[i] == MV_LEFT)
-       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
+      {
+       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
+       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
+      }
       else
-       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
+      {
+       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
+       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
+      }
     }
   }
 
-#if 1
   SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
   {
     int element = Feld[x][y];
 
@@ -3777,19 +6502,22 @@ static void ToggleBeltSwitch(int x, int y)
   for (i = 0; i < NUM_BELT_PARTS; i++)
   {
     int element = belt_base_active_element[belt_nr] + i;
-    int graphic = el2img(element);
+    int graphic_1 = el2img(element);
+    int graphic_2 = el2panelimg(element);
 
     if (belt_dir == MV_LEFT)
-      graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
+    {
+      graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
+      graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
+    }
     else
-      graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
+    {
+      graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
+      graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
+    }
   }
 
-#if 1
   SCAN_PLAYFIELD(xx, yy)
-#else
-  for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
   {
     int element = Feld[xx][yy];
 
@@ -3800,7 +6528,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;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
     else if (IS_BELT(element) && belt_dir != MV_NONE)
@@ -3812,7 +6540,7 @@ static void ToggleBeltSwitch(int x, int y)
        int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
 
        Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
@@ -3824,7 +6552,7 @@ static void ToggleBeltSwitch(int x, int y)
        int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
 
        Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
   }
@@ -3836,20 +6564,45 @@ static void ToggleSwitchgateSwitch(int x, int y)
 
   game.switchgate_pos = !game.switchgate_pos;
 
-#if 1
   SCAN_PLAYFIELD(xx, yy)
-#else
-  for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
   {
     int element = Feld[xx][yy];
 
+#if !USE_BOTH_SWITCHGATE_SWITCHES
     if (element == EL_SWITCHGATE_SWITCH_UP ||
        element == EL_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      DrawLevelField(xx, yy);
+      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;
+      TEST_DrawLevelField(xx, yy);
+    }
+    else if (element == EL_SWITCHGATE_SWITCH_DOWN)
+    {
+      Feld[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;
+      TEST_DrawLevelField(xx, yy);
+    }
+    else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+    {
+      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
+      TEST_DrawLevelField(xx, yy);
     }
+#endif
     else if (element == EL_SWITCHGATE_OPEN ||
             element == EL_SWITCHGATE_OPENING)
     {
@@ -3887,11 +6640,7 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
 {
   int x, y;
 
-#if 1
   SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
   {
     int element = Feld[x][y];
 
@@ -3899,25 +6648,25 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
        game.light_time_left > 0)
     {
       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
             game.light_time_left == 0)
     {
       Feld[x][y] = EL_LIGHT_SWITCH;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER &&
             game.light_time_left > 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.light_time_left == 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
             element == EL_INVISIBLE_WALL ||
@@ -3926,11 +6675,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
       if (game.light_time_left > 0)
        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -3939,11 +6688,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
       if (game.light_time_left == 0)
        Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
   }
 }
@@ -3952,11 +6701,7 @@ static void RedrawAllInvisibleElementsForLenses()
 {
   int x, y;
 
-#if 1
   SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
   {
     int element = Feld[x][y];
 
@@ -3964,13 +6709,13 @@ static void RedrawAllInvisibleElementsForLenses()
        game.lenses_time_left > 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.lenses_time_left == 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
             element == EL_INVISIBLE_WALL ||
@@ -3979,11 +6724,11 @@ static void RedrawAllInvisibleElementsForLenses()
       if (game.lenses_time_left > 0)
        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -3992,11 +6737,11 @@ static void RedrawAllInvisibleElementsForLenses()
       if (game.lenses_time_left == 0)
        Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
   }
 }
@@ -4005,11 +6750,7 @@ static void RedrawAllInvisibleElementsForMagnifier()
 {
   int x, y;
 
-#if 1
   SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
   {
     int element = Feld[x][y];
 
@@ -4017,13 +6758,13 @@ static void RedrawAllInvisibleElementsForMagnifier()
        game.magnify_time_left > 0)
     {
       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
             game.magnify_time_left == 0)
     {
       Feld[x][y] = EL_EMC_FAKE_GRASS;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY(element) &&
             game.magnify_time_left > 0)
@@ -4034,8 +6775,10 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    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);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY_ACTIVE(element) &&
             game.magnify_time_left == 0)
@@ -4046,8 +6789,10 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    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);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -4069,11 +6814,7 @@ static void ActivateTimegateSwitch(int x, int y)
 
   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
 
-#if 1
   SCAN_PLAYFIELD(xx, yy)
-#else
-  for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
   {
     int element = Feld[xx][yy];
 
@@ -4081,20 +6822,25 @@ static void ActivateTimegateSwitch(int x, int y)
        element == EL_TIMEGATE_CLOSING)
     {
       Feld[xx][yy] = EL_TIMEGATE_OPENING;
-      PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
+      PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
     }
 
     /*
     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
     {
       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     */
 
   }
 
+#if 1
+  Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
+               EL_DC_TIMEGATE_SWITCH_ACTIVE);
+#else
   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
+#endif
 }
 
 void Impact(int x, int y)
@@ -4125,7 +6871,17 @@ void Impact(int x, int y)
       RemoveMovingField(x, y + 1);
       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
       Feld[x][y + 2] = EL_ROCK;
-      DrawLevelField(x, y + 2);
+      TEST_DrawLevelField(x, y + 2);
+
+      object_hit = TRUE;
+    }
+
+    if (Feld[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;
+      TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
     }
@@ -4149,7 +6905,7 @@ void Impact(int x, int y)
       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
   {
     ResetGfxAnimation(x, y);
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
   }
 
   if (impact && CAN_EXPLODE_IMPACT(element))
@@ -4157,7 +6913,8 @@ void Impact(int x, int y)
     Bang(x, y);
     return;
   }
-  else if (impact && element == EL_PEARL)
+  else if (impact && element == EL_PEARL &&
+          smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
   {
     ResetGfxAnimation(x, y);
 
@@ -4190,30 +6947,33 @@ void Impact(int x, int y)
 
   if (object_hit)              /* check which object was hit */
   {
-    if (CAN_PASS_MAGIC_WALL(element) && 
-       (smashed == EL_MAGIC_WALL ||
-        smashed == EL_BD_MAGIC_WALL))
+    if ((CAN_PASS_MAGIC_WALL(element) && 
+        (smashed == EL_MAGIC_WALL ||
+         smashed == EL_BD_MAGIC_WALL)) ||
+       (CAN_PASS_DC_MAGIC_WALL(element) &&
+        smashed == EL_DC_MAGIC_WALL))
     {
       int xx, yy;
       int activated_magic_wall =
        (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
-        EL_BD_MAGIC_WALL_ACTIVE);
+        smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
+        EL_DC_MAGIC_WALL_ACTIVE);
 
       /* activate magic wall / mill */
-#if 1
       SCAN_PLAYFIELD(xx, yy)
-#else
-      for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
+      {
        if (Feld[xx][yy] == smashed)
          Feld[xx][yy] = activated_magic_wall;
+      }
 
       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
       game.magic_wall_active = TRUE;
 
       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
                            SND_MAGIC_WALL_ACTIVATING :
-                           SND_BD_MAGIC_WALL_ACTIVATING));
+                           smashed == EL_BD_MAGIC_WALL ?
+                           SND_BD_MAGIC_WALL_ACTIVATING :
+                           SND_DC_MAGIC_WALL_ACTIVATING));
     }
 
     if (IS_PLAYER(x, y + 1))
@@ -4293,7 +7053,9 @@ void Impact(int x, int y)
          ToggleBeltSwitch(x, y + 1);
        }
        else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
-                smashed == EL_SWITCHGATE_SWITCH_DOWN)
+                smashed == EL_SWITCHGATE_SWITCH_DOWN ||
+                smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
+                smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
        {
          ToggleSwitchgateSwitch(x, y + 1);
        }
@@ -4326,12 +7088,15 @@ void Impact(int x, int y)
   /* 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_BD_MAGIC_WALL_ACTIVE ||
+       Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
   {
     if (Feld[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)
       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
+    else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+      PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
 
     return;
   }
@@ -4606,16 +7371,35 @@ 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;
+       ResetGfxAnimation(move_x, move_y);
+       TEST_DrawLevelField(move_x, move_y);
+
+       MovDir[x][y] = back_dir;
+      }
+      else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_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;
   }
   else if (element == EL_ROBOT ||
           element == EL_SATELLITE ||
-          element == EL_PENGUIN)
+          element == EL_PENGUIN ||
+          element == EL_EMC_ANDROID)
   {
     int attr_x = -1, attr_y = -1;
 
@@ -4669,7 +7453,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)
+       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))
        {
          attr_x = ex;
          attr_y = ey;
@@ -4716,21 +7503,21 @@ inline static void TurnRoundExt(int x, int y)
          new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
        Moving2Blocked(x, y, &newx, &newy);
 
-       if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
+       if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
          return;
 
        MovDir[x][y] =
          new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
        Moving2Blocked(x, y, &newx, &newy);
 
-       if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
+       if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
          return;
 
        MovDir[x][y] = old_move_dir;
        return;
       }
     }
-    else       /* (element == EL_SATELLITE) */
+    else if (element == EL_SATELLITE)
     {
       int newx, newy;
 
@@ -4759,6 +7546,144 @@ inline static void TurnRoundExt(int x, int y)
        return;
       }
     }
+    else if (element == EL_EMC_ANDROID)
+    {
+      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)          */
+      };
+      static struct
+      {
+       int dx, dy;
+       int dir;
+      } check_xy[8] =
+      {
+        { -1, -1,      MV_LEFT  | MV_UP   },
+               {  0, -1,                  MV_UP   },
+       { +1, -1,       MV_RIGHT | MV_UP   },
+       { +1,  0,       MV_RIGHT           },
+       { +1, +1,       MV_RIGHT | MV_DOWN },
+       {  0, +1,                  MV_DOWN },
+       { -1, +1,       MV_LEFT  | MV_DOWN },
+       { -1,  0,       MV_LEFT            },
+      };
+      int start_pos, check_order;
+      boolean can_clone = FALSE;
+      int i;
+
+      /* check if there is any free field around current position */
+      for (i = 0; i < 8; i++)
+      {
+       int newx = x + check_xy[i].dx;
+       int newy = y + check_xy[i].dy;
+
+       if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+       {
+         can_clone = TRUE;
+
+         break;
+       }
+      }
+
+      if (can_clone)           /* randomly find an element to clone */
+      {
+       can_clone = FALSE;
+
+       start_pos = check_pos[RND(8)];
+       check_order = (RND(2) ? -1 : +1);
+
+       for (i = 0; i < 8; i++)
+       {
+         int pos_raw = start_pos + i * check_order;
+         int pos = (pos_raw + 8) % 8;
+         int newx = x + check_xy[pos].dx;
+         int newy = y + check_xy[pos].dy;
+
+         if (ANDROID_CAN_CLONE_FIELD(newx, newy))
+         {
+           element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
+           element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
+
+           Store[x][y] = Feld[newx][newy];
+
+           can_clone = TRUE;
+
+           break;
+         }
+       }
+      }
+
+      if (can_clone)           /* randomly find a direction to move */
+      {
+       can_clone = FALSE;
+
+       start_pos = check_pos[RND(8)];
+       check_order = (RND(2) ? -1 : +1);
+
+       for (i = 0; i < 8; i++)
+       {
+         int pos_raw = start_pos + i * check_order;
+         int pos = (pos_raw + 8) % 8;
+         int newx = x + check_xy[pos].dx;
+         int newy = y + check_xy[pos].dy;
+         int new_move_dir = check_xy[pos].dir;
+
+         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+         {
+           MovDir[x][y] = new_move_dir;
+           MovDelay[x][y] = level.android_clone_time * 8 + 1;
+
+           can_clone = TRUE;
+
+           break;
+         }
+       }
+      }
+
+      if (can_clone)           /* cloning and moving successful */
+       return;
+
+      /* 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 */
+       int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
+       int pos = (pos_raw + 8) % 8;
+       int newx = x + check_xy[pos].dx;
+       int newy = y + check_xy[pos].dy;
+       int new_move_dir = check_xy[pos].dir;
+
+       if (IS_PLAYER(newx, newy))
+         break;
+
+       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+       {
+         MovDir[x][y] = new_move_dir;
+         MovDelay[x][y] = level.android_move_time * 8 + 1;
+
+         break;
+       }
+      }
+    }
   }
   else if (move_pattern == MV_TURNING_LEFT ||
           move_pattern == MV_TURNING_RIGHT ||
@@ -5007,7 +7932,9 @@ static void TurnRound(int x, int y)
     GfxFrame[x][y] = 0;
 
   if (MovDelay[x][y])
-    GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
+    GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
+
+  ResetGfxFrame(x, y, FALSE);
 }
 
 static boolean JustBeingPushed(int x, int y)
@@ -5049,31 +7976,143 @@ void StartMoving(int x, int y)
       if (JustBeingPushed(x, y))
        return;
 
-    if (element == EL_QUICKSAND_FULL)
-    {
-      if (IS_FREE(x, y + 1))
-      {
-       InitMovingField(x, y, MV_DOWN);
-       started_moving = TRUE;
+    if (element == EL_QUICKSAND_FULL)
+    {
+      if (IS_FREE(x, y + 1))
+      {
+       InitMovingField(x, y, MV_DOWN);
+       started_moving = TRUE;
+
+       Feld[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;
+#else
+       Store[x][y] = EL_ROCK;
+#endif
+
+       PlayLevelSoundAction(x, y, ACTION_EMPTYING);
+      }
+      else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+      {
+       if (!MovDelay[x][y])
+       {
+         MovDelay[x][y] = TILEY + 1;
+
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
+       if (MovDelay[x][y])
+       {
+         DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_QUICKSAND_EMPTY;
+       Feld[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)
+      {
+       if (!MovDelay[x][y])
+       {
+         MovDelay[x][y] = TILEY + 1;
+
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
+       if (MovDelay[x][y])
+       {
+         DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_QUICKSAND_EMPTY;
+       Feld[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 (element == EL_QUICKSAND_FAST_FULL)
+    {
+      if (IS_FREE(x, y + 1))
+      {
+       InitMovingField(x, y, MV_DOWN);
+       started_moving = TRUE;
+
+       Feld[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;
+#else
+       Store[x][y] = EL_ROCK;
+#endif
+
+       PlayLevelSoundAction(x, y, ACTION_EMPTYING);
+      }
+      else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+      {
+       if (!MovDelay[x][y])
+       {
+         MovDelay[x][y] = TILEY + 1;
+
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
+       if (MovDelay[x][y])
+       {
+         DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
 
-       Feld[x][y] = EL_QUICKSAND_EMPTYING;
-       Store[x][y] = EL_ROCK;
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
 
-       PlayLevelSoundAction(x, y, ACTION_EMPTYING);
+       Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
+       Feld[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)
       {
        if (!MovDelay[x][y])
+       {
          MovDelay[x][y] = TILEY + 1;
 
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
        if (MovDelay[x][y])
        {
+         DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
          MovDelay[x][y]--;
          if (MovDelay[x][y])
            return;
        }
 
-       Feld[x][y] = EL_QUICKSAND_EMPTY;
+       Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
        Feld[x][y + 1] = EL_QUICKSAND_FULL;
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
@@ -5092,6 +8131,17 @@ void StartMoving(int x, int y)
 
       PlayLevelSoundAction(x, y, ACTION_FILLING);
     }
+    else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
+            Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+    {
+      InitMovingField(x, y, MV_DOWN);
+      started_moving = TRUE;
+
+      Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
+      Store[x][y] = element;
+
+      PlayLevelSoundAction(x, y, ACTION_FILLING);
+    }
     else if (element == EL_MAGIC_WALL_FULL)
     {
       if (IS_FREE(x, y + 1))
@@ -5105,7 +8155,7 @@ void StartMoving(int x, int y)
       else if (Feld[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])
        {
@@ -5128,12 +8178,12 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
 
        Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
-       Store[x][y] = EL_CHANGED2(Store[x][y]);
+       Store[x][y] = EL_CHANGED_BD(Store[x][y]);
       }
       else if (Feld[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])
        {
@@ -5144,20 +8194,52 @@ void StartMoving(int x, int y)
 
        Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
        Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
-       Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
+       Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
+       Store[x][y] = 0;
+      }
+    }
+    else if (element == EL_DC_MAGIC_WALL_FULL)
+    {
+      if (IS_FREE(x, y + 1))
+      {
+       InitMovingField(x, y, MV_DOWN);
+       started_moving = TRUE;
+
+       Feld[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)
+      {
+       if (!MovDelay[x][y])
+         MovDelay[x][y] = TILEY / 4 + 1;
+
+       if (MovDelay[x][y])
+       {
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
+       Feld[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))
+    else if ((CAN_PASS_MAGIC_WALL(element) &&
+             (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+              Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
+            (CAN_PASS_DC_MAGIC_WALL(element) &&
+             (Feld[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 :
-        EL_BD_MAGIC_WALL_FILLING);
+        Feld[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)
@@ -5169,9 +8251,14 @@ void StartMoving(int x, int y)
 
       Store[x][y] = EL_ACID;
     }
-    else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+    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))) ||
@@ -5191,6 +8278,7 @@ void StartMoving(int x, int y)
         simply not covered here... :-/ ) */
 
       CheckCollision[x][y] = 0;
+      CheckImpact[x][y] = 0;
 
       Impact(x, y);
     }
@@ -5396,7 +8484,7 @@ void StartMoving(int x, int y)
                               element == EL_SP_SNIKSNAK ||
                               element == EL_SP_ELECTRON ||
                               element == EL_MOLE))
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
       }
     }
 
@@ -5430,7 +8518,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
 
@@ -5470,7 +8558,7 @@ void StartMoving(int x, int y)
 
            if (IN_SCR_FIELD(sx, sy))
            {
-             DrawLevelFieldCrumbledSand(xx, yy);
+             TEST_DrawLevelFieldCrumbled(xx, yy);
              DrawGraphic(sx, sy, flame_graphic, frame);
            }
          }
@@ -5478,7 +8566,7 @@ void StartMoving(int x, int y)
          {
            if (Feld[xx][yy] == EL_FLAMES)
              Feld[xx][yy] = EL_EMPTY;
-           DrawLevelField(xx, yy);
+           TEST_DrawLevelField(xx, yy);
          }
        }
       }
@@ -5506,6 +8594,7 @@ void StartMoving(int x, int y)
 
     else if (CAN_MOVE_INTO_ACID(element) &&
             IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
+            !IS_MV_DIAGONAL(MovDir[x][y]) &&
             (MovDir[x][y] == MV_DOWN ||
              game.engine_version >= VERSION_IDENT(3,1,0,0)))
     {
@@ -5514,10 +8603,13 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
     {
-      if (Feld[newx][newy] == EL_EXIT_OPEN)
+      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)
       {
        RemoveField(x, y);
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
        if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
@@ -5526,14 +8618,14 @@ void StartMoving(int x, int y)
        local_player->friends_still_needed--;
        if (!local_player->friends_still_needed &&
            !local_player->GameOver && AllPlayersGone)
-         local_player->LevelSolved = local_player->GameOver = TRUE;
+         PlayerWins(local_player);
 
        return;
       }
       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
       {
-       if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
-         DrawLevelField(newx, newy);
+       if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
+         TEST_DrawLevelField(newx, newy);
        else
          GfxDir[x][y] = MovDir[x][y] = MV_NONE;
       }
@@ -5544,7 +8636,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -5558,7 +8650,7 @@ void StartMoving(int x, int y)
        else
        {
          Feld[newx][newy] = EL_EMPTY;
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        }
 
        PlayLevelSound(x, y, SND_PIG_DIGGING);
@@ -5568,7 +8660,98 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
+
+       return;
+      }
+    }
+    else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
+    {
+      if (Store[x][y] != EL_EMPTY)
+      {
+       boolean can_clone = FALSE;
+       int xx, yy;
+
+       /* 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])
+         {
+           can_clone = TRUE;
+
+           break;
+         }
+       }
+
+       /* 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;
+      }
+
+      if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+      {
+       if (IS_MV_DIAGONAL(MovDir[x][y]))
+       {
+         int diagonal_move_dir = MovDir[x][y];
+         int stored = Store[x][y];
+         int change_delay = 8;
+         int graphic;
+
+         /* android is moving diagonally */
+
+         CreateField(x, y, EL_DIAGONAL_SHRINKING);
+
+         Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
+         GfxElement[x][y] = EL_EMC_ANDROID;
+         GfxAction[x][y] = ACTION_SHRINKING;
+         GfxDir[x][y] = diagonal_move_dir;
+         ChangeDelay[x][y] = change_delay;
+
+         graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
+                                  GfxDir[x][y]);
+
+         DrawLevelGraphicAnimation(x, y, graphic);
+         PlayLevelSoundAction(x, y, ACTION_SHRINKING);
+
+         if (Feld[newx][newy] == EL_ACID)
+         {
+           SplashAcid(newx, newy);
+
+           return;
+         }
+
+         CreateField(newx, newy, EL_DIAGONAL_GROWING);
+
+         Store[newx][newy] = EL_EMC_ANDROID;
+         GfxElement[newx][newy] = EL_EMC_ANDROID;
+         GfxAction[newx][newy] = ACTION_GROWING;
+         GfxDir[newx][newy] = diagonal_move_dir;
+         ChangeDelay[newx][newy] = change_delay;
+
+         graphic = el_act_dir2img(GfxElement[newx][newy],
+                                  GfxAction[newx][newy], GfxDir[newx][newy]);
+
+         DrawLevelGraphicAnimation(newx, newy, graphic);
+         PlayLevelSoundAction(newx, newy, ACTION_GROWING);
+
+         return;
+       }
+       else
+       {
+         Feld[newx][newy] = EL_EMPTY;
+         TEST_DrawLevelField(newx, newy);
+
+         PlayLevelSoundAction(x, y, ACTION_DIGGING);
+       }
+      }
+      else if (!IS_FREE(newx, newy))
+      {
+#if 0
+       if (IS_PLAYER(x, y))
+         DrawPlayerField(x, y);
+       else
+         TEST_DrawLevelField(x, y);
+#endif
 
        return;
       }
@@ -5576,6 +8759,10 @@ void StartMoving(int x, int y)
     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))
@@ -5604,7 +8791,7 @@ void StartMoving(int x, int y)
        else
        {
          RemoveField(newx, newy);
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        }
 
        /* if digged element was about to explode, prevent the explosion */
@@ -5614,6 +8801,7 @@ void StartMoving(int x, int y)
       }
 
       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)))
@@ -5627,6 +8815,8 @@ void StartMoving(int x, int y)
        Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
                             new_element : move_leave_element);
       }
+#endif
+
 #endif
 
       if (move_pattern & MV_MAZE_RUNNER_STYLE)
@@ -5642,7 +8832,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -5669,7 +8859,7 @@ void StartMoving(int x, int y)
          if (IS_PLAYER(x, y))
            DrawPlayerField(x, y);
          else
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
 
          PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
 
@@ -5707,7 +8897,7 @@ void StartMoving(int x, int y)
       else
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
@@ -5738,7 +8928,7 @@ void StartMoving(int x, int y)
       else
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
@@ -5761,7 +8951,7 @@ void StartMoving(int x, int y)
 
        ResetGfxAnimation(x, y);
        GfxAction[x][y] = ACTION_DIGGING;
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
 
@@ -5770,7 +8960,7 @@ void StartMoving(int x, int y)
       else     /* element == EL_PACMAN */
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
        PlayLevelSound(x, y, SND_PACMAN_DIGGING);
       }
     }
@@ -5841,7 +9031,18 @@ void ContinueMoving(int x, int y)
 
   if (ABS(MovPos[x][y]) < TILEX)
   {
-    DrawLevelField(x, y);
+#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 */
   }
@@ -5860,7 +9061,7 @@ void ContinueMoving(int x, int y)
   {
     Feld[x][y] = EL_SAND;
 
-    DrawLevelFieldCrumbledSandNeighbours(x, y);
+    TEST_DrawLevelFieldCrumbledNeighbours(x, y);
   }
   else if (element == EL_QUICKSAND_FILLING)
   {
@@ -5872,6 +9073,16 @@ void ContinueMoving(int x, int y)
     Feld[x][y] = get_next_element(element);
     element = Feld[newx][newy] = Store[x][y];
   }
+  else if (element == EL_QUICKSAND_FAST_FILLING)
+  {
+    element = Feld[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];
+  }
   else if (element == EL_MAGIC_WALL_FILLING)
   {
     element = Feld[newx][newy] = get_next_element(element);
@@ -5904,6 +9115,24 @@ void ContinueMoving(int x, int y)
       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
 
+#if USE_NEW_CUSTOM_VALUE
+    InitField(newx, newy, FALSE);
+#endif
+  }
+  else if (element == EL_DC_MAGIC_WALL_FILLING)
+  {
+    element = Feld[newx][newy] = get_next_element(element);
+    if (!game.magic_wall_active)
+      element = Feld[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);
+    if (!game.magic_wall_active)
+      Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
+    element = Feld[newx][newy] = Store[x][y];
+
 #if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
 #endif
@@ -5931,29 +9160,17 @@ void ContinueMoving(int x, int y)
 
   MovDelay[newx][newy] = 0;
 
-#if 1
   if (CAN_CHANGE_OR_HAS_ACTION(element))
-#else
-  if (CAN_CHANGE(element))
-#endif
   {
     /* 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 0
-#if USE_NEW_CUSTOM_VALUE
-    CustomValue[newx][newy] = CustomValue[x][y];
-#endif
-#endif
   }
 
-#if 1
 #if USE_NEW_CUSTOM_VALUE
-    CustomValue[newx][newy] = CustomValue[x][y];
-#endif
+  CustomValue[newx][newy] = CustomValue[x][y];
 #endif
 
   ChangeDelay[x][y] = 0;
@@ -5974,16 +9191,28 @@ void ContinueMoving(int x, int y)
   Pushed[x][y] = Pushed[newx][newy] = FALSE;
 
   /* some elements can leave other elements behind after moving */
+#if 1
+  if (ei->move_leave_element != EL_EMPTY &&
+      (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
+      (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
+#else
   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
+#endif
   {
     int move_leave_element = ei->move_leave_element;
 
 #if 1
+#if 1
+    /* this makes it possible to leave the removed element again */
+    if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
+      move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
+#else
     /* this makes it possible to leave the removed element again */
     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
       move_leave_element = stored;
+#endif
 #else
     /* this makes it possible to leave the removed element again */
     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
@@ -5999,7 +9228,7 @@ void ContinueMoving(int x, int y)
     InitField(x, y, FALSE);
 
     if (GFX_CRUMBLED(Feld[x][y]))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
     if (ELEM_IS_PLAYER(move_leave_element))
       RelocatePlayer(x, y, move_leave_element);
@@ -6015,8 +9244,8 @@ void ContinueMoving(int x, int y)
        element_info[element].move_pattern == MV_WHEN_DROPPED)))
     GfxDir[x][y] = MovDir[newx][newy] = 0;
 
-  DrawLevelField(x, y);
-  DrawLevelField(newx, newy);
+  TEST_DrawLevelField(x, y);
+  TEST_DrawLevelField(newx, newy);
 
   Stop[newx][newy] = TRUE;     /* ignore this element until the next frame */
 
@@ -6036,13 +9265,18 @@ void ContinueMoving(int x, int y)
     int nextx = newx + dx, nexty = newy + dy;
     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
 
-    WasJustMoving[newx][newy] = 3;
+    WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
 
     if (CAN_FALL(element) && direction == MV_DOWN)
-      WasJustFalling[newx][newy] = 3;
+      WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
 
     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
-      CheckCollision[newx][newy] = 2;
+      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 */
@@ -6056,6 +9290,11 @@ void ContinueMoving(int x, int y)
   else if (element == EL_PENGUIN)
     TestIfFriendTouchesBadThing(newx, newy);
 
+  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) &&
@@ -6074,20 +9313,43 @@ void ContinueMoving(int x, int y)
                                        player->index_bit, push_side);
   }
 
+  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
+
   TestIfElementHitsCustomElement(newx, newy, direction);
   TestIfPlayerTouchesCustomElement(newx, newy);
   TestIfElementTouchesCustomElement(newx, newy);
 
-#if 1
   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
-                                     MV_DIR_OPPOSITE(direction));
-#endif
+                            MV_DIR_OPPOSITE(direction));
 }
 
 int AmoebeNachbarNr(int ax, int ay)
@@ -6156,11 +9418,7 @@ void AmoebenVereinigen(int ax, int ay)
       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
       AmoebaCnt2[old_group_nr] = 0;
 
-#if 1
       SCAN_PLAYFIELD(xx, yy)
-#else
-      for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
       {
        if (AmoebaNr[xx][yy] == old_group_nr)
          AmoebaNr[xx][yy] = new_group_nr;
@@ -6186,11 +9444,7 @@ void AmoebeUmwandeln(int ax, int ay)
     }
 #endif
 
-#if 1
     SCAN_PLAYFIELD(x, y)
-#else
-    for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
     {
       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
       {
@@ -6248,11 +9502,7 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
   }
 #endif
 
-#if 1
   SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
   {
     if (AmoebaNr[x][y] == group_nr &&
        (Feld[x][y] == EL_AMOEBA_DEAD ||
@@ -6262,7 +9512,7 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
       AmoebaNr[x][y] = 0;
       Feld[x][y] = new_element;
       InitField(x, y, FALSE);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
       done = TRUE;
     }
   }
@@ -6275,8 +9525,8 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
 
 void AmoebeWaechst(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 */
   {
@@ -6304,15 +9554,15 @@ void AmoebeWaechst(int x, int y)
     {
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
 
 void AmoebaDisappearing(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 */
   {
@@ -6336,7 +9586,7 @@ void AmoebaDisappearing(int x, int y)
     if (!MovDelay[x][y])
     {
       Feld[x][y] = EL_EMPTY;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* don't let mole enter this field in this cycle;
         (give priority to objects falling to this field from above) */
@@ -6351,6 +9601,7 @@ void AmoebeAbleger(int ax, int ay)
   int element = Feld[ax][ay];
   int graphic = el2img(element);
   int newax = ax, neway = ay;
+  boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -6359,10 +9610,10 @@ void AmoebeAbleger(int ax, int ay)
     { 0, +1 }
   };
 
-  if (!level.amoeba_speed)
+  if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
   {
     Feld[ax][ay] = EL_AMOEBA_DEAD;
-    DrawLevelField(ax, ay);
+    TEST_DrawLevelField(ax, ay);
     return;
   }
 
@@ -6379,7 +9630,7 @@ void AmoebeAbleger(int ax, int ay)
       return;
   }
 
-  if (element == EL_AMOEBA_WET)        /* object is an acid / amoeba drop */
+  if (can_drop)                        /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
   {
     int start = RND(4);
     int x = ax + xy[start][0];
@@ -6390,7 +9641,8 @@ void AmoebeAbleger(int ax, int ay)
 
     if (IS_FREE(x, y) ||
        CAN_GROW_INTO(Feld[x][y]) ||
-       Feld[x][y] == EL_QUICKSAND_EMPTY)
+       Feld[x][y] == EL_QUICKSAND_EMPTY ||
+       Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
     {
       newax = x;
       neway = y;
@@ -6415,7 +9667,8 @@ void AmoebeAbleger(int ax, int ay)
 
       if (IS_FREE(x, y) ||
          CAN_GROW_INTO(Feld[x][y]) ||
-         Feld[x][y] == EL_QUICKSAND_EMPTY)
+         Feld[x][y] == EL_QUICKSAND_EMPTY ||
+         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
       {
        newax = x;
        neway = y;
@@ -6430,7 +9683,7 @@ void AmoebeAbleger(int ax, int ay)
       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
       {
        Feld[ax][ay] = EL_AMOEBA_DEAD;
-       DrawLevelField(ax, ay);
+       TEST_DrawLevelField(ax, ay);
        AmoebaCnt[AmoebaNr[ax][ay]]--;
 
        if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
@@ -6473,13 +9726,13 @@ void AmoebeAbleger(int ax, int ay)
     }
   }
 
-  if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
+  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 */
     Store[newax][neway] = element;
   }
-  else if (neway == ay)
+  else if (neway == ay || element == EL_EMC_DRIPPER)
   {
     Feld[newax][neway] = EL_AMOEBA_DROP;       /* drop left/right of amoeba */
 
@@ -6494,15 +9747,12 @@ void AmoebeAbleger(int ax, int ay)
     return;
   }
 
-  DrawLevelField(newax, neway);
+  TEST_DrawLevelField(newax, neway);
 }
 
 void Life(int ax, int ay)
 {
   int x1, y1, x2, y2;
-#if 0
-  static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
-#endif
   int life_time = 40;
   int element = Feld[ax][ay];
   int graphic = el2img(element);
@@ -6555,7 +9805,7 @@ void Life(int ax, int ay)
       {
        Feld[xx][yy] = EL_EMPTY;
        if (!Stop[xx][yy])
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
@@ -6568,7 +9818,7 @@ void Life(int ax, int ay)
        Feld[xx][yy] = element;
        MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
        if (!Stop[xx][yy])
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
@@ -6593,7 +9843,11 @@ static void RunRobotWheel(int x, int y)
 static void StopRobotWheel(int x, int y)
 {
   if (ZX == x && ZY == y)
+  {
     ZX = ZY = -1;
+
+    game.robot_wheel_active = FALSE;
+  }
 }
 
 static void InitTimegateWheel(int x, int y)
@@ -6603,7 +9857,7 @@ static void InitTimegateWheel(int x, int y)
 
 static void RunTimegateWheel(int x, int y)
 {
-  PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
+  PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
 }
 
 static void InitMagicBallDelay(int x, int y)
@@ -6644,10 +9898,79 @@ static void ActivateMagicBall(int bx, int by)
     }
   }
 
-  game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
+  game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
+}
+
+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)
+  {
+    int element = Feld[x][y];
+    int graphic = el2img(element);
+
+    if (IS_ANIMATED(graphic))
+      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+    return;
+  }
+
+  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+    return;
+
+  Feld[x][y] = EL_EXIT_OPENING;
+
+  PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
+}
+
+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)
+  {
+    int element = Feld[x][y];
+    int graphic = el2img(element);
+
+    if (IS_ANIMATED(graphic))
+      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+    return;
+  }
+
+  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+    return;
+
+  Feld[x][y] = EL_EM_EXIT_OPENING;
+
+  PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
+}
+
+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)
+  {
+    int element = Feld[x][y];
+    int graphic = el2img(element);
+
+    if (IS_ANIMATED(graphic))
+      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+    return;
+  }
+
+  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+    return;
+
+  Feld[x][y] = EL_STEEL_EXIT_OPENING;
+
+  PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
 }
 
-void CheckExit(int x, int y)
+void CheckExitSteelEM(int x, int y)
 {
   if (local_player->gems_still_needed > 0 ||
       local_player->sokobanfields_still_needed > 0 ||
@@ -6665,9 +9988,9 @@ void CheckExit(int x, int y)
   if (AllPlayersGone)  /* do not re-open exit door closed after last player */
     return;
 
-  Feld[x][y] = EL_EXIT_OPENING;
+  Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
 
-  PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
+  PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
 }
 
 void CheckExitSP(int x, int y)
@@ -6695,11 +10018,7 @@ static void CloseAllOpenTimegates()
 {
   int x, y;
 
-#if 1
   SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
   {
     int element = Feld[x][y];
 
@@ -6712,7 +10031,7 @@ static void CloseAllOpenTimegates()
   }
 }
 
-void EdelsteinFunkeln(int x, int y)
+void DrawTwinkleOnField(int x, int y)
 {
   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
     return;
@@ -6721,15 +10040,12 @@ void EdelsteinFunkeln(int x, int y)
     return;
 
   if (MovDelay[x][y] == 0)     /* next animation frame */
-    MovDelay[x][y] = 11 * !SimpleRND(500);
+    MovDelay[x][y] = 11 * !GetSimpleRandom(500);
 
   if (MovDelay[x][y] != 0)     /* wait some time before next frame */
   {
     MovDelay[x][y]--;
 
-    if (setup.direct_draw && MovDelay[x][y])
-      SetDrawtoField(DRAW_BUFFERED);
-
     DrawLevelElementAnimation(x, y, Feld[x][y]);
 
     if (MovDelay[x][y] != 0)
@@ -6738,18 +10054,6 @@ void EdelsteinFunkeln(int x, int y)
                                           10 - MovDelay[x][y]);
 
       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
-
-      if (setup.direct_draw)
-      {
-       int dest_x, dest_y;
-
-       dest_x = FX + SCREENX(x) * TILEX;
-       dest_y = FY + SCREENY(y) * TILEY;
-
-       BlitBitmap(drawto_field, window,
-                  dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
-       SetDrawtoField(DRAW_DIRECT);
-      }
     }
   }
 }
@@ -6778,28 +10082,28 @@ 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]))
-         DrawLevelField(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]))
-         DrawLevelField(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]))
-         DrawLevelField(x, y - 1);
+         TEST_DrawLevelField(x, y - 1);
       }
       else
       {
        if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
-         DrawLevelField(x, y + 1);
+         TEST_DrawLevelField(x, y + 1);
       }
 
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -6863,7 +10167,8 @@ void MauerAbleger(int ax, int ay)
 
   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
       element == EL_EXPANDABLE_WALL_ANY ||
-      element == EL_EXPANDABLE_WALL)
+      element == EL_EXPANDABLE_WALL ||
+      element == EL_BD_EXPANDABLE_WALL)
   {
     if (links_frei)
     {
@@ -6889,7 +10194,7 @@ void MauerAbleger(int ax, int ay)
   }
 
   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
-    DrawLevelField(ax, ay);
+    TEST_DrawLevelField(ax, ay);
 
   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
     oben_massiv = TRUE;
@@ -6911,6 +10216,108 @@ void MauerAbleger(int ax, int ay)
     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
 }
 
+void MauerAblegerStahl(int ax, int ay)
+{
+  int element = Feld[ax][ay];
+  int graphic = el2img(element);
+  boolean oben_frei = FALSE, unten_frei = FALSE;
+  boolean links_frei = FALSE, rechts_frei = FALSE;
+  boolean oben_massiv = FALSE, unten_massiv = FALSE;
+  boolean links_massiv = FALSE, rechts_massiv = FALSE;
+  boolean new_wall = FALSE;
+
+  if (IS_ANIMATED(graphic))
+    DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
+
+  if (!MovDelay[ax][ay])       /* start building new wall */
+    MovDelay[ax][ay] = 6;
+
+  if (MovDelay[ax][ay])                /* wait some time before building new wall */
+  {
+    MovDelay[ax][ay]--;
+    if (MovDelay[ax][ay])
+      return;
+  }
+
+  if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
+    oben_frei = TRUE;
+  if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
+    unten_frei = TRUE;
+  if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
+    links_frei = TRUE;
+  if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
+    rechts_frei = TRUE;
+
+  if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+      element == EL_EXPANDABLE_STEELWALL_ANY)
+  {
+    if (oben_frei)
+    {
+      Feld[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)))
+       DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
+                   IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
+      new_wall = TRUE;
+    }
+    if (unten_frei)
+    {
+      Feld[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)))
+       DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
+                   IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
+      new_wall = TRUE;
+    }
+  }
+
+  if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+      element == EL_EXPANDABLE_STEELWALL_ANY)
+  {
+    if (links_frei)
+    {
+      Feld[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)))
+       DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
+                   IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
+      new_wall = TRUE;
+    }
+
+    if (rechts_frei)
+    {
+      Feld[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)))
+       DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
+                   IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
+      new_wall = TRUE;
+    }
+  }
+
+  if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
+    oben_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
+    unten_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
+    links_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[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;
+
+  if (new_wall)
+    PlayLevelSoundAction(ax, ay, ACTION_GROWING);
+}
+
 void CheckForDragon(int x, int y)
 {
   int i, j;
@@ -6951,7 +10358,7 @@ void CheckForDragon(int x, int y)
        if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
        {
          Feld[xx][yy] = EL_EMPTY;
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        }
        else
          break;
@@ -6987,9 +10394,10 @@ static void WarnBuggyBase(int x, int y)
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
   {
-    int xx = x + xy[i][0], yy = y + xy[i][1];
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
 
-    if (IS_PLAYER(xx, yy))
+    if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
     {
       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
 
@@ -7014,7 +10422,7 @@ static void ChangeActiveTrap(int x, int y)
 
   /* if new animation frame was drawn, correct crumbled sand border */
   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
-    DrawLevelFieldCrumbledSand(x, y);
+    TEST_DrawLevelFieldCrumbled(x, y);
 }
 
 static int getSpecialActionElement(int element, int number, int base_element)
@@ -7044,9 +10452,11 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 {
   struct ElementInfo *ei = &element_info[element];
   struct ElementChangeInfo *change = &ei->change_page[page];
+  int target_element = change->target_element;
   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)
@@ -7058,11 +10468,21 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     (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);
+
+#if 0
+  if (action_arg_element_raw == EL_GROUP_START)
+    printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
+#endif
 
   int action_arg_direction =
     (action_arg >= CA_ARG_DIRECTION_LEFT &&
@@ -7074,29 +10494,29 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      MV_NONE);
 
   int action_arg_number_min =
-    (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
+    (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
      CA_ARG_MIN);
 
   int action_arg_number_max =
-    (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
+    (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
      action_type == CA_SET_LEVEL_GEMS ? 999 :
      action_type == CA_SET_LEVEL_TIME ? 9999 :
      action_type == CA_SET_LEVEL_SCORE ? 99999 :
-     action_type == CA_SET_CE_SCORE ? 9999 :
      action_type == CA_SET_CE_VALUE ? 9999 :
+     action_type == CA_SET_CE_SCORE ? 9999 :
      CA_ARG_MAX);
 
   int action_arg_number_reset =
-    (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
+    (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
      action_type == CA_SET_LEVEL_TIME ? level.time :
      action_type == CA_SET_LEVEL_SCORE ? 0 :
-     action_type == CA_SET_CE_SCORE ? 0 :
-#if 1
-     action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
+#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);
 
   int action_arg_number =
@@ -7107,26 +10527,33 @@ 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 :
-     action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
 #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_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
-     action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
+     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_TIME ? TimeLeft :
      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
-     action_type == CA_SET_CE_SCORE ? ei->collect_score :
      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
+     action_type == CA_SET_CE_SCORE ? ei->collect_score :
      0);
 
   int action_arg_number_new =
@@ -7134,21 +10561,28 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                            action_mode, action_arg_number,
                            action_arg_number_min, action_arg_number_max);
 
+#if 1
+  int trigger_player_bits =
+    (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
+     change->actual_trigger_player_bits : change->trigger_player);
+#else
   int trigger_player_bits =
     (change->actual_trigger_player >= EL_PLAYER_1 &&
      change->actual_trigger_player <= EL_PLAYER_4 ?
      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
      PLAYER_BITS_ANY);
+#endif
 
   int action_arg_player_bits =
     (action_arg >= CA_ARG_PLAYER_1 &&
      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  -------------------------------------------- */
 
-  switch(action_type)
+  switch (action_type)
   {
     case CA_NO_ACTION:
     {
@@ -7181,7 +10615,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       {
        TimeLeft = action_arg_number_new;
 
+#if 1
+       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
@@ -7195,7 +10635,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->score = action_arg_number_new;
 
+#if 1
+      game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Score(local_player->score);
+#endif
 
       break;
     }
@@ -7204,11 +10650,18 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->gems_still_needed = action_arg_number_new;
 
+#if 1
+      game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Emeralds(local_player->gems_still_needed);
+#endif
 
       break;
     }
 
+#if !USE_PLAYER_GRAVITY
     case CA_SET_LEVEL_GRAVITY:
     {
       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
@@ -7217,6 +10670,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                      game.gravity);
       break;
     }
+#endif
 
     case CA_SET_LEVEL_WIND:
     {
@@ -7225,6 +10679,33 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_LEVEL_RANDOM_SEED:
+    {
+#if 1
+      /* ensure that setting a new random seed while playing is predictable */
+      InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
+#else
+      InitRND(action_arg_number_new);
+#endif
+
+#if 0
+      printf("::: %d -> %d\n", action_arg_number_new, RND(10));
+#endif
+
+#if 0
+      {
+       int i;
+
+       printf("::: ");
+       for (i = 0; i < 9; i++)
+         printf("%d, ", RND(2));
+       printf("\n");
+      }
+#endif
+
+      break;
+    }
+
     /* ---------- player actions  ------------------------------------------ */
 
     case CA_MOVE_PLAYER:
@@ -7241,7 +10722,7 @@ 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))
-         stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
+         PlayerWins(&stored_player[i]);
 
       break;
     }
@@ -7269,9 +10750,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
          {
            stored_player[i].key[KEY_NR(element)] = key_state;
 
-           DrawGameValue_Keys(stored_player[i].key);
-
-           redraw_mask |= REDRAW_DOOR_1;
+           DrawGameDoorValues();
          }
        }
       }
@@ -7281,19 +10760,32 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_PLAYER_SPEED:
     {
+#if 0
+      printf("::: trigger_player_bits == %d\n", trigger_player_bits);
+#endif
+
       for (i = 0; i < MAX_PLAYERS; i++)
       {
        if (trigger_player_bits & (1 << i))
        {
          int move_stepsize = TILEX / stored_player[i].move_delay_value;
 
-         if (action_arg == CA_ARG_SPEED_SLOWER ||
-             action_arg == CA_ARG_SPEED_FASTER)
+         if (action_arg == CA_ARG_SPEED_FASTER &&
+             stored_player[i].cannot_move)
+         {
+           action_arg_number = STEPSIZE_VERY_SLOW;
+         }
+         else if (action_arg == CA_ARG_SPEED_SLOWER ||
+                  action_arg == CA_ARG_SPEED_FASTER)
          {
            action_arg_number = 2;
            action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
                           CA_MODE_MULTIPLY);
          }
+         else if (action_arg == CA_ARG_NUMBER_RESET)
+         {
+           action_arg_number = level.initial_player_stepsize[i];
+         }
 
          move_stepsize =
            getModifiedActionNumber(move_stepsize,
@@ -7302,14 +10794,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                                    action_arg_number_min,
                                    action_arg_number_max);
 
-         /* make sure that value is power of 2 */
-         move_stepsize = (1 << log_2(move_stepsize));
-
-         /* do no immediately change -- the player might just be moving */
-         stored_player[i].move_delay_value_next = TILEX / move_stepsize;
-
-         stored_player[i].cannot_move =
-           (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
+         SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
        }
       }
 
@@ -7342,6 +10827,25 @@ 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++)
+      {
+       if (trigger_player_bits & (1 << i))
+       {
+         stored_player[i].gravity =
+           (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
+            action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
+            action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
+            stored_player[i].gravity);
+       }
+      }
+
+      break;
+    }
+#endif
+
     case CA_SET_PLAYER_ARTWORK:
     {
       for (i = 0; i < MAX_PLAYERS; i++)
@@ -7355,6 +10859,11 @@ 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);
@@ -7372,36 +10881,200 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
-    /* ---------- CE actions  ---------------------------------------------- */
-
-    case CA_SET_CE_SCORE:
+    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;
+
+      if (CustomValue[x][y] != last_ce_value)
+      {
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
+       CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
+
+       if (CustomValue[x][y] == 0)
+       {
+         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;
+
+      if (ei->collect_score != last_ce_score)
+      {
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
+       CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
+
+       if (ei->collect_score == 0)
+       {
+         int xx, yy;
+
+         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
+         CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
+
+         /*
+           This is a very special case that seems to be a mixture between
+           CheckElementChange() and CheckTriggeredElementChange(): while
+           the first one only affects single elements that are triggered
+           directly, the second one affects multiple elements in the playfield
+           that are triggered indirectly by another element. This is a third
+           case: Changing the CE score always affects multiple identical CEs,
+           so every affected CE must be checked, not only the single CE for
+           which the CE score was changed in the first place (as every instance
+           of that CE shares the same CE score, and therefore also can change)!
+         */
+         SCAN_PLAYFIELD(xx, yy)
+         {
+           if (Feld[xx][yy] == element)
+             CheckElementChange(xx, yy, element, EL_UNDEFINED,
+                                CE_SCORE_GETS_ZERO);
+         }
+       }
+      }
+#endif
+
+      break;
+    }
+
+    case CA_SET_CE_ARTWORK:
     {
-      ei->collect_score = action_arg_number_new;
-
-      break;
-    }
+      int artwork_element = action_arg_element;
+      boolean reset_frame = FALSE;
+      int xx, yy;
 
-    case CA_SET_CE_VALUE:
-    {
-#if USE_NEW_CUSTOM_VALUE
-      int last_custom_value = CustomValue[x][y];
+      if (action_arg == CA_ARG_ELEMENT_RESET)
+       artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
+                          element);
 
-      CustomValue[x][y] = action_arg_number_new;
+      if (ei->gfx_element != artwork_element)
+       reset_frame = TRUE;
 
-#if 0
-      printf("::: Count == %d\n", CustomValue[x][y]);
-#endif
+      ei->gfx_element = artwork_element;
 
-      if (CustomValue[x][y] == 0 && last_custom_value > 0)
+      SCAN_PLAYFIELD(xx, yy)
       {
-#if 0
-       printf("::: CE_VALUE_GETS_ZERO\n");
-#endif
+       if (Feld[xx][yy] == element)
+       {
+         if (reset_frame)
+         {
+           ResetGfxAnimation(xx, yy);
+           ResetRandomAnimationValue(xx, yy);
+         }
 
-       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
-       CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+         TEST_DrawLevelField(xx, yy);
+       }
       }
-#endif
 
       break;
     }
@@ -7422,54 +11095,98 @@ 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 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 add_player = (ELEM_IS_PLAYER(element) &&
-                       IS_WALKABLE(Feld[x][y]));
+  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));
 
-  /* check if element under player changes from accessible to unaccessible
+#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(Feld[x][y]) && !IS_ACCESSIBLE(element))
+      IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
   {
     Bang(x, y);
 
     return;
   }
+#endif
 
-  if (!add_player)
+  if (!add_player_onto_element)
   {
     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
       RemoveMovingField(x, y);
     else
       RemoveField(x, y);
 
-    Feld[x][y] = element;
+    Feld[x][y] = new_element;
 
+#if !USE_GFX_RESET_GFX_ANIMATION
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
+#endif
 
-    if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+    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[Feld[x][y]].use_last_ce_value)
+    if (element_info[new_element].use_last_ce_value)
       CustomValue[x][y] = last_ce_value;
 #endif
 
     InitField_WithBug1(x, y, FALSE);
 
-    DrawLevelField(x, y);
+    new_element = Feld[x][y];  /* element may have changed */
 
-    if (GFX_CRUMBLED(Feld[x][y]))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+#if USE_GFX_RESET_GFX_ANIMATION
+    ResetGfxAnimation(x, y);
+    ResetRandomAnimationValue(x, y);
+#endif
+
+    TEST_DrawLevelField(x, y);
+
+    if (GFX_CRUMBLED(new_element))
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+  }
+
+#if 1
+  /* check if element under the player changes from accessible to unaccessible
+     (needed for special case of dropping element which then changes) */
+  /* (must be checked after creating new element for walkable group elements) */
+#if USE_FIX_KILLED_BY_NON_WALKABLE
+  if (IS_PLAYER(x, y) && !player_explosion_protected &&
+      IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+  {
+    Bang(x, y);
+
+    return;
+  }
+#else
+  if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
+      IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+  {
+    Bang(x, y);
+
+    return;
   }
+#endif
+#endif
 
   /* "ChangeCount" not set yet to allow "entered by player" change one time */
-  if (ELEM_IS_PLAYER(element))
-    RelocatePlayer(x, y, element);
+  if (new_element_is_player)
+    RelocatePlayer(x, y, new_element);
 
   if (is_change)
     ChangeCount[x][y]++;       /* count number of changes in the same frame */
@@ -7486,12 +11203,30 @@ static void CreateField(int x, int y, int element)
 
 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];
+
+    /* 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);
 }
 
 static boolean ChangeElement(int x, int y, int element, int page)
 {
-  struct ElementChangeInfo *change = &element_info[element].change_page[page];
+  struct ElementInfo *ei = &element_info[element];
+  struct ElementChangeInfo *change = &ei->change_page[page];
+  int ce_value = CustomValue[x][y];
+  int ce_score = ei->collect_score;
   int target_element;
   int old_element = Feld[x][y];
 
@@ -7503,9 +11238,11 @@ static boolean ChangeElement(int x, int y, int element, int page)
   {
     /* 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 = 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 */
@@ -7610,7 +11347,8 @@ static boolean ChangeElement(int x, int y, int element, int page)
          ChangeEvent[ex][ey] = ChangeEvent[x][y];
 
          content_element = change->target_content.e[xx][yy];
-         target_element = GET_TARGET_ELEMENT(content_element, change);
+         target_element = GET_TARGET_ELEMENT(element, content_element, change,
+                                             ce_value, ce_score);
 
          CreateElementFromChange(ex, ey, target_element);
 
@@ -7631,7 +11369,16 @@ static boolean ChangeElement(int x, int y, int element, int page)
   }
   else
   {
-    target_element = GET_TARGET_ELEMENT(change->target_element, change);
+    target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
+                                       ce_value, ce_score);
+
+    if (element == EL_DIAGONAL_GROWING ||
+       element == EL_DIAGONAL_SHRINKING)
+    {
+      target_element = Store[x][y];
+
+      Store[x][y] = EL_EMPTY;
+    }
 
     CreateElementFromChange(x, y, target_element);
 
@@ -7652,6 +11399,7 @@ 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) &&
@@ -7682,8 +11430,19 @@ static void HandleElementChange(int x, int y, int page)
 
     if (change->can_change)
     {
-      ResetGfxAnimation(x, y);
-      ResetRandomAnimationValue(x, y);
+#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
+      {
+       ResetGfxAnimation(x, y);
+       ResetRandomAnimationValue(x, y);
+      }
+#endif
 
       if (change->pre_change_function)
        change->pre_change_function(x, y);
@@ -7723,6 +11482,15 @@ static void HandleElementChange(int x, int y, int page)
       return;
     }
 
+#if 1
+    /* special case: set new level random seed before changing element */
+    if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
+      handle_action_before_change = TRUE;
+
+    if (change->has_action && handle_action_before_change)
+      ExecuteCustomElementAction(x, y, element, page);
+#endif
+
     if (change->can_change)
     {
       if (ChangeElement(x, y, element, page))
@@ -7732,7 +11500,7 @@ 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);
   }
 }
@@ -7832,6 +11600,14 @@ 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++)
   {
     int element = EL_CUSTOM_START + i;
@@ -7854,24 +11630,42 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
       {
        change->actual_trigger_element = trigger_element;
-       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+       change->actual_trigger_player_bits = trigger_player;
        change->actual_trigger_side = trigger_side;
        change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
+       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
+
+#if 0
+       printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
+              element, EL_NAME(element), p);
+#endif
 
        if ((change->can_change && !change_done) || change->has_action)
        {
          int x, y;
 
-#if 1
          SCAN_PLAYFIELD(x, y)
-#else
-         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
          {
            if (Feld[x][y] == element)
            {
              if (change->can_change && !change_done)
              {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+               /* if element already changed in this frame, not only prevent
+                  another element change (checked in ChangeElement()), but
+                  also prevent additional element actions for this element */
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+#endif
+
+#if 0
+               printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
+                      element, EL_NAME(element), p);
+#endif
+
                ChangeDelay[x][y] = 1;
                ChangeEvent[x][y] = trigger_event;
 
@@ -7880,6 +11674,22 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
 #if USE_NEW_DELAYED_ACTION
              else if (change->has_action)
              {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+               /* if element already changed in this frame, not only prevent
+                  another element change (checked in ChangeElement()), but
+                  also prevent additional element actions for this element */
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+#endif
+
+
+#if 0
+               printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
+                      element, EL_NAME(element), p);
+#endif
+
                ExecuteCustomElementAction(x, y, element, p);
                PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
              }
@@ -7897,12 +11707,20 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
          {
            change_done = TRUE;
            change_done_any = TRUE;
+
+#if 0
+           printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
+                  element, EL_NAME(element), p);
+#endif
+
          }
        }
       }
     }
   }
 
+  RECURSION_LOOP_DETECTION_END();
+
   return change_done_any;
 }
 
@@ -7926,17 +11744,49 @@ static boolean CheckElementChangeExt(int x, int y,
     element = Feld[x][y];
   }
 
-  if (Feld[x][y] != element)   /* check if element has already changed */
+#if 0
+  /* check if element has already changed */
+  if (Feld[x][y] != element)
+    return FALSE;
+#else
+  /* check if element has already changed or is about to change after moving */
+  if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
+       Feld[x][y] != element) ||
+
+      (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);
+
+#if 0
+  printf("::: X: trigger_player_bits == %d\n", trigger_player);
+#endif
 
   for (p = 0; p < element_info[element].num_change_pages; p++)
   {
     struct ElementChangeInfo *change = &element_info[element].change_page[p];
 
+    /* check trigger element for all events where the element that is checked
+       for changing interacts with a directly adjacent element -- this is
+       different to element changes that affect other elements to change on the
+       whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
     boolean check_trigger_element =
       (trigger_event == CE_TOUCHING_X ||
        trigger_event == CE_HITTING_X ||
-       trigger_event == CE_HIT_BY_X);
+       trigger_event == CE_HIT_BY_X ||
+#if 1
+       /* this one was forgotten until 3.2.3 */
+       trigger_event == CE_DIGGING_X);
+#endif
 
     if (change->can_change_or_has_action &&
        change->has_event[trigger_event] &&
@@ -7946,9 +11796,11 @@ static boolean CheckElementChangeExt(int x, int y,
         IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
     {
       change->actual_trigger_element = trigger_element;
-      change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+      change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+      change->actual_trigger_player_bits = trigger_player;
       change->actual_trigger_side = trigger_side;
       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 */
       if (check_trigger_element)
@@ -7971,6 +11823,7 @@ static boolean CheckElementChangeExt(int x, int y,
        int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
 
        change->actual_trigger_ce_value = CustomValue[xx][yy];
+       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
       }
 
       if (change->can_change && !change_done)
@@ -7998,6 +11851,8 @@ static boolean CheckElementChangeExt(int x, int y,
     }
   }
 
+  RECURSION_LOOP_DETECTION_END();
+
   return change_done;
 }
 
@@ -8039,6 +11894,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
   boolean last_waiting = player->is_waiting;
   int move_dir = player->MovDir;
 
+  player->dir_waiting = move_dir;
   player->last_action_waiting = player->action_waiting;
 
   if (is_waiting)
@@ -8050,13 +11906,13 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
       player->frame_counter_bored =
        FrameCounter +
        game.player_boring_delay_fixed +
-       SimpleRND(game.player_boring_delay_random);
+       GetSimpleRandom(game.player_boring_delay_random);
       player->frame_counter_sleeping =
        FrameCounter +
        game.player_sleeping_delay_fixed +
-       SimpleRND(game.player_sleeping_delay_random);
+       GetSimpleRandom(game.player_sleeping_delay_random);
 
-      InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
+      InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
     }
 
     if (game.player_sleeping_delay_fixed +
@@ -8074,6 +11930,24 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
                              player->is_bored ? ACTION_BORING :
                              ACTION_WAITING);
 
+    if (player->is_sleeping && player->use_murphy)
+    {
+      /* 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 &&
+          !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 &&
+               !IS_MOVING(player->jx + 1, player->jy)))
+       move_dir = MV_RIGHT;
+      else
+       player->is_sleeping = FALSE;
+
+      player->dir_waiting = move_dir;
+    }
+
     if (player->is_sleeping)
     {
       if (player->num_special_action_sleeping > 0)
@@ -8092,10 +11966,10 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
 
          player->anim_delay_counter =
            graphic_info[special_graphic].anim_delay_fixed +
-           SimpleRND(graphic_info[special_graphic].anim_delay_random);
+           GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
          player->post_delay_counter =
            graphic_info[special_graphic].post_delay_fixed +
-           SimpleRND(graphic_info[special_graphic].post_delay_random);
+           GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
 
          player->special_action_sleeping = special_action;
        }
@@ -8118,16 +11992,16 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
        if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
        {
          int special_action =
-           ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
+           ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
          int special_graphic =
            el_act_dir2img(player->artwork_element, special_action, move_dir);
 
          player->anim_delay_counter =
            graphic_info[special_graphic].anim_delay_fixed +
-           SimpleRND(graphic_info[special_graphic].anim_delay_random);
+           GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
          player->post_delay_counter =
            graphic_info[special_graphic].post_delay_fixed +
-           SimpleRND(graphic_info[special_graphic].post_delay_random);
+           GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
 
          player->special_action_bored = special_action;
        }
@@ -8156,6 +12030,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
     player->anim_delay_counter = 0;
     player->post_delay_counter = 0;
 
+    player->dir_waiting = player->MovDir;
     player->action_waiting = ACTION_DEFAULT;
 
     player->special_action_bored = ACTION_DEFAULT;
@@ -8163,17 +12038,30 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
   }
 }
 
+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)
+    {
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+      SnapField(player, 0, 0);                 /* stop snapping */
+    }
+  }
+}
+
 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;
   int down     = player_action & JOY_DOWN;
   int button1  = player_action & JOY_BUTTON_1;
   int button2  = player_action & JOY_BUTTON_2;
-  int dx       = (left ? -1    : right ? 1     : 0);
-  int dy       = (up   ? -1    : down  ? 1     : 0);
+  int dx       = (left ? -1 : right ? 1 : 0);
+  int dy       = (up   ? -1 : down  ? 1 : 0);
 
   if (!player->active || tape.pausing)
     return 0;
@@ -8181,23 +12069,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);
 
@@ -8218,11 +12099,132 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
       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()
+{
+  int i;
+
+  /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
+  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;
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    if (game_sp.LevelSolved &&
+       !game_sp.GameOver)                              /* game won */
+    {
+      PlayerWins(local_player);
+
+      game_sp.GameOver = TRUE;
+
+      AllPlayersGone = TRUE;
+    }
+
+    if (game_sp.GameOver)                              /* game lost */
+      AllPlayersGone = TRUE;
+  }
+
+  if (TimeFrames >= FRAMES_PER_SECOND)
+  {
+    TimeFrames = 0;
+    TapeTime++;
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
+
+      if (SHIELD_ON(player))
+      {
+       player->shield_normal_time_left--;
+
+       if (player->shield_deadly_time_left > 0)
+         player->shield_deadly_time_left--;
+      }
+    }
+
+    if (!local_player->LevelSolved && !level.use_step_counter)
+    {
+      TimePlayed++;
+
+      if (TimeLeft > 0)
+      {
+       TimeLeft--;
+
+       if (TimeLeft <= 10 && setup.time_limit)
+         PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
+
+#if 1
+       /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
+          is reset from other values in UpdateGameDoorValues() -- FIX THIS */
+
+       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+       /* (already called by UpdateAndDisplayGameControlValues() below) */
+       // DisplayGameControlValues();
+#else
+       DrawGameValue_Time(TimeLeft);
+#endif
+
+       if (!TimeLeft && setup.time_limit)
+       {
+         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+           level.native_em_level->lev->killed_out_of_time = TRUE;
+         else
+           for (i = 0; i < MAX_PLAYERS; i++)
+             KillPlayer(&stored_player[i]);
+       }
+      }
+#if 1
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
+      {
+       game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
+
+       /* (already called by UpdateAndDisplayGameControlValues() below) */
+       // DisplayGameControlValues();
+      }
+#else
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
+       DrawGameValue_Time(TimePlayed);
+#endif
+
+      level.native_em_level->lev->time =
+       (game.no_time_limit ? TimePlayed : TimeLeft);
+    }
+
+    if (tape.recording || tape.playing)
+      DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
+  }
+
+#if 1
+  UpdateAndDisplayGameControlValues();
+#else
+  UpdateGameDoorValues();
+  DrawGameDoorValues();
+#endif
+}
+
 void AdvanceFrameAndPlayerCounters(int player_nr)
 {
   int i;
@@ -8268,20 +12270,104 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
 
     if (stored_player[i].drop_delay > 0)
       stored_player[i].drop_delay--;
+
+    if (stored_player[i].is_dropping_pressed)
+      stored_player[i].drop_pressed_delay++;
+  }
+}
+
+void StartGameActions(boolean init_network_game, boolean record_tape,
+                     int 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_StartPlaying();
+
+    return;
   }
+#endif
+
+  InitGame();
 }
 
 void GameActions()
 {
-  static unsigned long game_frame_delay = 0;
-  unsigned long game_frame_delay_value;
-  int magic_wall_x = 0, magic_wall_y = 0;
-  int i, x, y, element, graphic;
+  static unsigned int game_frame_delay = 0;
+  unsigned int game_frame_delay_value;
   byte *recorded_player_action;
   byte summarized_player_action = 0;
   byte tape_action[MAX_PLAYERS];
+  int i;
+
+  /* detect endless loops, caused by custom element programming */
+  if (recursion_loop_detected && recursion_loop_depth == 0)
+  {
+    char *message = getStringCat3("Internal Error! Element ",
+                                 EL_NAME(recursion_loop_element),
+                                 " caused endless loop! Quit the game?");
+
+    Error(ERR_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 */
+
+    free(message);
+
+    return;
+  }
+
+  if (game.restart_level)
+    StartGameActions(options.network, setup.autorecord, level.random_seed);
+
+  /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
+  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;
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    if (game_sp.LevelSolved &&
+       !game_sp.GameOver)                              /* game won */
+    {
+      PlayerWins(local_player);
+
+      game_sp.GameOver = TRUE;
+
+      AllPlayersGone = TRUE;
+    }
+
+    if (game_sp.GameOver)                              /* game lost */
+      AllPlayersGone = TRUE;
+  }
+
+  if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
+    GameWon();
 
-  if (game_status != GAME_MODE_PLAYING)
+  if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
+    TapeStop();
+
+  if (game_status != GAME_MODE_PLAYING)                /* status might have changed */
     return;
 
   game_frame_delay_value =
@@ -8292,8 +12378,6 @@ void GameActions()
 
   /* ---------- main game synchronization point ---------- */
 
-  InitPlayfieldScanModeVars();
-
   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
 
   if (network_playing && !network_player_action_received)
@@ -8311,25 +12395,43 @@ void GameActions()
 
     if (!network_player_action_received)
       return;          /* failed to get network player actions in time */
+
+    /* 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 */
+
+  network_player_action_received = FALSE;
+
+  /* when playing tape, read previously recorded player input from tape data */
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
 #if 1
-  /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
-  if (recorded_player_action == NULL && tape.pausing)
+  /* TapePlayAction() may return NULL when toggling to "pause before death" */
+  if (tape.pausing)
     return;
 #endif
 
+  if (tape.set_centered_player)
+  {
+    game.centered_player_nr_next = tape.centered_player_nr_next;
+    game.set_centered_player = TRUE;
+  }
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     summarized_player_action |= stored_player[i].action;
 
+#if 1
+    if (!network_playing && (game.team_mode || tape.playing))
+      stored_player[i].effective_action = stored_player[i].action;
+#else
     if (!network_playing)
       stored_player[i].effective_action = stored_player[i].action;
+#endif
   }
 
 #if defined(NETWORK_AVALIABLE)
@@ -8337,9 +12439,19 @@ void GameActions()
     SendToServer_MovePlayer(summarized_player_action);
 #endif
 
-  if (!options.network && !setup.team_mode)
+  if (!options.network && !game.team_mode)
     local_player->effective_action = summarized_player_action;
 
+  if (tape.recording &&
+      setup.team_mode &&
+      setup.input_on_focus &&
+      game.centered_player_nr != -1)
+  {
+    for (i = 0; i < MAX_PLAYERS; i++)
+      stored_player[i].effective_action =
+       (i == game.centered_player_nr ? summarized_player_action : 0);
+  }
+
   if (recorded_player_action != NULL)
     for (i = 0; i < MAX_PLAYERS; i++)
       stored_player[i].effective_action = recorded_player_action[i];
@@ -8348,14 +12460,203 @@ void GameActions()
   {
     tape_action[i] = stored_player[i].effective_action;
 
+#if 1
+    /* (this may happen in the RND game engine if a player was not present on
+       the playfield on level start, but appeared later from a custom element */
+    if (tape.recording &&
+       setup.team_mode &&
+       tape_action[i] &&
+       !tape.player_participates[i])
+      tape.player_participates[i] = TRUE;
+#else
+    /* (this can only happen in the R'n'D game engine) */
     if (tape.recording && tape_action[i] && !tape.player_participates[i])
       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
+#endif
   }
 
-  /* only save actions from input devices, but not programmed actions */
+  /* only record actions from input devices, but not programmed actions */
   if (tape.recording)
     TapeRecordAction(tape_action);
 
+#if USE_NEW_PLAYER_ASSIGNMENTS
+#if 1
+  if (game.team_mode)
+#endif
+  {
+    byte mapped_action[MAX_PLAYERS];
+
+#if DEBUG_PLAYER_ACTIONS
+    printf(":::");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %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
+    printf(" =>");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %d, ", stored_player[i].effective_action);
+    printf("\n");
+#endif
+  }
+#if DEBUG_PLAYER_ACTIONS
+  else
+  {
+    printf(":::");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %d, ", stored_player[i].effective_action);
+    printf("\n");
+  }
+#endif
+#endif
+
+#if 0
+  printf("::: summarized_player_action == %d\n",
+        local_player->effective_action);
+#endif
+
+
+
+
+#if 0
+#if DEBUG_INIT_PLAYER
+    if (options.debug)
+    {
+      printf("Player status (final):\n");
+
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+
+       printf("- player %d: present == %d, connected == %d, active == %d",
+              i + 1,
+              player->present,
+              player->connected,
+              player->active);
+
+       if (local_player == player)
+         printf(" (local player)");
+
+       printf("\n");
+      }
+    }
+#endif
+#endif
+
+
+
+  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
+  {
+    GameActions_RND();
+  }
+}
+
+void GameActions_EM_Main()
+{
+  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();
+
+  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
+}
+
+void GameActions_SP_Main()
+{
+  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);
+
+  CheckLevelTime();
+
+  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
+}
+
+void GameActions_RND()
+{
+  int magic_wall_x = 0, magic_wall_y = 0;
+  int i, x, y, element, graphic;
+
+  InitPlayfieldScanModeVars();
+
+#if USE_ONE_MORE_CHANGE_PER_FRAME
+  if (game.engine_version >= VERSION_IDENT(3,2,0,7))
+  {
+    SCAN_PLAYFIELD(x, y)
+    {
+      ChangeCount[x][y] = 0;
+      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 */
+    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 */
+    if (game.centered_player_nr_next >= 0 &&
+       !stored_player[game.centered_player_nr_next].active)
+    {
+      game.centered_player_nr_next = game.centered_player_nr;
+      game.set_centered_player = FALSE;
+    }
+  }
+
+  if (game.set_centered_player &&
+      ScreenMovPos == 0)       /* screen currently aligned at tile position */
+  {
+    int sx, sy;
+
+    if (game.centered_player_nr_next == -1)
+    {
+      setScreenCenteredToAllPlayers(&sx, &sy);
+    }
+    else
+    {
+      sx = stored_player[game.centered_player_nr_next].jx;
+      sy = stored_player[game.centered_player_nr_next].jy;
+    }
+
+    game.centered_player_nr = game.centered_player_nr_next;
+    game.set_centered_player = FALSE;
+
+    DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
+    DrawGameDoorValues();
+  }
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     int actual_player_action = stored_player[i].effective_action;
@@ -8375,20 +12676,11 @@ void GameActions()
     if (stored_player[i].programmed_action)
       actual_player_action = stored_player[i].programmed_action;
 
-#if 1
     PlayerActions(&stored_player[i], actual_player_action);
-#else
-    tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
-
-    if (tape.recording && tape_action[i] && !tape.player_participates[i])
-      tape.player_participates[i] = TRUE;    /* player just appeared from CE */
-#endif
 
     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
   }
 
-  network_player_action_received = FALSE;
-
   ScrollScreen(NULL, SCROLL_GO_ON);
 
   /* for backwards compatibility, the following code emulates a fixed bug that
@@ -8416,18 +12708,16 @@ void GameActions()
 
        /* continue moving after pushing (this is actually a bug) */
        if (!IS_MOVING(x, y))
-       {
          Stop[x][y] = FALSE;
-       }
       }
     }
   }
 
-#if 1
-  SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#if 0
+  debug_print_timestamp(0, "start main loop profiling");
 #endif
+
+  SCAN_PLAYFIELD(x, y)
   {
     ChangeCount[x][y] = 0;
     ChangeEvent[x][y] = -1;
@@ -8447,7 +12737,7 @@ void GameActions()
       if (MovDelay[x][y] <= 0)
       {
        RemoveField(x, y);
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        TestIfElementTouchesCustomElement(x, y);        /* for empty space */
       }
@@ -8471,15 +12761,17 @@ void GameActions()
       WasJustFalling[x][y]--;
     if (CheckCollision[x][y] > 0)
       CheckCollision[x][y]--;
+    if (CheckImpact[x][y] > 0)
+      CheckImpact[x][y]--;
 
     GfxFrame[x][y]++;
 
     /* reset finished pushing action (not done in ContinueMoving() to allow
-       continous pushing animation for elements with zero push delay) */
+       continuous pushing animation for elements with zero push delay) */
     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
     {
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
 
 #if DEBUG
@@ -8499,46 +12791,69 @@ void GameActions()
 #endif
   }
 
-#if 1
-  SCAN_PLAYFIELD(x, y)
-#else
-  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#if 0
+  debug_print_timestamp(0, "- time for pre-main loop:");
 #endif
+
+#if 0  // -------------------- !!! TEST ONLY !!! --------------------
+  SCAN_PLAYFIELD(x, y)
   {
     element = Feld[x][y];
     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
 
-#if 0
-    printf("::: %d,%d\n", x, y);
-
-    if (element == EL_ROCK)
-      printf("::: Yo man! Rocks can fall!\n");
+#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];
 
-    if (graphic_info[graphic].anim_global_sync)
-      GfxFrame[x][y] = FrameCounter;
-    else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
-    {
-      int old_gfx_frame = GfxFrame[x][y];
+      if (graphic_info[graphic2].anim_global_sync)
+       GfxFrame[x][y] = FrameCounter;
+      else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
+       GfxFrame[x][y] = CustomValue[x][y];
+      else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
+       GfxFrame[x][y] = element_info[element2].collect_score;
+      else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
+       GfxFrame[x][y] = ChangeDelay[x][y];
 
-      GfxFrame[x][y] = CustomValue[x][y];
+      if (redraw && GfxFrame[x][y] != last_gfx_frame)
+       DrawLevelGraphicAnimation(x, y, graphic2);
+    }
+#else
+    ResetGfxFrame(x, y, TRUE);
+#endif
 
 #if 1
-      if (GfxFrame[x][y] != old_gfx_frame)
+    if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+       IS_NEXT_FRAME(GfxFrame[x][y], graphic))
+      ResetRandomAnimationValue(x, y);
 #endif
-       DrawLevelGraphicAnimation(x, y, graphic);
-    }
-    else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
-    {
-      int old_gfx_frame = GfxFrame[x][y];
 
-      GfxFrame[x][y] = element_info[element].collect_score;
+#if 1
+    SetRandomAnimationValue(x, y);
+#endif
 
 #if 1
-      if (GfxFrame[x][y] != old_gfx_frame)
+    PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
 #endif
-       DrawLevelGraphicAnimation(x, y, graphic);
-    }
+  }
+#endif // -------------------- !!! TEST ONLY !!! --------------------
+
+#if 0
+  debug_print_timestamp(0, "- time for TEST loop:     -->");
+#endif
+
+  SCAN_PLAYFIELD(x, y)
+  {
+    element = Feld[x][y];
+    graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+    ResetGfxFrame(x, y, TRUE);
 
     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
        IS_NEXT_FRAME(GfxFrame[x][y], graphic))
@@ -8561,18 +12876,6 @@ void GameActions()
        (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
     {
       int page = element_info[element].event_page_nr[CE_DELAY];
-#if 0
-      HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
-#else
-
-#if 0
-      printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
-#endif
-
-#if 0
-      if (element == EL_CUSTOM_255)
-       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
-#endif
 
 #if 1
       HandleElementChange(x, y, page);
@@ -8584,12 +12887,147 @@ void GameActions()
        ExecuteCustomElementAction(x, y, element, page);
 #endif
 
-#endif
+      element = Feld[x][y];
+      graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+    }
+
+#if 0  // ---------------------------------------------------------------------
+
+    if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
+    {
+      StartMoving(x, y);
 
       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])
+       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+      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)))
     {
       StartMoving(x, y);
@@ -8603,11 +13041,14 @@ void GameActions()
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       EdelsteinFunkeln(x, y);
+       TEST_DrawTwinkleOnField(x, y);
     }
     else if ((element == EL_ACID ||
              element == EL_EXIT_OPEN ||
+             element == EL_EM_EXIT_OPEN ||
              element == EL_SP_EXIT_OPEN ||
+             element == EL_STEEL_EXIT_OPEN ||
+             element == EL_EM_STEEL_EXIT_OPEN ||
              element == EL_SP_TERMINAL ||
              element == EL_SP_TERMINAL_ACTIVE ||
              element == EL_EXTRA_TIME ||
@@ -8633,35 +13074,43 @@ void GameActions()
       Life(x, y);
     else if (element == EL_EXIT_CLOSED)
       CheckExit(x, y);
+    else if (element == EL_EM_EXIT_CLOSED)
+      CheckExitEM(x, y);
+    else if (element == EL_STEEL_EXIT_CLOSED)
+      CheckExitSteel(x, y);
+    else if (element == EL_EM_STEEL_EXIT_CLOSED)
+      CheckExitSteelEM(x, y);
     else if (element == EL_SP_EXIT_CLOSED)
       CheckExitSP(x, y);
-    else if (element == EL_EXPANDABLE_WALL_GROWING)
+    else if (element == EL_EXPANDABLE_WALL_GROWING ||
+            element == EL_EXPANDABLE_STEELWALL_GROWING)
       MauerWaechst(x, y);
     else if (element == EL_EXPANDABLE_WALL ||
             element == EL_EXPANDABLE_WALL_HORIZONTAL ||
             element == EL_EXPANDABLE_WALL_VERTICAL ||
-            element == EL_EXPANDABLE_WALL_ANY)
+            element == EL_EXPANDABLE_WALL_ANY ||
+            element == EL_BD_EXPANDABLE_WALL)
       MauerAbleger(x, y);
+    else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+            element == EL_EXPANDABLE_STEELWALL_ANY)
+      MauerAblegerStahl(x, y);
     else if (element == EL_FLAMES)
       CheckForDragon(x, y);
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
-    else if (element == EL_ELEMENT_SNAPPING)
+    else if (element == EL_ELEMENT_SNAPPING ||
+            element == EL_DIAGONAL_SHRINKING ||
+            element == EL_DIAGONAL_GROWING)
     {
-#if 1
       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
 
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-#endif
     }
     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
-#if 0
-    if (element == EL_CUSTOM_255 ||
-       element == EL_CUSTOM_256)
-      DrawLevelGraphicAnimation(x, y, graphic);
-#endif
+#endif // ---------------------------------------------------------------------
 
     if (IS_BELT_ACTIVE(element))
       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
@@ -8676,7 +13125,10 @@ void GameActions()
           element == EL_MAGIC_WALL_EMPTYING ||
           element == EL_BD_MAGIC_WALL_FULL ||
           element == EL_BD_MAGIC_WALL_ACTIVE ||
-          element == EL_BD_MAGIC_WALL_EMPTYING) &&
+          element == EL_BD_MAGIC_WALL_EMPTYING ||
+          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))
       {
        magic_wall_x = x;
@@ -8685,11 +13137,15 @@ void GameActions()
     }
   }
 
+#if 0
+  debug_print_timestamp(0, "- time for MAIN loop:     -->");
+#endif
+
 #if USE_NEW_AMOEBA_CODE
   /* 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++)
     {
@@ -8701,6 +13157,7 @@ void GameActions()
          (element == EL_EMPTY ||
           CAN_GROW_INTO(element) ||
           element == EL_QUICKSAND_EMPTY ||
+          element == EL_QUICKSAND_FAST_EMPTY ||
           element == EL_ACID_SPLASH_LEFT ||
           element == EL_ACID_SPLASH_RIGHT))
       {
@@ -8722,11 +13179,7 @@ void GameActions()
   {
     game.explosions_delayed = FALSE;
 
-#if 1
     SCAN_PLAYFIELD(x, y)
-#else
-    for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
     {
       element = Feld[x][y];
 
@@ -8751,6 +13204,10 @@ void GameActions()
          element == EL_BD_MAGIC_WALL_ACTIVE ||
          element == EL_BD_MAGIC_WALL_EMPTYING)
        PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
+      else if (element == EL_DC_MAGIC_WALL_FULL ||
+              element == EL_DC_MAGIC_WALL_ACTIVE ||
+              element == EL_DC_MAGIC_WALL_EMPTYING)
+       PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
       else
        PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
     }
@@ -8758,13 +13215,10 @@ void GameActions()
     if (game.magic_wall_time_left > 0)
     {
       game.magic_wall_time_left--;
+
       if (!game.magic_wall_time_left)
       {
-#if 1
        SCAN_PLAYFIELD(x, y)
-#else
-       for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
-#endif
        {
          element = Feld[x][y];
 
@@ -8772,13 +13226,19 @@ void GameActions()
              element == EL_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           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;
-           DrawLevelField(x, y);
+           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;
+           TEST_DrawLevelField(x, y);
          }
        }
 
@@ -8832,57 +13292,46 @@ void GameActions()
     }
   }
 
-  if (TimeFrames >= FRAMES_PER_SECOND)
+#if USE_DELAYED_GFX_REDRAW
+  SCAN_PLAYFIELD(x, y)
   {
-    TimeFrames = 0;
-    TapeTime++;
-
-    for (i = 0; i < MAX_PLAYERS; i++)
-    {
-      struct PlayerInfo *player = &stored_player[i];
-
-      if (SHIELD_ON(player))
-      {
-       player->shield_normal_time_left--;
-
-       if (player->shield_deadly_time_left > 0)
-         player->shield_deadly_time_left--;
-      }
-    }
-
-    if (!level.use_step_counter)
+#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
     {
-      TimePlayed++;
+      /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
+        !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
 
-      if (TimeLeft > 0)
-      {
-       TimeLeft--;
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
+       DrawLevelField(x, y);
 
-       if (TimeLeft <= 10 && setup.time_limit)
-         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
+       DrawLevelFieldCrumbled(x, y);
 
-       DrawGameValue_Time(TimeLeft);
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
+       DrawLevelFieldCrumbledNeighbours(x, y);
 
-       if (!TimeLeft && setup.time_limit)
-         for (i = 0; i < MAX_PLAYERS; i++)
-           KillPlayer(&stored_player[i]);
-      }
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
-       DrawGameValue_Time(TimePlayed);
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
+       DrawTwinkleOnField(x, y);
     }
 
-    if (tape.recording || tape.playing)
-      DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
+    GfxRedraw[x][y] = GFX_REDRAW_NONE;
   }
+#endif
+
+  CheckLevelTime();
 
   DrawAllPlayers();
   PlayAllPlayersSound();
 
   if (options.debug)                   /* calculate frames per second */
   {
-    static unsigned long fps_counter = 0;
+    static unsigned int fps_counter = 0;
     static int fps_frames = 0;
-    unsigned long fps_delay_ms = Counter() - fps_counter;
+    unsigned int fps_delay_ms = Counter() - fps_counter;
 
     fps_frames++;
 
@@ -8906,6 +13355,11 @@ void GameActions()
     local_player->show_envelope = 0;
   }
 
+#if 0
+  debug_print_timestamp(0, "stop main loop profiling ");
+  printf("----------------------------------------------------------\n");
+#endif
+
   /* use random number generator in every frame to make it less predictable */
   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
     RND(1);
@@ -8952,25 +13406,110 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
+#if 0
+  /* (directly solved in BlitBitmap() now) */
+  static Bitmap *bitmap_db_field2 = NULL;
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
   int x, y;
+#else
+  int x, y;
+#endif
+
+#if 0
+  /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
+  /* only horizontal XOR vertical scroll direction allowed */
+  if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
+    return;
+#endif
+
+#if 0
+  /* (directly solved in BlitBitmap() now) */
+  if (bitmap_db_field2 == NULL)
+    bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
+
+  /* needed when blitting directly to same bitmap -- should not be needed with
+     recent SDL libraries, but apparently does not work in 1.2.11 directly */
+  BlitBitmap(drawto_field, bitmap_db_field2,
+            FX + TILEX * (dx == -1) - softscroll_offset,
+            FY + TILEY * (dy == -1) - softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
+  BlitBitmap(bitmap_db_field2, drawto_field,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
+
+#else
+
+#if 0
+  /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
+  int xsize = (BX2 - BX1 + 1);
+  int ysize = (BY2 - BY1 + 1);
+  int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
+  int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
+  int step  = (start < end ? +1 : -1);
+
+  for (i = start; i != end; i += step)
+  {
+    BlitBitmap(drawto_field, drawto_field,
+              FX + TILEX * (dx != 0 ? i + step : 0),
+              FY + TILEY * (dy != 0 ? i + step : 0),
+              TILEX * (dx != 0 ? 1 : xsize),
+              TILEY * (dy != 0 ? 1 : ysize),
+              FX + TILEX * (dx != 0 ? i : 0),
+              FY + TILEY * (dy != 0 ? i : 0));
+  }
+
+#else
+
+#if NEW_TILESIZE
+#if NEW_SCROLL
+  int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
+#else
+  int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
+#endif
+#else
+#if NEW_SCROLL
+  int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
+#else
+  int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
+#endif
+#endif
 
+#if NEW_TILESIZE
+  BlitBitmap(drawto_field, drawto_field,
+            FX + TILEX_VAR * (dx == -1) - softscroll_offset,
+            FY + TILEY_VAR * (dy == -1) - softscroll_offset,
+            SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX_VAR * (dx == 1) - softscroll_offset,
+            FY + TILEY_VAR * (dy == 1) - softscroll_offset);
+#else
   BlitBitmap(drawto_field, drawto_field,
             FX + TILEX * (dx == -1) - softscroll_offset,
             FY + TILEY * (dy == -1) - softscroll_offset,
-            SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
-            SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
             FX + TILEX * (dx == 1) - softscroll_offset,
             FY + TILEY * (dy == 1) - softscroll_offset);
+#endif
+
+#endif
+#endif
 
-  if (dx)
+  if (dx != 0)
   {
     x = (dx == 1 ? BX1 : BX2);
     for (y = BY1; y <= BY2; y++)
       DrawScreenField(x, y);
   }
 
-  if (dy)
+  if (dy != 0)
   {
     y = (dy == 1 ? BY1 : BY2);
     for (x = BX1; x <= BX2; x++)
@@ -9024,11 +13563,15 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
 
 static void CheckGravityMovement(struct PlayerInfo *player)
 {
+#if USE_PLAYER_GRAVITY
+  if (player->gravity && !player->programmed_action)
+#else
   if (game.gravity && !player->programmed_action)
+#endif
   {
     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
-    boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
+    boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
     int jx = player->jx, jy = player->jy;
     boolean player_is_moving_to_valid_field =
       (!player_is_snapping &&
@@ -9046,7 +13589,11 @@ 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 =
@@ -9073,11 +13620,14 @@ 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;
 
   if (!player->active || (!dx && !dy))
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
 
   player->MovDir = (dx < 0 ? MV_LEFT :
                    dx > 0 ? MV_RIGHT :
@@ -9085,11 +13635,10 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
                    dy > 0 ? MV_DOWN :  MV_NONE);
 
   if (!IN_LEV_FIELD(new_jx, new_jy))
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
 
-  if (player->cannot_move)
+  if (!player_can_move)
   {
-#if 1
     if (player->MovPos == 0)
     {
       player->is_moving = FALSE;
@@ -9098,20 +13647,22 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
       player->is_snapping = FALSE;
       player->is_pushing = FALSE;
     }
-#else
-    DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
-    SnapField(player, 0, 0);
-#endif
-
-    return MF_NO_ACTION;
   }
 
+#if 1
+  if (!options.network && game.centered_player_nr == -1 &&
+      !AllPlayersInSight(player, new_jx, new_jy))
+    return MP_NO_ACTION;
+#else
   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
+#endif
 
+#if !USE_FIXED_DONT_RUN_INTO
   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
 
-  if (DONT_RUN_INTO(element))
+  /* (moved to DigField()) */
+  if (player_can_move && DONT_RUN_INTO(element))
   {
     if (element == EL_ACID && dx == 0 && dy == 1)
     {
@@ -9125,16 +13676,17 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
     else
       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
 
-    return MF_MOVING;
+    return MP_MOVING;
   }
+#endif
 
   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
-  if (can_move != MF_MOVING)
+  if (can_move != MP_MOVING)
     return can_move;
 
   /* check if DigField() has caused relocation of the player */
   if (player->jx != jx || player->jy != jy)
-    return MF_NO_ACTION;       /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
+    return MP_NO_ACTION;       /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
 
   StorePlayer[jx][jy] = 0;
   player->last_jx = jx;
@@ -9156,16 +13708,23 @@ 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 */
   ScrollPlayer(player, SCROLL_INIT);
+#endif
 
-  return MF_MOVING;
+  return MP_MOVING;
 }
 
 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 {
   int jx = player->jx, jy = player->jy;
   int old_jx = jx, old_jy = jy;
-  int moved = MF_NO_ACTION;
+  int moved = MP_NO_ACTION;
 
   if (!player->active)
     return FALSE;
@@ -9203,7 +13762,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     int original_move_delay_value = player->move_delay_value;
 
 #if DEBUG
-    printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
+    printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
           tape.counter);
 #endif
 
@@ -9224,6 +13783,8 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->move_delay_value = original_move_delay_value;
   }
 
+  player->is_active = FALSE;
+
   if (player->last_move_dir & MV_HORIZONTAL)
   {
     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
@@ -9235,14 +13796,31 @@ 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;
+    player->is_digging = FALSE;
+    player->is_collecting = FALSE;
+    player->is_snapping = FALSE;
+    player->is_pushing = FALSE;
+  }
+#endif
+
   jx = player->jx;
   jy = player->jy;
 
-  if (moved & MF_MOVING && !ScreenMovPos &&
+#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 = (setup.scroll_delay ? 3 : 0);
+    int offset = game.scroll_delay_value;
 
     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
     {
@@ -9294,12 +13872,22 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
     {
+#if 1
+      if (!options.network && game.centered_player_nr == -1 &&
+         !AllPlayersInVisibleScreen())
+      {
+       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);
@@ -9309,20 +13897,27 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
   player->StepFrame = 0;
 
-  if (moved & MF_MOVING)
+  if (moved & MP_MOVING)
   {
     if (old_jx != jx && old_jy == jy)
       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
     else if (old_jx == jx && old_jy != jy)
       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
 
-    DrawLevelField(jx, jy);    /* for "crumbled sand" */
+    TEST_DrawLevelField(jx, jy);       /* for "crumbled sand" */
 
     player->last_move_dir = player->MovDir;
     player->is_moving = TRUE;
     player->is_snapping = FALSE;
     player->is_switching = FALSE;
     player->is_dropping = FALSE;
+    player->is_dropping_pressed = FALSE;
+    player->drop_pressed_delay = 0;
+
+#if 0
+    /* should better be called here than above, but this breaks some tapes */
+    ScrollPlayer(player, SCROLL_INIT);
+#endif
   }
   else
   {
@@ -9388,8 +13983,13 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        last_field_block_delay += player->move_delay_value;
 
        /* when blocking enabled, prevent moving up despite gravity */
+#if USE_PLAYER_GRAVITY
+       if (player->gravity && player->MovDir == MV_UP)
+         block_delay_adjustment = -1;
+#else
        if (game.gravity && player->MovDir == MV_UP)
          block_delay_adjustment = -1;
+#endif
       }
 
       /* add block delay adjustment (also possible when not blocking) */
@@ -9409,12 +14009,6 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
   else if (!FrameReached(&player->actual_frame_counter, 1))
     return;
 
-#if 0
-  printf("::: player->MovPos: %d -> %d\n",
-        player->MovPos,
-        player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
-#endif
-
 #if USE_NEW_PLAYER_SPEED
   if (player->MovPos != 0)
   {
@@ -9436,10 +14030,6 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
   if (player->MovPos == 0)     /* player reached destination field */
   {
-#if 0
-    printf("::: player reached destination field\n");
-#endif
-
     if (player->move_delay_reset_counter > 0)
     {
       player->move_delay_reset_counter--;
@@ -9458,6 +14048,15 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     player->last_jy = jy;
 
     if (Feld[jx][jy] == EL_EXIT_OPEN ||
+       Feld[jx][jy] == EL_EM_EXIT_OPEN ||
+#if 1
+       Feld[jx][jy] == EL_EM_EXIT_OPENING ||
+#endif
+       Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
+       Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
+#if 1
+       Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
+#endif
        Feld[jx][jy] == EL_SP_EXIT_OPEN ||
        Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
     {
@@ -9466,7 +14065,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
       if (local_player->friends_still_needed == 0 ||
          IS_SP_ELEMENT(Feld[jx][jy]))
-       player->LevelSolved = player->GameOver = TRUE;
+       PlayerWins(player);
     }
 
     /* this breaks one level: "machine", level 000 */
@@ -9496,8 +14095,13 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                          CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
 
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
+                                       CE_MOVE_OF_X, move_direction);
+#else
       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
                                        CE_MOVE_OF_X, move_direction);
+#endif
     }
 
     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
@@ -9514,7 +14118,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        RemovePlayer(player);
     }
 
-    if (level.use_step_counter)
+    if (!local_player->LevelSolved && level.use_step_counter)
     {
       int i;
 
@@ -9525,16 +14129,31 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        TimeLeft--;
 
        if (TimeLeft <= 10 && setup.time_limit)
-         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+         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]);
       }
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+#if 1
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
+      {
+       game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
+
+       DisplayGameControlValues();
+      }
+#else
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
+#endif
     }
 
     if (tape.single_step && tape.recording && !tape.pausing &&
@@ -9545,7 +14164,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
 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)
   {
@@ -9582,7 +14201,113 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
   };
   static int trigger_sides[4][2] =
   {
-    /* center side       border side */
+    /* 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
+  };
+  int center_element = Feld[x][y];     /* should always be non-moving! */
+  int i;
+
+  for (i = 0; i < NUM_DIRECTIONS; i++)
+  {
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
+    int center_side = trigger_sides[i][0];
+    int border_side = trigger_sides[i][1];
+    int border_element;
+
+    if (!IN_LEV_FIELD(xx, yy))
+      continue;
+
+    if (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! */
+      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 */
+
+      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);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (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);
+      }
+#endif
+    }
+    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 */
+      }
+
+      CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, center_side);
+      CheckTriggeredElementChangeByPlayer(x, y, center_element,
+                                         CE_PLAYER_TOUCHES_X,
+                                         player->index_bit, center_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (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);
+      }
+#endif
+
+      break;
+    }
+  }
+}
+
+#if USE_ELEMENT_TOUCHING_BUGFIX
+
+void TestIfElementTouchesCustomElement(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  */
@@ -9595,60 +14320,86 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     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 border_element_old[NUM_DIRECTIONS];
   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;
 
+    border_element_old[i] = -1;
+
     if (!IN_LEV_FIELD(xx, yy))
       continue;
 
-    if (IS_PLAYER(x, y))
-    {
-      struct PlayerInfo *player = PLAYERINFO(x, y);
+    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 */
 
-      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 */
+    border_element_old[i] = border_element;
+  }
 
-      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);
-    }
-    else if (IS_PLAYER(xx, yy))
-    {
-      struct PlayerInfo *player = PLAYERINFO(xx, yy);
+  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_element = border_element_old[i];
 
-      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 */
-      }
+    if (border_element == -1)
+      continue;
 
-      CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
-                                player->index_bit, center_side);
-      CheckTriggeredElementChangeByPlayer(x, y, center_element,
-                                         CE_PLAYER_TOUCHES_X,
-                                         player->index_bit, center_side);
-      break;
+    /* check for change of border element */
+    CheckElementChangeBySide(xx, yy, border_element, center_element,
+                            CE_TOUCHING_X, center_side);
+
+    /* (center element cannot be player, so we dont have to check this here) */
+  }
+
+  for (i = 0; i < NUM_DIRECTIONS; i++)
+  {
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
+    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);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+    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 TestIfElementTouchesCustomElement(int x, int y)
+#else
+
+void TestIfElementTouchesCustomElement_OLD(int x, int y)
 {
   static int xy[4][2] =
   {
@@ -9708,6 +14459,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
   }
 }
 
+#endif
+
 void TestIfElementHitsCustomElement(int x, int y, int direction)
 {
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
@@ -9738,11 +14491,25 @@ 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 USE_FIX_CE_ACTION_WITH_PLAYER
+      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);
+      }
+#endif
     }
   }
 
@@ -9817,6 +14584,7 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 {
   int i, kill_x = -1, kill_y = -1;
+
   int bad_element = -1;
   static int test_xy[4][2] =
   {
@@ -9914,6 +14682,7 @@ 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;
 
@@ -9944,12 +14713,14 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
        kill_x = test_x;
        kill_y = test_y;
+
        break;
       }
       else if (test_element == EL_PENGUIN)
       {
        kill_x = test_x;
        kill_y = test_y;
+
        break;
       }
     }
@@ -9972,6 +14743,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 = Feld[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 = Feld[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);
@@ -10043,6 +14871,28 @@ void KillPlayer(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+#if 0
+  printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
+  /* the following code was introduced to prevent an infinite loop when calling
+     -> Bang()
+     -> CheckTriggeredElementChangeExt()
+     -> ExecuteCustomElementAction()
+     -> KillPlayer()
+     -> (infinitely repeating the above sequence of function calls)
+     which occurs when killing the player while having a CE with the setting
+     "kill player X when explosion of <player X>"; the solution using a new
+     field "player->killed" was chosen for backwards compatibility, although
+     clever use of the fields "player->active" etc. would probably also work */
+#if 1
+  if (player->killed)
+    return;
+#endif
+
+  player->killed = TRUE;
+
   /* remove accessible field at the player's position */
   Feld[jx][jy] = EL_EMPTY;
 
@@ -10050,8 +14900,31 @@ void KillPlayer(struct PlayerInfo *player)
   player->shield_normal_time_left = 0;
   player->shield_deadly_time_left = 0;
 
+#if 0
+  printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
   Bang(jx, jy);
+
+#if 0
+  printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
+#if USE_PLAYER_REANIMATION
+#if 1
+  if (player->reanimated)      /* killed player may have been reanimated */
+    player->killed = player->reanimated = FALSE;
+  else
+    BuryPlayer(player);
+#else
+  if (player->killed)          /* player may have been reanimated */
+    BuryPlayer(player);
+#endif
+#else
   BuryPlayer(player);
+#endif
 }
 
 static void KillPlayerUnlessEnemyProtected(int x, int y)
@@ -10092,7 +14965,7 @@ void RemovePlayer(struct PlayerInfo *player)
     StorePlayer[jx][jy] = 0;
 
   if (player->is_moving)
-    DrawLevelField(player->last_jx, player->last_jy);
+    TEST_DrawLevelField(player->last_jx, player->last_jy);
 
   for (i = 0; i < MAX_PLAYERS; i++)
     if (stored_player[i].active)
@@ -10109,7 +14982,7 @@ void RemovePlayer(struct PlayerInfo *player)
 static void setFieldForSnapping(int x, int y, int element, int direction)
 {
   struct ElementInfo *ei = &element_info[element];
-  int direction_bit = MV_DIR_BIT(direction);
+  int direction_bit = MV_DIR_TO_BIT(direction);
   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
                IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
@@ -10163,12 +15036,14 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player,
   =============================================================================
 */
 
-int DigField(struct PlayerInfo *player,
-            int oldx, int oldy, int x, int y,
-            int real_dx, int real_dy, int mode)
+static int DigField(struct PlayerInfo *player,
+                   int oldx, int oldy, int x, int y,
+                   int real_dx, int real_dy, int mode)
 {
   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
   boolean player_was_pushing = player->is_pushing;
+  boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
+  boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
   int jx = oldx, jy = oldy;
   int dx = x - jx, dy = y - jy;
   int nextx = x + dx, nexty = y + dy;
@@ -10179,7 +15054,11 @@ int DigField(struct PlayerInfo *player,
   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
   int dig_side = MV_DIR_OPPOSITE(move_direction);
   int old_element = Feld[jx][jy];
+#if USE_FIXED_DONT_RUN_INTO
+  int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
+#else
   int element;
+#endif
   int collect_count;
 
   if (is_player)               /* function can also be called by EL_PENGUIN */
@@ -10198,12 +15077,14 @@ int DigField(struct PlayerInfo *player,
       player->is_switching = FALSE;
       player->push_delay = -1;
 
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
     }
   }
 
+#if !USE_FIXED_DONT_RUN_INTO
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
-    return MF_NO_ACTION;
+    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];
@@ -10214,35 +15095,51 @@ int DigField(struct PlayerInfo *player,
     old_element = Back[jx][jy];
 
   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
-    return MF_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 MF_NO_ACTION;       /* field has no opening in this direction */
+    return MP_NO_ACTION;       /* field has no opening in this direction */
 
-  element = Feld[x][y];
-#if USE_NEW_CUSTOM_VALUE
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
+  {
+    SplashAcid(x, y);
 
-#if 1
-  collect_count = element_info[element].collect_count_initial;
-#else
-  collect_count = CustomValue[x][y];
+    Feld[jx][jy] = player->artwork_element;
+    InitMovingField(jx, jy, MV_DOWN);
+    Store[jx][jy] = EL_ACID;
+    ContinueMoving(jx, jy);
+    BuryPlayer(player);
+
+    return MP_DONT_RUN_INTO;
+  }
 #endif
 
-#else
-  collect_count = element_info[element].collect_count_initial;
+#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 0
-  if (element != EL_BLOCKED &&
-      CustomValue[x][y] != element_info[element].collect_count_initial)
-    printf("::: %d: %d != %d\n",
-          element,
-          CustomValue[x][y],
-          element_info[element].collect_count_initial);
+#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 */
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
+
+  if (game.engine_version < VERSION_IDENT(2,2,0,0))
+    player_can_move = player_can_move_or_snap;
 
   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
       game.engine_version >= VERSION_IDENT(2,2,0,0))
@@ -10252,18 +15149,29 @@ int DigField(struct PlayerInfo *player,
     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
                                        player->index_bit, dig_side);
 
+    if (element == EL_DC_LANDMINE)
+      Bang(x, y);
+
     if (Feld[x][y] != element)         /* field changed by snapping */
-      return MF_ACTION;
+      return MP_ACTION;
 
-    return MF_NO_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 MF_NO_ACTION;       /* player cannot walk here due to gravity */
+    return MP_NO_ACTION;       /* player cannot walk here due to gravity */
+#endif
 
-  if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
+  if (player_can_move &&
+      IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
   {
     int sound_element = SND_ELEMENT(element);
     int sound_action = ACTION_WALKING;
@@ -10271,19 +15179,28 @@ int DigField(struct PlayerInfo *player,
     if (IS_RND_GATE(element))
     {
       if (!player->key[RND_GATE_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_RND_GATE_GRAY(element))
     {
       if (!player->key[RND_GATE_GRAY_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_RND_GATE_GRAY_ACTIVE(element))
     {
       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (element == EL_EXIT_OPEN ||
+            element == EL_EM_EXIT_OPEN ||
+#if 1
+            element == EL_EM_EXIT_OPENING ||
+#endif
+            element == EL_STEEL_EXIT_OPEN ||
+            element == EL_EM_STEEL_EXIT_OPEN ||
+#if 1
+            element == EL_EM_STEEL_EXIT_OPENING ||
+#endif
             element == EL_SP_EXIT_OPEN ||
             element == EL_SP_EXIT_OPENING)
     {
@@ -10300,28 +15217,53 @@ int DigField(struct PlayerInfo *player,
     else
       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
   }
-  else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
+  else if (player_can_move &&
+          IS_PASSABLE(element) && canPassField(x, y, move_direction))
   {
     if (!ACCESS_FROM(element, opposite_direction))
-      return MF_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! */
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (IS_EM_GATE(element))
     {
       if (!player->key[EM_GATE_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_EM_GATE_GRAY(element))
     {
       if (!player->key[EM_GATE_GRAY_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_EM_GATE_GRAY_ACTIVE(element))
     {
       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
+    }
+    else if (IS_EMC_GATE(element))
+    {
+      if (!player->key[EMC_GATE_NR(element)])
+       return MP_NO_ACTION;
+    }
+    else if (IS_EMC_GATE_GRAY(element))
+    {
+      if (!player->key[EMC_GATE_GRAY_NR(element)])
+       return MP_NO_ACTION;
+    }
+    else if (IS_EMC_GATE_GRAY_ACTIVE(element))
+    {
+      if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
+       return MP_NO_ACTION;
+    }
+    else if (element == EL_DC_GATE_WHITE ||
+            element == EL_DC_GATE_WHITE_GRAY ||
+            element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
+    {
+      if (player->num_white_keys == 0)
+       return MP_NO_ACTION;
+
+      player->num_white_keys--;
     }
     else if (IS_SP_PORT(element))
     {
@@ -10329,17 +15271,29 @@ 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 */
@@ -10354,7 +15308,7 @@ int DigField(struct PlayerInfo *player,
 
     PlayLevelSoundAction(x, y, ACTION_PASSING);
   }
-  else if (IS_DIGGABLE(element))
+  else if (player_can_move_or_snap && IS_DIGGABLE(element))
   {
     RemoveField(x, y);
 
@@ -10384,7 +15338,7 @@ int DigField(struct PlayerInfo *player,
                                          player->index_bit, dig_side);
     }
   }
-  else if (IS_COLLECTIBLE(element))
+  else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
   {
     RemoveField(x, y);
 
@@ -10401,7 +15355,14 @@ int DigField(struct PlayerInfo *player,
     else if (element == EL_EXTRA_TIME && level.time > 0)
     {
       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)
     {
@@ -10409,12 +15370,14 @@ int DigField(struct PlayerInfo *player,
       if (element == EL_SHIELD_DEADLY)
        player->shield_deadly_time_left += level.shield_deadly_time;
     }
-    else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
+    else if (element == EL_DYNAMITE ||
+            element == EL_EM_DYNAMITE ||
+            element == EL_SP_DISK_RED)
     {
       if (player->inventory_size < MAX_INVENTORY_SIZE)
        player->inventory_element[player->inventory_size++] = element;
 
-      DrawGameValue_Dynamite(local_player->inventory_size);
+      DrawGameDoorValues();
     }
     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
     {
@@ -10433,9 +15396,14 @@ int DigField(struct PlayerInfo *player,
     {
       player->key[KEY_NR(element)] = TRUE;
 
-      DrawGameValue_Keys(player->key);
+      DrawGameDoorValues();
+    }
+    else if (element == EL_DC_KEY_WHITE)
+    {
+      player->num_white_keys++;
 
-      redraw_mask |= REDRAW_DOOR_1;
+      /* display white keys? */
+      /* DrawGameDoorValues(); */
     }
     else if (IS_ENVELOPE(element))
     {
@@ -10465,7 +15433,7 @@ int DigField(struct PlayerInfo *player,
          if (player->inventory_size < MAX_INVENTORY_SIZE)
            player->inventory_element[player->inventory_size++] = element;
 
-      DrawGameValue_Dynamite(local_player->inventory_size);
+      DrawGameDoorValues();
     }
     else if (collect_count > 0)
     {
@@ -10473,7 +15441,13 @@ int DigField(struct PlayerInfo *player,
       if (local_player->gems_still_needed < 0)
        local_player->gems_still_needed = 0;
 
+#if 1
+      game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Emeralds(local_player->gems_still_needed);
+#endif
     }
 
     RaiseScoreElement(element);
@@ -10498,17 +15472,17 @@ int DigField(struct PlayerInfo *player,
                                          player->index_bit, dig_side);
     }
   }
-  else if (IS_PUSHABLE(element))
+  else if (player_can_move_or_snap && IS_PUSHABLE(element))
   {
     if (mode == DF_SNAP && element != EL_BD_ROCK)
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (CAN_FALL(element) && dy)
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
        !(element == EL_SPRING && level.use_spring_bug))
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
        ((move_direction & MV_VERTICAL &&
@@ -10521,12 +15495,12 @@ int DigField(struct PlayerInfo *player,
            IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
           (element_info[element].move_pattern & MV_DOWN &&
            IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     /* 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 MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
     {
@@ -10545,15 +15519,18 @@ int DigField(struct PlayerInfo *player,
     }
 
     player->is_pushing = TRUE;
+    player->is_active = TRUE;
 
     if (!(IN_LEV_FIELD(nextx, nexty) &&
          (IS_FREE(nextx, nexty) ||
-          (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
-           IS_SB_ELEMENT(element)))))
-      return MF_NO_ACTION;
+          (IS_SB_ELEMENT(element) &&
+           Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
+          (IS_CUSTOM_ELEMENT(element) &&
+           CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
+      return MP_NO_ACTION;
 
     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (player->push_delay == -1)      /* new pushing; restart delay */
       player->push_delay = 0;
@@ -10566,7 +15543,14 @@ int DigField(struct PlayerInfo *player,
       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
        player->move_delay = 0;
 
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
+    }
+
+    if (IS_CUSTOM_ELEMENT(element) &&
+       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
+    {
+      if (!DigFieldByCE(nextx, nexty, element))
+       return MP_NO_ACTION;
     }
 
     if (IS_SB_ELEMENT(element))
@@ -10594,10 +15578,16 @@ int DigField(struct PlayerInfo *player,
        PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
                                    ACTION_FILLING);
 
+#if 1
+      if (local_player->sokobanfields_still_needed == 0 &&
+         (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
+#else
       if (local_player->sokobanfields_still_needed == 0 &&
          game.emulation == EMU_SOKOBAN)
+#endif
       {
-       player->LevelSolved = player->GameOver = TRUE;
+       PlayerWins(player);
+
        PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
       }
     }
@@ -10636,7 +15626,7 @@ int DigField(struct PlayerInfo *player,
       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                          player->index_bit, dig_side);
 
-      return MF_ACTION;
+      return MP_ACTION;
     }
 
     player->is_switching = TRUE;
@@ -10651,17 +15641,15 @@ int DigField(struct PlayerInfo *player,
       ZX = x;
       ZY = y;
 
-      DrawLevelField(x, y);
+      game.robot_wheel_active = TRUE;
+
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_SP_TERMINAL)
     {
       int xx, yy;
 
-#if 1
       SCAN_PLAYFIELD(xx, yy)
-#else
-      for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
       {
        if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
          Bang(xx, yy);
@@ -10674,7 +15662,9 @@ int DigField(struct PlayerInfo *player,
       ToggleBeltSwitch(x, y);
     }
     else if (element == EL_SWITCHGATE_SWITCH_UP ||
-            element == EL_SWITCHGATE_SWITCH_DOWN)
+            element == EL_SWITCHGATE_SWITCH_DOWN ||
+            element == EL_DC_SWITCHGATE_SWITCH_UP ||
+            element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
       ToggleSwitchgateSwitch(x, y);
     }
@@ -10683,7 +15673,8 @@ int DigField(struct PlayerInfo *player,
     {
       ToggleLightSwitch(x, y);
     }
-    else if (element == EL_TIMEGATE_SWITCH)
+    else if (element == EL_TIMEGATE_SWITCH ||
+            element == EL_DC_TIMEGATE_SWITCH)
     {
       ActivateTimegateSwitch(x, y);
     }
@@ -10707,7 +15698,7 @@ int DigField(struct PlayerInfo *player,
       local_player->lights_still_needed--;
 
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_TIME_ORB_FULL)
     {
@@ -10716,11 +15707,19 @@ int DigField(struct PlayerInfo *player,
       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);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
             element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
@@ -10729,11 +15728,7 @@ int DigField(struct PlayerInfo *player,
 
       game.ball_state = !game.ball_state;
 
-#if 1
       SCAN_PLAYFIELD(xx, yy)
-#else
-      for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
-#endif
       {
        int e = Feld[xx][yy];
 
@@ -10763,7 +15758,7 @@ int DigField(struct PlayerInfo *player,
     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                        player->index_bit, dig_side);
 
-    return MF_ACTION;
+    return MP_ACTION;
   }
   else
   {
@@ -10784,26 +15779,88 @@ int DigField(struct PlayerInfo *player,
                                          player->index_bit, dig_side);
     }
 
-    CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
-                              player->index_bit, dig_side);
-    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
-                                       player->index_bit, dig_side);
+    CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
+                              player->index_bit, dig_side);
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
+                                       player->index_bit, dig_side);
+
+    return MP_NO_ACTION;
+  }
+
+  player->push_delay = -1;
+
+  if (is_player)               /* function can also be called by EL_PENGUIN */
+  {
+    if (Feld[x][y] != element)         /* really digged/collected something */
+    {
+      player->is_collecting = !player->is_digging;
+      player->is_active = TRUE;
+    }
+  }
+
+  return MP_MOVING;
+}
+
+static boolean DigFieldByCE(int x, int y, int digging_element)
+{
+  int element = Feld[x][y];
+
+  if (!IS_FREE(x, y))
+  {
+    int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
+                 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
+                 ACTION_BREAKING);
+
+    /* no element can dig solid indestructible elements */
+    if (IS_INDESTRUCTIBLE(element) &&
+       !IS_DIGGABLE(element) &&
+       !IS_COLLECTIBLE(element))
+      return FALSE;
+
+    if (AmoebaNr[x][y] &&
+       (element == EL_AMOEBA_FULL ||
+        element == EL_BD_AMOEBA ||
+        element == EL_AMOEBA_GROWING))
+    {
+      AmoebaCnt[AmoebaNr[x][y]]--;
+      AmoebaCnt2[AmoebaNr[x][y]]--;
+    }
+
+    if (IS_MOVING(x, y))
+      RemoveMovingField(x, y);
+    else
+    {
+      RemoveField(x, y);
+      TEST_DrawLevelField(x, y);
+    }
+
+    /* if digged element was about to explode, prevent the explosion */
+    ExplodeField[x][y] = EX_TYPE_NONE;
 
-    return MF_NO_ACTION;
+    PlayLevelSoundAction(x, y, action);
   }
 
-  player->push_delay = -1;
+  Store[x][y] = EL_EMPTY;
 
-  if (is_player)               /* function can also be called by EL_PENGUIN */
+#if 1
+  /* this makes it possible to leave the removed element again */
+  if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
+    Store[x][y] = element;
+#else
+  if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
   {
-    if (Feld[x][y] != element)         /* really digged/collected something */
-      player->is_collecting = !player->is_digging;
+    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 MF_MOVING;
+  return TRUE;
 }
 
-boolean SnapField(struct PlayerInfo *player, int dx, int dy)
+static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 {
   int jx = player->jx, jy = player->jy;
   int x = jx + dx, y = jy + dy;
@@ -10811,6 +15868,8 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
                        dx == +1 ? MV_RIGHT :
                        dy == -1 ? MV_UP    :
                        dy == +1 ? MV_DOWN  : MV_NONE);
+  boolean can_continue_snapping = (level.continuous_snapping &&
+                                  WasJustFalling[x][y] < CHECK_DELAY_FALLING);
 
   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
     return FALSE;
@@ -10838,8 +15897,14 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
     return FALSE;
   }
 
+#if USE_NEW_CONTINUOUS_SNAPPING
+  /* prevent snapping with already pressed snap key when not allowed */
+  if (player->is_snapping && !can_continue_snapping)
+    return FALSE;
+#else
   if (player->is_snapping)
     return FALSE;
+#endif
 
   player->MovDir = snap_direction;
 
@@ -10851,11 +15916,14 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   }
 
   player->is_dropping = FALSE;
+  player->is_dropping_pressed = FALSE;
+  player->drop_pressed_delay = 0;
 
-  if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
+  if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
     return FALSE;
 
   player->is_snapping = TRUE;
+  player->is_active = TRUE;
 
   if (player->MovPos == 0)
   {
@@ -10865,19 +15933,22 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   }
 
   if (player->MovPos != 0)     /* prevent graphic bugs in versions < 2.2.0 */
-    DrawLevelField(player->last_jx, player->last_jy);
+    TEST_DrawLevelField(player->last_jx, player->last_jy);
 
-  DrawLevelField(x, y);
+  TEST_DrawLevelField(x, y);
 
   return TRUE;
 }
 
-boolean DropElement(struct PlayerInfo *player)
+static boolean DropElement(struct PlayerInfo *player)
 {
   int old_element, new_element;
   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 ?
@@ -10885,13 +15956,16 @@ boolean DropElement(struct PlayerInfo *player)
                      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)
-    return MF_ACTION;
+    return MP_ACTION;
 
   if (IS_THROWABLE(drop_element))
   {
@@ -10913,6 +15987,10 @@ boolean DropElement(struct PlayerInfo *player)
   if (new_element == EL_UNDEFINED)
     return FALSE;
 
+  /* 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 */
   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
     return FALSE;
@@ -10934,10 +16012,12 @@ boolean DropElement(struct PlayerInfo *player)
     {
       player->inventory_size--;
 
-      DrawGameValue_Dynamite(local_player->inventory_size);
+      DrawGameDoorValues();
 
       if (new_element == EL_DYNAMITE)
        new_element = EL_DYNAMITE_ACTIVE;
+      else if (new_element == EL_EM_DYNAMITE)
+       new_element = EL_EM_DYNAMITE_ACTIVE;
       else if (new_element == EL_SP_DISK_RED)
        new_element = EL_SP_DISK_RED_ACTIVE;
     }
@@ -10982,22 +16062,36 @@ boolean DropElement(struct PlayerInfo *player)
   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 0
+    int move_direction;
+    int nextx, nexty;
+#endif
 
     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
       MovDir[dropx][dropy] = drop_direction;
 
+#if 0
     move_direction = MovDir[dropx][dropy];
     nextx = dropx + GET_DX_FROM_DIR(move_direction);
     nexty = dropy + GET_DY_FROM_DIR(move_direction);
+#endif
 
     ChangeCount[dropx][dropy] = 0;     /* allow at least one more change */
-    CheckCollision[dropx][dropy] = 2;
+
+#if USE_FIX_IMPACT_COLLISION
+    /* do not cause impact style collision by dropping elements that can fall */
+    CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
+#else
+    CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
+#endif
   }
 
   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
   player->is_dropping = TRUE;
 
+  player->drop_pressed_delay = 0;
+  player->is_dropping_pressed = FALSE;
+
   player->drop_x = dropx;
   player->drop_y = dropy;
 
@@ -11122,9 +16216,12 @@ static void PlayLevelMusic()
     PlayMusic(MAP_NOCONF_MUSIC(level_nr));     /* from music dir */
 }
 
-void PlayLevelSound_EM(int x, int y, int element_em, int sample)
+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;
 
   switch (sample)
   {
@@ -11169,7 +16266,7 @@ void PlayLevelSound_EM(int x, int y, int element_em, int sample)
       break;
 
     case SAMPLE_slurp:
-      PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
+      PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
       break;
 
     case SAMPLE_eater:
@@ -11270,7 +16367,7 @@ void PlayLevelSound_EM(int x, int y, int element_em, int sample)
       break;
 
     case SAMPLE_time:
-      PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+      PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
       break;
 
     default:
@@ -11279,16 +16376,37 @@ void PlayLevelSound_EM(int x, int y, int element_em, int sample)
   }
 }
 
+void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
+{
+  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;
+
+#if 0
+  printf("::: %d -> %d\n", element_sp, action_sp);
+#endif
+
+  PlayLevelSoundElementAction(x, y, element, action);
+}
+
 void RaiseScore(int value)
 {
   local_player->score += value;
 
+#if 1
+  game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+
+  DisplayGameControlValues();
+#else
   DrawGameValue_Score(local_player->score);
+#endif
 }
 
 void RaiseScoreElement(int element)
 {
-  switch(element)
+  switch (element)
   {
     case EL_EMERALD:
     case EL_BD_DIAMOND:
@@ -11331,6 +16449,7 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_NUT]);
       break;
     case EL_DYNAMITE:
+    case EL_EM_DYNAMITE:
     case EL_SP_DISK_RED:
     case EL_DYNABOMB_INCREASE_NUMBER:
     case EL_DYNABOMB_INCREASE_SIZE:
@@ -11356,6 +16475,7 @@ void RaiseScoreElement(int element)
     case EL_EMC_KEY_6:
     case EL_EMC_KEY_7:
     case EL_EMC_KEY_8:
+    case EL_DC_KEY_WHITE:
       RaiseScore(level.score[SC_KEY]);
       break;
     default:
@@ -11364,25 +16484,57 @@ void RaiseScoreElement(int element)
   }
 }
 
-void RequestQuitGame(boolean ask_if_really_quit)
+void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
 {
-  if (AllPlayersGone ||
-      !ask_if_really_quit ||
-      level_editor_test_game ||
-      Request("Do you really want to quit the game ?",
-             REQ_ASK | REQ_STAY_CLOSED))
+  if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
   {
+#if 1
+    /* closing door required in case of envelope style request dialogs */
+    if (!skip_request)
+      CloseDoor(DOOR_CLOSE_1);
+#endif
+
 #if defined(NETWORK_AVALIABLE)
     if (options.network)
-      SendToServer_StopPlaying();
+      SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
     else
 #endif
     {
-      game_status = GAME_MODE_MAIN;
-      DrawMainMenu();
+      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;
+
+       DrawAndFadeInMainMenu(REDRAW_FIELD);
+      }
     }
   }
-  else
+  else         /* continue playing the game */
   {
     if (tape.playing && tape.deactivate_display)
       TapeDeactivateDisplayOff(TRUE);
@@ -11394,60 +16546,325 @@ void RequestQuitGame(boolean ask_if_really_quit)
   }
 }
 
+void RequestQuitGame(boolean ask_if_really_quit)
+{
+  boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
+  boolean skip_request = AllPlayersGone || quick_quit;
 
-/* ---------- new game button stuff ---------------------------------------- */
+  RequestQuitGameExt(skip_request, quick_quit,
+                    "Do you really want to quit the game?");
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* random generator functions                                                */
+/* ------------------------------------------------------------------------- */
+
+unsigned int InitEngineRandom_RND(int seed)
+{
+  game.num_random_calls = 0;
+
+#if 0
+  unsigned int rnd_seed = InitEngineRandom(seed);
+
+  printf("::: START RND: %d\n", rnd_seed);
+
+  return rnd_seed;
+#else
+
+  return InitEngineRandom(seed);
+
+#endif
+
+}
+
+unsigned int RND(int max)
+{
+  if (max > 0)
+  {
+    game.num_random_calls++;
+
+    return GetEngineRandom(max);
+  }
+
+  return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* game engine snapshot handling functions                                   */
+/* ------------------------------------------------------------------------- */
+
+struct EngineSnapshotInfo
+{
+  /* runtime values for custom element collect score */
+  int collect_score[NUM_CUSTOM_ELEMENTS];
+
+  /* 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];
+};
+
+static struct EngineSnapshotInfo engine_snapshot_rnd;
+static char *snapshot_level_identifier = NULL;
+static int snapshot_level_nr = -1;
+
+static void SaveEngineSnapshotValues_RND()
+{
+  static int belt_base_active_element[4] =
+  {
+    EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
+    EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
+    EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
+    EL_CONVEYOR_BELT_4_LEFT_ACTIVE
+  };
+  int i, j;
+
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
 
-/* 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)
+    engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
+  }
+
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+  {
+    int element = EL_GROUP_START + i;
+
+    engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
+  }
+
+  for (i = 0; i < 4; i++)
+  {
+    for (j = 0; j < NUM_BELT_PARTS; j++)
+    {
+      int element = belt_base_active_element[i] + j;
+      int graphic = el2img(element);
+      int anim_mode = graphic_info[graphic].anim_mode;
+
+      engine_snapshot_rnd.belt_graphic[i][j] = graphic;
+      engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
+    }
+  }
+}
+
+static void LoadEngineSnapshotValues_RND()
+{
+  unsigned int num_random_calls = game.num_random_calls;
+  int i, j;
+
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+
+    element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
+  }
+
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+  {
+    int element = EL_GROUP_START + i;
+
+    element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
+  }
+
+  for (i = 0; i < 4; i++)
+  {
+    for (j = 0; j < NUM_BELT_PARTS; 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;
+    }
+  }
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+  {
+    InitRND(tape.random_seed);
+    for (i = 0; i < num_random_calls; i++)
+      RND(1);
+  }
+
+  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");
+  }
+}
+
+void SaveEngineSnapshot()
+{
+  /* do not save snapshots from editor */
+  if (level_editor_test_game)
+    return;
+
+  /* free previous snapshot buffers, if needed */
+  FreeEngineSnapshotBuffers();
+
+  /* copy some special values to a structure better suited for the snapshot */
+
+  SaveEngineSnapshotValues_RND();
+  SaveEngineSnapshotValues_EM();
+  SaveEngineSnapshotValues_SP();
+
+  /* save values stored in special snapshot structure */
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+
+  /* save further RND engine values */
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
+
+  /* save level identification information */
+
+  setString(&snapshot_level_identifier, leveldir_current->identifier);
+  snapshot_level_nr = level_nr;
+
+#if 0
+  ListNode *node = engine_snapshot_list_rnd;
+  int num_bytes = 0;
+
+  while (node != NULL)
+  {
+    num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
+
+    node = node->next;
+  }
+
+  printf("::: size of engine snapshot: %d bytes\n", num_bytes);
+#endif
+}
+
+void LoadEngineSnapshot()
+{
+  /* restore generically stored snapshot buffers */
+
+  LoadEngineSnapshotBuffers();
+
+  /* restore special values from snapshot structure */
+
+  LoadEngineSnapshotValues_RND();
+  LoadEngineSnapshotValues_EM();
+  LoadEngineSnapshotValues_SP();
+}
+
+boolean CheckEngineSnapshot()
+{
+  return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
+         snapshot_level_nr == level_nr);
+}
+
+
+/* ---------- new game button stuff ---------------------------------------- */
 
 static struct
 {
-  int x, y;
+  int graphic;
+  struct Rect *pos;
   int gadget_id;
   char *infotext;
 } gamebutton_info[NUM_GAME_BUTTONS] =
 {
   {
-    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_STOP,
-    "stop game"
+    IMG_GAME_BUTTON_GFX_STOP,          &game.button.stop,
+    GAME_CTRL_ID_STOP,                 "stop game"
+  },
+  {
+    IMG_GAME_BUTTON_GFX_PAUSE,         &game.button.pause,
+    GAME_CTRL_ID_PAUSE,                        "pause game"
   },
   {
-    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PAUSE,
-    "pause game"
+    IMG_GAME_BUTTON_GFX_PLAY,          &game.button.play,
+    GAME_CTRL_ID_PLAY,                 "play game"
   },
   {
-    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PLAY,
-    "play game"
+    IMG_GAME_BUTTON_GFX_SOUND_MUSIC,   &game.button.sound_music,
+    SOUND_CTRL_ID_MUSIC,               "background music on/off"
   },
   {
-    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_MUSIC,
-    "background music on/off"
+    IMG_GAME_BUTTON_GFX_SOUND_LOOPS,   &game.button.sound_loops,
+    SOUND_CTRL_ID_LOOPS,               "sound loops on/off"
   },
   {
-    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_LOOPS,
-    "sound loops on/off"
+    IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,  &game.button.sound_simple,
+    SOUND_CTRL_ID_SIMPLE,              "normal sounds on/off"
   },
   {
-    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_SIMPLE,
-    "normal sounds on/off"
+    IMG_GAME_BUTTON_GFX_SAVE,          &game.button.save,
+    GAME_CTRL_ID_SAVE,                 "save game"
+  },
+  {
+    IMG_GAME_BUTTON_GFX_LOAD,          &game.button.load,
+    GAME_CTRL_ID_LOAD,                 "load game"
   }
 };
 
@@ -11457,29 +16874,40 @@ void CreateGameButtons()
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
   {
-    Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+    struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
+    struct Rect *pos = gamebutton_info[i].pos;
     struct GadgetInfo *gi;
     int button_type;
     boolean checked;
-    unsigned long event_mask;
-    int gd_xoffset, gd_yoffset;
-    int gd_x1, gd_x2, gd_y1, gd_y2;
+    unsigned int event_mask;
+    int base_x = (tape.show_game_buttons ? VX : DX);
+    int base_y = (tape.show_game_buttons ? VY : DY);
+    int gd_x   = gfx->src_x;
+    int gd_y   = gfx->src_y;
+    int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
+    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 id = i;
 
-    gd_xoffset = gamebutton_info[i].x;
-    gd_yoffset = gamebutton_info[i].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_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
     {
@@ -11489,23 +16917,22 @@ void CreateGameButtons()
         (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
         (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : 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_INFO_TEXT, gamebutton_info[i].infotext,
-                     GDI_X, DX + gd_xoffset,
-                     GDI_Y, DY + gd_yoffset,
-                     GDI_WIDTH, GAME_BUTTON_XSIZE,
-                     GDI_HEIGHT, GAME_BUTTON_YSIZE,
+                     GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
+                     GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
+                     GDI_WIDTH, gfx->width,
+                     GDI_HEIGHT, gfx->height,
                      GDI_TYPE, button_type,
                      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_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleGameButtons,
                      GDI_END);
@@ -11525,7 +16952,7 @@ void FreeGameButtons()
     FreeGadget(game_gadget[i]);
 }
 
-static void MapGameButtons()
+void MapGameButtons()
 {
   int i;
 
@@ -11541,21 +16968,38 @@ void UnmapGameButtons()
     UnmapGadget(game_gadget[i]);
 }
 
-static void HandleGameButtons(struct GadgetInfo *gi)
+void RedrawGameButtons()
+{
+  int i;
+
+  for (i = 0; i < NUM_GAME_BUTTONS; i++)
+    RedrawGadget(game_gadget[i]);
+}
+
+static void HandleGameButtonsExt(int id)
 {
-  int id = gi->custom_id;
+  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:
-      RequestQuitGame(TRUE);
+      if (game_status == GAME_MODE_MAIN)
+       break;
+
+      if (tape.playing)
+       TapeStop();
+      else
+       RequestQuitGame(TRUE);
+
       break;
 
     case GAME_CTRL_ID_PAUSE:
-      if (options.network)
+      if (options.network && game_status == GAME_MODE_PLAYING)
       {
 #if defined(NETWORK_AVALIABLE)
        if (tape.pausing)
@@ -11569,7 +17013,11 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       break;
 
     case GAME_CTRL_ID_PLAY:
-      if (tape.pausing)
+      if (game_status == GAME_MODE_MAIN)
+      {
+        StartGameActions(options.network, setup.autorecord, level.random_seed);
+      }
+      else if (tape.pausing)
       {
 #if defined(NETWORK_AVALIABLE)
        if (options.network)
@@ -11578,7 +17026,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
 #endif
        {
          tape.pausing = FALSE;
-         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
        }
       }
       break;
@@ -11587,6 +17035,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       if (setup.sound_music)
       { 
        setup.sound_music = FALSE;
+
        FadeMusic();
       }
       else if (audio.music_available)
@@ -11605,6 +17054,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       else if (audio.loops_available)
       {
        setup.sound = setup.sound_loops = TRUE;
+
        SetAudioMode(setup.sound);
       }
       break;
@@ -11615,11 +17065,44 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       else if (audio.sound_available)
       {
        setup.sound = setup.sound_simple = TRUE;
+
        SetAudioMode(setup.sound);
       }
       break;
 
+    case GAME_CTRL_ID_SAVE:
+      TapeQuickSave();
+      break;
+
+    case GAME_CTRL_ID_LOAD:
+      TapeQuickLoad();
+      break;
+
     default:
       break;
   }
 }
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+  HandleGameButtonsExt(gi->custom_id);
+}
+
+void HandleSoundButtonKeys(Key key)
+{
+#if 1
+  if (key == setup.shortcut.sound_simple)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
+  else if (key == setup.shortcut.sound_loops)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
+  else if (key == setup.shortcut.sound_music)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
+#else
+  if (key == setup.shortcut.sound_simple)
+    HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
+  else if (key == setup.shortcut.sound_loops)
+    HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
+  else if (key == setup.shortcut.sound_music)
+    HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
+#endif
+}