rnd-20070331-1-src
[rocksndiamonds.git] / src / game.c
index 8fdc17e2f81893af4fb7da2d2f07f6e5141d40e8..d580212fd8e725218ce9cb3cdc68ceff033ecbbc 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                            *
 #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_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
+
+
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
 #define EX_TYPE_DYNA           (1 << 4)
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
+#define PANEL_OFF()            (local_player->LevelSolved_PanelOff)
+#define        PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
+#define PANEL_XPOS(p)          (DX + ALIGNED_TEXT_XPOS(p))
+#define PANEL_YPOS(p)          (DY + ALIGNED_TEXT_YPOS(p))
+
 /* special positions in the game control window (relative to control window) */
-#define XX_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
+#define XX_LEVEL1              (PANEL_XPOS(game.panel.level))
+#define XX_LEVEL2              (PANEL_XPOS(game.panel.level) - 1)
+#define XX_LEVEL               (PANEL_XPOS(game.panel.level))
+#define YY_LEVEL               (PANEL_YPOS(game.panel.level))
+#define XX_EMERALDS            (PANEL_XPOS(game.panel.gems))
+#define YY_EMERALDS            (PANEL_YPOS(game.panel.gems))
+#define XX_DYNAMITE            (PANEL_XPOS(game.panel.inventory))
+#define YY_DYNAMITE            (PANEL_YPOS(game.panel.inventory))
+#define XX_KEYS                        (PANEL_XPOS(game.panel.keys))
+#define YY_KEYS                        (PANEL_YPOS(game.panel.keys))
+#define XX_SCORE               (PANEL_XPOS(game.panel.score))
+#define YY_SCORE               (PANEL_YPOS(game.panel.score))
+#define XX_TIME1               (PANEL_XPOS(game.panel.time))
+#define XX_TIME2               (PANEL_XPOS(game.panel.time) + 1)
+#define XX_TIME                        (PANEL_XPOS(game.panel.time))
+#define YY_TIME                        (PANEL_YPOS(game.panel.time))
 
 /* special positions in the game control window (relative to main window) */
+#define DX_LEVEL1              (DX + XX_LEVEL1)
+#define DX_LEVEL2              (DX + XX_LEVEL2)
 #define DX_LEVEL               (DX + XX_LEVEL)
 #define DY_LEVEL               (DY + YY_LEVEL)
 #define DX_EMERALDS            (DX + XX_EMERALDS)
 #define DY_SCORE               (DY + YY_SCORE)
 #define DX_TIME1               (DX + XX_TIME1)
 #define DX_TIME2               (DX + XX_TIME2)
+#define DX_TIME                        (DX + XX_TIME)
 #define DY_TIME                        (DY + YY_TIME)
 
+#if 1
+/* game panel display and control definitions */
+
+#define GAME_CONTROL_LEVEL_NUMBER              0
+#define GAME_CONTROL_GEMS                      1
+#define GAME_CONTROL_INVENTORY                 2
+#define GAME_CONTROL_KEY_1                     3
+#define GAME_CONTROL_KEY_2                     4
+#define GAME_CONTROL_KEY_3                     5
+#define GAME_CONTROL_KEY_4                     6
+#define GAME_CONTROL_KEY_5                     7
+#define GAME_CONTROL_KEY_6                     8
+#define GAME_CONTROL_KEY_7                     9
+#define GAME_CONTROL_KEY_8                     10
+#define GAME_CONTROL_KEY_WHITE                 11
+#define GAME_CONTROL_KEY_WHITE_COUNT           12
+#define GAME_CONTROL_SCORE                     13
+#define GAME_CONTROL_TIME                      14
+#define GAME_CONTROL_TIME_HH                   15
+#define GAME_CONTROL_TIME_MM                   16
+#define GAME_CONTROL_TIME_SS                   17
+#define GAME_CONTROL_DROP_NEXT_1               18
+#define GAME_CONTROL_DROP_NEXT_2               19
+#define GAME_CONTROL_DROP_NEXT_3               20
+#define GAME_CONTROL_DROP_NEXT_4               21
+#define GAME_CONTROL_DROP_NEXT_5               22
+#define GAME_CONTROL_DROP_NEXT_6               23
+#define GAME_CONTROL_DROP_NEXT_7               24
+#define GAME_CONTROL_DROP_NEXT_8               25
+#define GAME_CONTROL_SHIELD_NORMAL             26
+#define GAME_CONTROL_SHIELD_NORMAL_TIME                27
+#define GAME_CONTROL_SHIELD_DEADLY             28
+#define GAME_CONTROL_SHIELD_DEADLY_TIME                29
+#define GAME_CONTROL_EXIT                      30
+#define GAME_CONTROL_EM_EXIT                   31
+#define GAME_CONTROL_SP_EXIT                   32
+#define GAME_CONTROL_STEEL_EXIT                        33
+#define GAME_CONTROL_EM_STEEL_EXIT             34
+#define GAME_CONTROL_EMC_MAGIC_BALL            35
+#define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH     36
+#define GAME_CONTROL_LIGHT_SWITCH              37
+#define GAME_CONTROL_LIGHT_SWITCH_TIME         38
+#define GAME_CONTROL_TIMEGATE_SWITCH           39
+#define GAME_CONTROL_TIMEGATE_SWITCH_TIME      40
+#define GAME_CONTROL_SWITCHGATE_SWITCH         41
+#define GAME_CONTROL_EMC_LENSES                        42
+#define GAME_CONTROL_EMC_LENSES_TIME           43
+#define GAME_CONTROL_EMC_MAGNIFIER             44
+#define GAME_CONTROL_EMC_MAGNIFIER_TIME                45
+#define GAME_CONTROL_BALLOON_SWITCH            46
+#define GAME_CONTROL_DYNABOMB_NUMBER           47
+#define GAME_CONTROL_DYNABOMB_SIZE             48
+#define GAME_CONTROL_DYNABOMB_POWER            49
+#define GAME_CONTROL_PENGUINS                  50
+#define GAME_CONTROL_SOKOBAN_OBJECTS           51
+#define GAME_CONTROL_SOKOBAN_FIELDS            52
+#define GAME_CONTROL_ROBOT_WHEEL               53
+#define GAME_CONTROL_CONVEYOR_BELT_1           54
+#define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH    55
+#define GAME_CONTROL_CONVEYOR_BELT_2           56
+#define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH    57
+#define GAME_CONTROL_CONVEYOR_BELT_3           58
+#define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH    59
+#define GAME_CONTROL_CONVEYOR_BELT_4           60
+#define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH    61
+#define GAME_CONTROL_MAGIC_WALL                        62
+#define GAME_CONTROL_MAGIC_WALL_TIME           63
+#define GAME_CONTROL_BD_MAGIC_WALL             64
+#define GAME_CONTROL_DC_MAGIC_WALL             65
+#define GAME_CONTROL_PLAYER_NAME               66
+#define GAME_CONTROL_LEVEL_NAME                        67
+#define GAME_CONTROL_LEVEL_AUTHOR              68
+
+#define NUM_GAME_CONTROLS                      69
+
+int game_control_value[NUM_GAME_CONTROLS];
+int last_game_control_value[NUM_GAME_CONTROLS];
+
+struct GameControlInfo
+{
+  int nr;
+
+  struct TextPosInfo *pos;
+  int type;
+};
+
+static struct GameControlInfo game_controls[] =
+{
+  {
+    GAME_CONTROL_LEVEL_NUMBER,
+    &game.panel.level_number,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_GEMS,
+    &game.panel.gems,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_INVENTORY,
+    &game.panel.inventory,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_KEY_1,
+    &game.panel.key[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_2,
+    &game.panel.key[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_3,
+    &game.panel.key[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_4,
+    &game.panel.key[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_5,
+    &game.panel.key[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_6,
+    &game.panel.key[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_7,
+    &game.panel.key[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_8,
+    &game.panel.key[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_WHITE,
+    &game.panel.key_white,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_WHITE_COUNT,
+    &game.panel.key_white_count,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SCORE,
+    &game.panel.score,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIME,
+    &game.panel.time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIME_HH,
+    &game.panel.time_hh,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIME_MM,
+    &game.panel.time_mm,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIME_SS,
+    &game.panel.time_ss,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_1,
+    &game.panel.drop_next_1,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_2,
+    &game.panel.drop_next_2,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_3,
+    &game.panel.drop_next_3,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_4,
+    &game.panel.drop_next_4,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_5,
+    &game.panel.drop_next_5,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_6,
+    &game.panel.drop_next_6,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_7,
+    &game.panel.drop_next_7,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_8,
+    &game.panel.drop_next_8,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_SHIELD_NORMAL,
+    &game.panel.shield_normal,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_SHIELD_NORMAL_TIME,
+    &game.panel.shield_normal_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SHIELD_DEADLY,
+    &game.panel.shield_deadly,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_SHIELD_DEADLY_TIME,
+    &game.panel.shield_deadly_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_EXIT,
+    &game.panel.exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EM_EXIT,
+    &game.panel.em_exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_SP_EXIT,
+    &game.panel.sp_exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_STEEL_EXIT,
+    &game.panel.steel_exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EM_STEEL_EXIT,
+    &game.panel.em_steel_exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_MAGIC_BALL,
+    &game.panel.emc_magic_ball,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
+    &game.panel.emc_magic_ball_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_LIGHT_SWITCH,
+    &game.panel.light_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_LIGHT_SWITCH_TIME,
+    &game.panel.light_switch_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIMEGATE_SWITCH,
+    &game.panel.timegate_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_TIMEGATE_SWITCH_TIME,
+    &game.panel.timegate_switch_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SWITCHGATE_SWITCH,
+    &game.panel.switchgate_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_LENSES,
+    &game.panel.emc_lenses,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_LENSES_TIME,
+    &game.panel.emc_lenses_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_EMC_MAGNIFIER,
+    &game.panel.emc_magnifier,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_MAGNIFIER_TIME,
+    &game.panel.emc_magnifier_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_BALLOON_SWITCH,
+    &game.panel.balloon_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DYNABOMB_NUMBER,
+    &game.panel.dynabomb_number,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_DYNABOMB_SIZE,
+    &game.panel.dynabomb_size,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_DYNABOMB_POWER,
+    &game.panel.dynabomb_power,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_PENGUINS,
+    &game.panel.penguins,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SOKOBAN_OBJECTS,
+    &game.panel.sokoban_objects,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SOKOBAN_FIELDS,
+    &game.panel.sokoban_fields,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_ROBOT_WHEEL,
+    &game.panel.robot_wheel,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_1,
+    &game.panel.conveyor_belt_1,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
+    &game.panel.conveyor_belt_1_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_2,
+    &game.panel.conveyor_belt_2,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
+    &game.panel.conveyor_belt_2_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_3,
+    &game.panel.conveyor_belt_3,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
+    &game.panel.conveyor_belt_3_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_4,
+    &game.panel.conveyor_belt_4,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
+    &game.panel.conveyor_belt_4_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_MAGIC_WALL,
+    &game.panel.magic_wall,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_MAGIC_WALL_TIME,
+    &game.panel.magic_wall_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_BD_MAGIC_WALL,
+    &game.panel.bd_magic_wall,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DC_MAGIC_WALL,
+    &game.panel.dc_magic_wall,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_PLAYER_NAME,
+    &game.panel.player_name,
+    TYPE_STRING,
+  },
+  {
+    GAME_CONTROL_LEVEL_NAME,
+    &game.panel.level_name,
+    TYPE_STRING,
+  },
+  {
+    GAME_CONTROL_LEVEL_AUTHOR,
+    &game.panel.level_author,
+    TYPE_STRING,
+  },
+
+  {
+    -1,
+    NULL,
+    -1,
+  }
+};
+#endif
+
+
+/* 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
 #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 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 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        INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
 
 #define GET_NEW_PUSH_DELAY(e)  (   (element_info[e].push_delay_fixed) + \
                                 RND(element_info[e].push_delay_random))
                                 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) +\
+#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))
 
-#if 1
+
 #define GET_VALID_RUNTIME_ELEMENT(e)                                   \
         ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
-#else
-#define GET_VALID_FILE_ELEMENT(e)                                      \
-       ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
-#endif
 
-#define GET_TARGET_ELEMENT(e, ch)                                      \
-       ((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))
+#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_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))
        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 ||\
+       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)
        (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
                                Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
 
-#if 0
-#define GROUP_NR(e)            ((e) - EL_GROUP_START)
-#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))
-#endif
-
 #define MOVE_ENTER_EL(e)       (element_info[e].move_enter_element)
 
 #define CE_ENTER_FIELD_COND(e, x, y)                                   \
@@ -327,8 +804,63 @@ static void PlayLevelMusic();
 static void MapGameButtons();
 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 KillPlayer(struct PlayerInfo *);
+void BuryPlayer(struct PlayerInfo *);
+void RemovePlayer(struct PlayerInfo *);
+
+boolean SnapField(struct PlayerInfo *, int, int);
+boolean DropElement(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;
+
 
 /* ------------------------------------------------------------------------- */
 /* definition of elements that automatically change to other elements after  */
@@ -353,8 +885,6 @@ static void RunTimegateWheel(int, int);
 static void InitMagicBallDelay(int, int);
 static void ActivateMagicBall(int, int);
 
-static void InitDiagonalMovingElement(int, int);
-
 struct ChangingElementInfo
 {
   int element;
@@ -399,6 +929,62 @@ static struct ChangingElementInfo change_delay_list[] =
     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,
@@ -520,6 +1106,14 @@ static struct ChangingElementInfo change_delay_list[] =
     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,
@@ -550,7 +1144,7 @@ static struct ChangingElementInfo change_delay_list[] =
     0,
     NULL,
     NULL,
-    InitDiagonalMovingElement
+    NULL,
   },
 
   {
@@ -592,10 +1186,14 @@ move_stepsize_list[] =
   { 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_BD_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 },
 };
@@ -682,7 +1280,19 @@ static int playfield_scan_delta_y = 1;
                                     (y) += playfield_scan_delta_y)     \
                                for ((x) = playfield_scan_start_x;      \
                                     (x) >= 0 && (x) <= lev_fieldx - 1; \
-                                    (x) += playfield_scan_delta_x)     \
+                                    (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()
 {
@@ -726,6 +1336,7 @@ static int get_move_delay_from_stepsize(int 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);
 
@@ -737,8 +1348,8 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
 
   if (init_game)
   {
-    player->move_delay       = game.initial_move_delay;
-    player->move_delay_value = game.initial_move_delay_value;
+    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;
 
@@ -748,6 +1359,8 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
 
 void GetPlayerConfig()
 {
+  GameFrameDelay = setup.game_frame_delay;
+
   if (!audio.sound_available)
     setup.sound_simple = FALSE;
 
@@ -766,60 +1379,35 @@ void GetPlayerConfig()
   InitJoysticks();
 }
 
-static int getBeltNrFromBeltElement(int element)
+int GetElementFromGroupElement(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);
-}
+  if (IS_GROUP_ELEMENT(element))
+  {
+    struct ElementGroupInfo *group = element_info[element].group;
+    int last_anim_random_frame = gfx.anim_random_frame;
+    int element_pos;
 
-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);
-}
+    if (group->choice_mode == ANIM_RANDOM)
+      gfx.anim_random_frame = RND(group->num_elements_resolved);
 
-static int getBeltNrFromBeltSwitchElement(int element)
-{
-  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);
+    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 int getBeltDirNrFromBeltSwitchElement(int element)
+static void InitPlayerField(int x, int y, int element, boolean init_game)
 {
-  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
-  };
-
-  int belt_nr = getBeltNrFromBeltSwitchElement(element);
-  int belt_dir_nr = element - belt_base_element[belt_nr];
-
-  return (belt_dir_nr % 3);
-}
-
-static int getBeltDirFromBeltSwitchElement(int element)
-{
-  static int belt_move_dir[3] =
-  {
-    MV_LEFT,
-    MV_NONE,
-    MV_RIGHT
-  };
-
-  int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
-
-  return belt_move_dir[belt_dir_nr];
-}
-
-static void InitPlayerField(int x, int y, int element, boolean init_game)
-{
-  if (element == EL_SP_MURPHY)
+  if (element == EL_SP_MURPHY)
   {
     if (init_game)
     {
@@ -832,6 +1420,9 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
       else
       {
        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;
@@ -927,41 +1518,45 @@ static void InitField(int x, int y, boolean init_game)
        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_BUG:
+    case EL_SPACESHIP:
     case EL_SPACESHIP_RIGHT:
     case EL_SPACESHIP_UP:
     case EL_SPACESHIP_LEFT:
     case EL_SPACESHIP_DOWN:
-    case EL_SPACESHIP:
+    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_BUTTERFLY:
+    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_BD_FIREFLY:
     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:
-    case EL_MOLE:
       InitMovDir(x, y);
       break;
 
@@ -1034,16 +1629,31 @@ static void InitField(int x, int y, boolean init_game)
       }
       break;
 
+#if !USE_BOTH_SWITCHGATE_SWITCHES
     case EL_SWITCHGATE_SWITCH_DOWN:    /* always start with same switch pos */
       if (init_game)
        Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
       break;
 
+    case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
+      if (init_game)
+       Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
+      break;
+#endif
+
     case EL_LIGHT_SWITCH_ACTIVE:
       if (init_game)
        game.light_time_left = level.time_light * FRAMES_PER_SECOND;
       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;
@@ -1055,7 +1665,6 @@ static void InitField(int x, int y, boolean init_game)
       break;
 
     default:
-#if 1
       if (IS_CUSTOM_ELEMENT(element))
       {
        if (CAN_MOVE(element))
@@ -1063,56 +1672,21 @@ static void InitField(int x, int y, boolean init_game)
 
 #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]);
+         CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
 #endif
       }
-#else
-      if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
-       InitMovDir(x, y);
-#endif
       else 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++;
-
-       Feld[x][y] = group->element_resolved[element_pos];
+       Feld[x][y] = GetElementFromGroupElement(element);
 
        InitField(x, y, init_game);
       }
+
       break;
   }
 
-#if 1
   if (!init_game)
     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
-#endif
-
-#if 0
-
-#if USE_NEW_CUSTOM_VALUE
-
-#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
-
-#endif
-
-#endif
 }
 
 static inline void InitField_WithBug1(int x, int y, boolean init_game)
@@ -1146,221 +1720,800 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
   */
 }
 
-inline void DrawGameValue_Emeralds(int value)
-{
-  DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
-}
-
-inline void DrawGameValue_Dynamite(int value)
-{
-  DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
-}
+#if 1
 
-inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+void InitGameControlValues()
 {
   int i;
 
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
+  for (i = 0; i < NUM_GAME_CONTROLS; i++)
+    game_control_value[i] = last_game_control_value[i] = -1;
+
+  for (i = 0; game_controls[i].nr != -1; i++)
   {
-    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);
+    int nr = game_controls[i].nr;
+    int type = game_controls[i].type;
+    struct TextPosInfo *pos = game_controls[i].pos;
+
+    game_control_value[nr] = last_game_control_value[nr] = -1;
+
+    /* determine panel value width for later calculation of alignment */
+    if (type == TYPE_INTEGER || type == TYPE_STRING)
+      pos->width = pos->chars * getFontWidth(pos->font);
+    else if (type == TYPE_ELEMENT)
+      pos->width = MINI_TILESIZE;
   }
 }
 
-inline void DrawGameValue_Score(int value)
+void UpdateGameControlValues()
 {
-  DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
-}
+  int i, j;
 
-inline void DrawGameValue_Time(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);
-}
+  game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
+  game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
 
-inline void DrawGameValue_Level(int value)
-{
-  if (level_nr < 100)
-    DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
-  else
+  game_control_value[GAME_CONTROL_INVENTORY] = 0;
+  for (i = 0; i < MAX_NUM_KEYS; i++)
+    game_control_value[GAME_CONTROL_KEY_1 + i] = 0;
+  game_control_value[GAME_CONTROL_KEY_WHITE] = 0;
+  game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
+
+  if (game.centered_player_nr == -1)
   {
-    /* misuse area for displaying emeralds to draw bigger level number */
-    DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
-               int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      for (j = 0; j < MAX_NUM_KEYS; j++)
+       if (stored_player[i].key[j])
+         game_control_value[GAME_CONTROL_KEY_1 + j] = 1;
 
-    /* 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);
+      game_control_value[GAME_CONTROL_INVENTORY] +=
+       stored_player[i].inventory_size;
 
-    /* restore the area for displaying emeralds */
-    DrawGameValue_Emeralds(local_player->gems_still_needed);
+      if (stored_player[i].num_white_keys > 0)
+       game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
 
-    /* yes, this is all really ugly :-) */
+      game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
+       stored_player[i].num_white_keys;
+    }
   }
-}
+  else
+  {
+    int player_nr = game.centered_player_nr;
 
-void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
-                      int key_bits)
-{
-  int key[MAX_NUM_KEYS];
-  int i;
+    for (i = 0; i < MAX_NUM_KEYS; i++)
+      if (stored_player[player_nr].key[i])
+       game_control_value[GAME_CONTROL_KEY_1 + i] = 1;
 
-  for (i = 0; i < MAX_NUM_KEYS; i++)
-    key[i] = key_bits & (1 << i);
+    game_control_value[GAME_CONTROL_INVENTORY] +=
+      stored_player[player_nr].inventory_size;
 
-  DrawGameValue_Level(level_nr);
+    if (stored_player[player_nr].num_white_keys > 0)
+      game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
 
-  DrawGameValue_Emeralds(emeralds);
-  DrawGameValue_Dynamite(dynamite);
-  DrawGameValue_Score(score);
-  DrawGameValue_Time(time);
+    game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
+      stored_player[player_nr].num_white_keys;
+  }
 
-  DrawGameValue_Keys(key);
-}
+  game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
+                                           local_player->score_final :
+                                           local_player->score);
 
-void DrawGameDoorValues()
-{
-  int i;
+  game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
+                                          TimePlayed :
+                                          TimeLeft);
 
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-  {
-    DrawGameDoorValues_EM();
+  game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
+  game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
+  game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
 
-    return;
+  for (i = 0; i < 8; i++)
+    game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
+    (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
+     EL_EMPTY);
+  game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
+    local_player->shield_normal_time_left;
+  game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
+    (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
+     EL_EMPTY);
+  game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
+    local_player->shield_deadly_time_left;
+
+  if (local_player->gems_still_needed > 0 ||
+      local_player->sokobanfields_still_needed > 0 ||
+      local_player->lights_still_needed > 0)
+  {
+    game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_CLOSED;
+    game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_CLOSED;
+    game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_CLOSED;
+    game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_CLOSED;
+    game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
+  }
+  else
+  {
+    game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_OPEN;
+    game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_OPEN;
+    game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_OPEN;
+    game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_OPEN;
+    game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
   }
 
-  DrawGameValue_Level(level_nr);
+  game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
 
-  DrawGameValue_Emeralds(local_player->gems_still_needed);
-  DrawGameValue_Dynamite(local_player->inventory_size);
-  DrawGameValue_Score(local_player->score);
-  DrawGameValue_Time(TimeLeft);
+  game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
+    (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
+  game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
 
-  for (i = 0; i < MAX_PLAYERS; i++)
-    DrawGameValue_Keys(stored_player[i].key);
+  game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
+    (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
+  game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
+    game.timegate_time_left;
+
+  game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_EMC_LENSES] =
+    (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
+  game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
+
+  game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
+    (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
+  game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
+
+  game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
+    (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_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
+    local_player->dynabomb_count;
+  game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
+    local_player->dynabomb_size;
+  game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
+    (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
+
+  game_control_value[GAME_CONTROL_PENGUINS] =
+    local_player->friends_still_needed;
+
+  game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
+    local_player->sokobanfields_still_needed;
+  game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
+    local_player->sokobanfields_still_needed;
+
+  game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
+    game.magic_wall_time_left;
+  game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
+  game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
+  game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
 }
 
-#if 0
-static void resolve_group_element(int group_element, int recursion_depth)
+void DisplayGameControlValues()
 {
-  static int group_nr;
-  static struct ElementGroupInfo *group;
-  struct ElementGroupInfo *actual_group = element_info[group_element].group;
   int i;
 
-  if (recursion_depth > NUM_GROUP_ELEMENTS)    /* recursion too deep */
+  for (i = 0; game_controls[i].nr != -1; i++)
   {
-    Error(ERR_WARN, "recursion too deep when resolving group element %d",
-         group_element - EL_GROUP_START + 1);
+    int nr = game_controls[i].nr;
+    int type = game_controls[i].type;
+    struct TextPosInfo *pos = game_controls[i].pos;
+    int value = game_control_value[nr];
+    int last_value = last_game_control_value[nr];
+    int chars = pos->chars;
+    int font = pos->font;
 
-    /* replace element which caused too deep recursion by question mark */
-    group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
+    if (value == last_value)
+      continue;
 
-    return;
-  }
+    last_game_control_value[nr] = value;
 
-  if (recursion_depth == 0)                    /* initialization */
-  {
-    group = element_info[group_element].group;
-    group_nr = group_element - EL_GROUP_START;
+#if 0
+    printf("::: value %d changed from %d to %d\n", nr, last_value, value);
+#endif
 
-    group->num_elements_resolved = 0;
-    group->choice_pos = 0;
-  }
+    if (PANEL_DEACTIVATED(pos))
+      continue;
 
-  for (i = 0; i < actual_group->num_elements; i++)
-  {
-    int element = actual_group->element[i];
+    if (type == TYPE_INTEGER)
+    {
+      if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
+      {
+       boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
 
-    if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
-      break;
+       if (use_dynamic_chars)          /* use dynamic number of chars */
+       {
+         int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
+         int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
+         int chars2 = chars1 + 1;
+         int font1 = pos->font;
+         int font2 = pos->font_alt;
 
-    if (IS_GROUP_ELEMENT(element))
-      resolve_group_element(element, recursion_depth + 1);
-    else
+         chars = (value < value_change ? chars1 : chars2);
+         font  = (value < value_change ? font1  : font2);
+
+         /* clear background if value just changed its size (dynamic chars) */
+         if ((last_value < value_change) != (value < value_change))
+         {
+           int width1 = chars1 * getFontWidth(font1);
+           int width2 = chars2 * 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);
+         }
+       }
+
+       pos->width = chars * getFontWidth(font);
+      }
+
+      DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
+    }
+    else if (type == TYPE_ELEMENT)
+    {
+      if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8)
+      {
+       int key_nr = nr - GAME_CONTROL_KEY_1;
+       int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX;
+       int src_y = DOOR_GFX_PAGEY1 + 123;
+       int dst_x = PANEL_XPOS(pos);
+       int dst_y = PANEL_YPOS(pos);
+       int 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) + key_nr;
+       int graphic = el2edimg(element);
+
+       if (value)
+         DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
+       else
+         BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+                    MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+      }
+      else if (value != EL_UNDEFINED)
+      {
+       int graphic = el2edimg(value);
+       int dst_x = PANEL_XPOS(pos);
+       int dst_y = PANEL_YPOS(pos);
+
+       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
+      }
+    }
+    else if (type == TYPE_STRING)
     {
-      group->element_resolved[group->num_elements_resolved++] = element;
-      element_info[element].in_group[group_nr] = TRUE;
+      char *s = (nr == GAME_CONTROL_PLAYER_NAME  ? setup.player_name :
+                nr == GAME_CONTROL_LEVEL_NAME   ? level.name :
+                nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
+
+      if (s != NULL)
+      {
+       char *s_cut = getStringCopyN(s, pos->chars);
+
+       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
+
+       free(s_cut);
+      }
     }
+
+    redraw_mask |= REDRAW_DOOR_1;
   }
 }
+
+void DrawGameValue_Emeralds(int value)
+{
+  struct TextPosInfo *pos = &game.panel.gems;
+#if 1
+  int font_nr = pos->font;
+#else
+  int font_nr = FONT_TEXT_2;
 #endif
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->chars;
 
-/*
-  =============================================================================
-  InitGameEngine()
-  -----------------------------------------------------------------------------
-  initialize game engine due to level / tape version number
-  =============================================================================
-*/
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-static void InitGameEngine()
+  if (PANEL_DEACTIVATED(pos))
+    return;
+
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
+
+void DrawGameValue_Dynamite(int value)
 {
-  int i, j, k, l, x, y;
+  struct TextPosInfo *pos = &game.panel.inventory;
+#if 1
+  int font_nr = pos->font;
+#else
+  int font_nr = FONT_TEXT_2;
+#endif
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->chars;
 
-  /* set game engine from tape file when re-playing, else from level file */
-  game.engine_version = (tape.playing ? tape.engine_version :
-                        level.game_version);
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-  /* ---------------------------------------------------------------------- */
-  /* set flags for bugs and changes according to active game engine version */
-  /* ---------------------------------------------------------------------- */
+  if (PANEL_DEACTIVATED(pos))
+    return;
 
-  /*
-    Summary of bugfix/change:
-    Fixed handling for custom elements that change when pushed by the player.
+  pos->width = chars * font_width;
 
-    Fixed/changed in version:
-    3.1.0
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
 
-    Description:
-    Before 3.1.0, custom elements that "change when pushing" changed directly
-    after the player started pushing them (until then handled in "DigField()").
-    Since 3.1.0, these custom elements are not changed until the "pushing"
-    move of the element is finished (now handled in "ContinueMoving()").
+void DrawGameValue_Score(int value)
+{
+  struct TextPosInfo *pos = &game.panel.score;
+#if 1
+  int font_nr = pos->font;
+#else
+  int font_nr = FONT_TEXT_2;
+#endif
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->chars;
 
-    Affected levels/tapes:
-    The first condition is generally needed for all levels/tapes before version
-    3.1.0, which might use the old behaviour before it was changed; known tapes
-    that are affected are some tapes from the level set "Walpurgis Gardens" by
-    Jamie Cullen.
-    The second condition is an exception from the above case and is needed for
-    the special case of tapes recorded with game (not engine!) version 3.1.0 or
-    above (including some development versions of 3.1.0), but before it was
-    known that this change would break tapes like the above and was fixed in
-    3.1.1, so that the changed behaviour was active although the engine version
-    while recording maybe was before 3.1.0. There is at least one tape that is
-    affected by this exception, which is the tape for the one-level set "Bug
-    Machine" by Juergen Bonhagen.
-  */
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-  game.use_change_when_pushing_bug =
-    (game.engine_version < VERSION_IDENT(3,1,0,0) &&
-     !(tape.playing &&
-       tape.game_version >= VERSION_IDENT(3,1,0,0) &&
-       tape.game_version <  VERSION_IDENT(3,1,1,0)));
+  if (PANEL_DEACTIVATED(pos))
+    return;
 
-  /*
-    Summary of bugfix/change:
-    Fixed handling for blocking the field the player leaves when moving.
+  pos->width = chars * font_width;
 
-    Fixed/changed in version:
-    3.1.1
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
 
-    Description:
-    Before 3.1.1, when "block last field when moving" was enabled, the field
-    the player is leaving when moving was blocked for the time of the move,
+void DrawGameValue_Time(int value)
+{
+  struct TextPosInfo *pos = &game.panel.time;
+  static int last_value = -1;
+  int chars1 = 3;
+  int chars2 = 4;
+  int chars = pos->chars;
+#if 1
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+#else
+  int font1_nr = FONT_TEXT_2;
+  int font2_nr = FONT_TEXT_1;
+#endif
+  int font_nr = font1_nr;
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
+    return;
+
+  if (use_dynamic_chars)               /* use dynamic number of chars */
+  {
+    chars   = (value < 1000 ? chars1   : chars2);
+    font_nr = (value < 1000 ? font1_nr : font2_nr);
+  }
+
+  /* clear background if value just changed its size (dynamic chars only) */
+  if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
+  {
+    int width1 = chars1 * getFontWidth(font1_nr);
+    int width2 = chars2 * getFontWidth(font2_nr);
+    int max_width = MAX(width1, width2);
+    int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
+
+    pos->width = max_width;
+
+    ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                              max_width, max_height);
+  }
+
+  pos->width = chars * getFontWidth(font_nr);
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+
+  last_value = value;
+}
+
+void DrawGameValue_Level(int value)
+{
+  struct TextPosInfo *pos = &game.panel.level_number;
+  int chars1 = 2;
+  int chars2 = 3;
+  int chars = pos->chars;
+#if 1
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+#else
+  int font1_nr = FONT_TEXT_2;
+  int font2_nr = FONT_TEXT_1;
+#endif
+  int font_nr = font1_nr;
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
+    return;
+
+  if (use_dynamic_chars)               /* use dynamic number of chars */
+  {
+    chars   = (level_nr < 100 ? chars1   : chars2);
+    font_nr = (level_nr < 100 ? font1_nr : font2_nr);
+  }
+
+  pos->width = chars * getFontWidth(font_nr);
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
+
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+{
+#if 0
+  struct TextPosInfo *pos = &game.panel.keys;
+#endif
+#if 0
+  int base_key_graphic = EL_KEY_1;
+#endif
+  int i;
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+#if 0
+  if (PANEL_DEACTIVATED(pos))
+    return;
+#endif
+
+#if 0
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    base_key_graphic = EL_EM_KEY_1;
+#endif
+
+#if 0
+  pos->width = 4 * MINI_TILEX;
+#endif
+
+#if 1
+  for (i = 0; i < MAX_NUM_KEYS; i++)
+#else
+  /* currently only 4 of 8 possible keys are displayed */
+  for (i = 0; i < STD_NUM_KEYS; i++)
+#endif
+  {
+#if 1
+    struct TextPosInfo *pos = &game.panel.key[i];
+#endif
+    int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
+    int src_y = DOOR_GFX_PAGEY1 + 123;
+#if 1
+    int dst_x = PANEL_XPOS(pos);
+    int dst_y = PANEL_YPOS(pos);
+#else
+    int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
+    int dst_y = PANEL_YPOS(pos);
+#endif
+
+#if 1
+    int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
+                  level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
+                  EL_KEY_1) + i;
+    int graphic = el2edimg(element);
+#endif
+
+#if 1
+    if (PANEL_DEACTIVATED(pos))
+      continue;
+#endif
+
+#if 0
+    /* masked blit with tiles from half-size scaled bitmap does not work yet
+       (no mask bitmap created for these sizes after loading and scaling) --
+       solution: load without creating mask, scale, then create final mask */
+
+    BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+              MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+
+    if (key[i])
+    {
+#if 0
+      int graphic = el2edimg(base_key_graphic + i);
+#endif
+      Bitmap *src_bitmap;
+      int src_x, src_y;
+
+      getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
+
+      SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+                   dst_x - src_x, dst_y - src_y);
+      BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
+                      dst_x, dst_y);
+    }
+#else
+#if 1
+    if (key[i])
+      DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
+    else
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+#else
+    if (key[i])
+      DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
+    else
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+#endif
+#endif
+  }
+}
+
+#else
+
+void DrawGameValue_Emeralds(int value)
+{
+  int font_nr = FONT_TEXT_2;
+  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
+
+  if (PANEL_DEACTIVATED(game.panel.gems))
+    return;
+
+  DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
+}
+
+void DrawGameValue_Dynamite(int value)
+{
+  int font_nr = FONT_TEXT_2;
+  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
+
+  if (PANEL_DEACTIVATED(game.panel.inventory))
+    return;
+
+  DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
+}
+
+void DrawGameValue_Score(int value)
+{
+  int font_nr = FONT_TEXT_2;
+  int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
+
+  if (PANEL_DEACTIVATED(game.panel.score))
+    return;
+
+  DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
+}
+
+void DrawGameValue_Time(int value)
+{
+  int font1_nr = FONT_TEXT_2;
+#if 1
+  int font2_nr = FONT_TEXT_1;
+#else
+  int font2_nr = FONT_LEVEL_NUMBER;
+#endif
+  int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
+  int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
+
+  if (PANEL_DEACTIVATED(game.panel.time))
+    return;
+
+  /* clear background if value just changed its size */
+  if (value == 999 || value == 1000)
+    ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
+
+  if (value < 1000)
+    DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
+  else
+    DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
+}
+
+void DrawGameValue_Level(int value)
+{
+  int font1_nr = FONT_TEXT_2;
+#if 1
+  int font2_nr = FONT_TEXT_1;
+#else
+  int font2_nr = FONT_LEVEL_NUMBER;
+#endif
+
+  if (PANEL_DEACTIVATED(game.panel.level))
+    return;
+
+  if (level_nr < 100)
+    DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
+  else
+    DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
+}
+
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+{
+  int base_key_graphic = EL_KEY_1;
+  int i;
+
+  if (PANEL_DEACTIVATED(game.panel.keys))
+    return;
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    base_key_graphic = EL_EM_KEY_1;
+
+  /* currently only 4 of 8 possible keys are displayed */
+  for (i = 0; i < STD_NUM_KEYS; i++)
+  {
+    int x = XX_KEYS + i * MINI_TILEX;
+    int y = YY_KEYS;
+
+    if (key[i])
+      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
+    else
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
+  }
+}
+
+#endif
+
+void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
+                      int key_bits)
+{
+  int key[MAX_NUM_KEYS];
+  int i;
+
+  /* prevent EM engine from updating time/score values parallel to GameWon() */
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
+      local_player->LevelSolved)
+    return;
+
+  for (i = 0; i < MAX_NUM_KEYS; i++)
+    key[i] = key_bits & (1 << i);
+
+  DrawGameValue_Level(level_nr);
+
+  DrawGameValue_Emeralds(emeralds);
+  DrawGameValue_Dynamite(dynamite);
+  DrawGameValue_Score(score);
+  DrawGameValue_Time(time);
+
+  DrawGameValue_Keys(key);
+}
+
+void DrawGameDoorValues()
+{
+  UpdateGameControlValues();
+  DisplayGameControlValues();
+}
+
+void DrawGameDoorValues_OLD()
+{
+  int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
+  int dynamite_value = 0;
+  int score_value = (local_player->LevelSolved ? local_player->score_final :
+                    local_player->score);
+  int gems_value = local_player->gems_still_needed;
+  int key_bits = 0;
+  int i, j;
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+  {
+    DrawGameDoorValues_EM();
+
+    return;
+  }
+
+  if (game.centered_player_nr == -1)
+  {
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      for (j = 0; j < MAX_NUM_KEYS; j++)
+       if (stored_player[i].key[j])
+         key_bits |= (1 << j);
+
+      dynamite_value += stored_player[i].inventory_size;
+    }
+  }
+  else
+  {
+    int player_nr = game.centered_player_nr;
+
+    for (i = 0; i < MAX_NUM_KEYS; i++)
+      if (stored_player[player_nr].key[i])
+       key_bits |= (1 << i);
+
+    dynamite_value = stored_player[player_nr].inventory_size;
+  }
+
+  DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
+                   key_bits);
+}
+
+
+/*
+  =============================================================================
+  InitGameEngine()
+  -----------------------------------------------------------------------------
+  initialize game engine due to level / tape version number
+  =============================================================================
+*/
+
+static void InitGameEngine()
+{
+  int i, j, k, l, x, y;
+
+  /* set game engine from tape file when re-playing, else from level file */
+  game.engine_version = (tape.playing ? tape.engine_version :
+                        level.game_version);
+
+  /* ---------------------------------------------------------------------- */
+  /* set flags for bugs and changes according to active game engine version */
+  /* ---------------------------------------------------------------------- */
+
+  /*
+    Summary of bugfix/change:
+    Fixed handling for custom elements that change when pushed by the player.
+
+    Fixed/changed in version:
+    3.1.0
+
+    Description:
+    Before 3.1.0, custom elements that "change when pushing" changed directly
+    after the player started pushing them (until then handled in "DigField()").
+    Since 3.1.0, these custom elements are not changed until the "pushing"
+    move of the element is finished (now handled in "ContinueMoving()").
+
+    Affected levels/tapes:
+    The first condition is generally needed for all levels/tapes before version
+    3.1.0, which might use the old behaviour before it was changed; known tapes
+    that are affected are some tapes from the level set "Walpurgis Gardens" by
+    Jamie Cullen.
+    The second condition is an exception from the above case and is needed for
+    the special case of tapes recorded with game (not engine!) version 3.1.0 or
+    above (including some development versions of 3.1.0), but before it was
+    known that this change would break tapes like the above and was fixed in
+    3.1.1, so that the changed behaviour was active although the engine version
+    while recording maybe was before 3.1.0. There is at least one tape that is
+    affected by this exception, which is the tape for the one-level set "Bug
+    Machine" by Juergen Bonhagen.
+  */
+
+  game.use_change_when_pushing_bug =
+    (game.engine_version < VERSION_IDENT(3,1,0,0) &&
+     !(tape.playing &&
+       tape.game_version >= VERSION_IDENT(3,1,0,0) &&
+       tape.game_version <  VERSION_IDENT(3,1,1,0)));
+
+  /*
+    Summary of bugfix/change:
+    Fixed handling for blocking the field the player leaves when moving.
+
+    Fixed/changed in version:
+    3.1.1
+
+    Description:
+    Before 3.1.1, when "block last field when moving" was enabled, the field
+    the player is leaving when moving was blocked for the time of the move,
     and was directly unblocked afterwards. This resulted in the last field
     being blocked for exactly one less than the number of frames of one player
     move. Additionally, even when blocking was disabled, the last field was
@@ -1427,32 +2580,18 @@ static void InitGameEngine()
   printf("       => game.engine_version == %06d\n", game.engine_version);
 #endif
 
-#if 0
-  /* ---------- 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);
-#endif
-
   /* ---------- initialize player's initial move delay --------------------- */
 
-#if 1
-  /* dynamically adjust player properties according to level information */
-  game.initial_move_delay_value =
-    get_move_delay_from_stepsize(level.initial_player_stepsize);
-#else
   /* dynamically adjust player properties according to level information */
-  game.initial_move_delay_value =
-    (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
-#endif
+  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 --------------------- */
 
@@ -1559,6 +2698,7 @@ static void InitGameEngine()
       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
       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;
     }
   }
 
@@ -1595,6 +2735,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;
          }
@@ -1610,8 +2753,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;
+      }
     }
   }
 
@@ -1718,6 +2870,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)
@@ -1743,6 +2907,7 @@ int get_num_special_action(int element, int action_first, int action_last)
   return num_special_action;
 }
 
+
 /*
   =============================================================================
   InitGame()
@@ -1756,9 +2921,15 @@ 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
   int i, j, x, y;
 
+  game_status = GAME_MODE_PLAYING;
+
   InitGameEngine();
+  InitGameControlValues();
 
   /* don't play tapes over network */
   network_playing = (options.network && !tape.playing);
@@ -1773,12 +2944,15 @@ void InitGame()
 
     player->present = FALSE;
     player->active = FALSE;
+    player->killed = 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;
@@ -1787,6 +2961,8 @@ 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;
@@ -1808,6 +2984,8 @@ void InitGame()
     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;
@@ -1816,6 +2994,8 @@ void InitGame()
 
     player->last_move_dir = MV_NONE;
 
+    player->is_active = FALSE;
+
     player->is_waiting = FALSE;
     player->is_moving = FALSE;
     player->is_auto_moving = FALSE;
@@ -1825,6 +3005,7 @@ 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;
@@ -1835,19 +3016,12 @@ void InitGame()
     player->anim_delay_counter = 0;
     player->post_delay_counter = 0;
 
+    player->dir_waiting = MV_NONE;
     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;
 
@@ -1856,26 +3030,18 @@ void InitGame()
 
     player->show_envelope = 0;
 
-#if 1
-    SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
-#else
-    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;
-
-    player->cannot_move = FALSE;
-#endif
+    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;
@@ -1888,6 +3054,12 @@ void InitGame()
 
     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;
   }
 
   network_player_action_received = FALSE;
@@ -1922,8 +3094,11 @@ void InitGame()
   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;
@@ -1933,7 +3108,17 @@ void InitGame()
 
   game.envelope_active = FALSE;
 
-  game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
+  /* 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++)
   {
@@ -1944,11 +3129,7 @@ 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++)
-#endif
   {
     Feld[x][y] = level.field[x][y];
     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
@@ -1962,6 +3143,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;
 
@@ -1982,11 +3164,7 @@ void InitGame()
     GfxDir[x][y] = MV_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;
@@ -2000,6 +3178,19 @@ void InitGame()
 
   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);
@@ -2078,10 +3269,6 @@ void InitGame()
          some_player->present = FALSE;
          some_player->active = FALSE;
 
-#if 0
-         player->element_nr = some_player->element_nr;
-#endif
-
          player->artwork_element = some_player->artwork_element;
 
          player->block_last_field       = some_player->block_last_field;
@@ -2099,7 +3286,7 @@ void InitGame()
 
   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 */
 
     for (i = 0; i < MAX_PLAYERS; i++)
     {
@@ -2194,11 +3381,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;
@@ -2227,7 +3410,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;
@@ -2244,7 +3428,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;
@@ -2264,7 +3449,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;
@@ -2295,13 +3481,37 @@ void InitGame()
                local_player->jy - MIDPOSY);
   }
 
+  StopAnimation();
+
   if (!game.restart_level)
     CloseDoor(DOOR_CLOSE_1);
 
+#if 1
+  if (level_editor_test_game)
+    FadeSkipNextFadeIn();
+  else
+    FadeSetStartItem();
+#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
+
   /* !!! 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
   {
@@ -2312,14 +3522,23 @@ void InitGame()
     if (game.timegate_time_left == 0)
       CloseAllOpenTimegates();
 
+    /* blit playfield from scroll buffer to normal back buffer for fading in */
     if (setup.soft_scrolling)
       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
 
     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 */
@@ -2327,6 +3546,9 @@ void InitGame()
               DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
   }
 
+  SetPanelBackground();
+  SetDrawBackgroundMask(REDRAW_DOOR_1);
+
   DrawGameDoorValues();
 
   if (!game.restart_level)
@@ -2345,7 +3567,7 @@ void InitGame()
 
     OpenDoor(DOOR_OPEN_ALL);
 
-    PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
+    PlaySound(SND_GAME_STARTING);
 
     if (setup.sound_music)
       PlayLevelMusic();
@@ -2360,6 +3582,13 @@ void InitGame()
     }
   }
 
+#if 1
+  UnmapAllGadgets();
+
+  MapGameButtons();
+  MapTapeButtons();
+#endif
+
   game.restart_level = FALSE;
 }
 
@@ -2389,7 +3618,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:
@@ -2431,6 +3660,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;
@@ -2562,123 +3799,229 @@ void InitAmoebaNr(int x, int y)
   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);
+}
+
 void GameWon()
 {
-  int hi_pos;
-  boolean raise_level = FALSE;
+  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->MovPos)
-    return;
+  if (!local_player->LevelSolved_GameWon)
+  {
+    int i;
 
-  if (tape.auto_play)          /* tape might already be stopped here */
-    tape.auto_play_level_solved = TRUE;
+    /* do not start end game actions before the player stops moving (to exit) */
+    if (local_player->MovPos)
+      return;
 
-  local_player->LevelSolved = FALSE;
+    local_player->LevelSolved_GameWon = TRUE;
+    local_player->LevelSolved_SaveTape = tape.recording;
+    local_player->LevelSolved_SaveScore = !tape.playing;
 
-  PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
+    if (tape.auto_play)                /* tape might already be stopped here */
+      tape.auto_play_level_solved = TRUE;
 
-  if (TimeLeft)
-  {
-    if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
-                  SND_CTRL_PLAY_LOOP);
+#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 = (level.time == 0 ? 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 (level.time == 0 && TimePlayed < 999)
+    {
+      time_final = 999;
+      score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
+    }
 
-    while (TimeLeft > 0)
+    local_player->score_final = score_final;
+
+    if (level_editor_test_game)
     {
-      if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+      time = time_final;
+      score = score_final;
 
-      if (TimeLeft > 100 && TimeLeft % 10 == 0)
-      {
-       TimeLeft -= 10;
-       RaiseScore(level.score[SC_TIME_BONUS] * 10);
-      }
-      else
+#if 1
+      game_control_value[GAME_CONTROL_TIME] = time;
+      game_control_value[GAME_CONTROL_SCORE] = 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 */
       {
-       TimeLeft--;
-       RaiseScore(level.score[SC_TIME_BONUS]);
+       /* 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 0
+         if (element == EL_EM_EXIT_OPEN ||
+             element == EL_EM_STEEL_EXIT_OPEN)
+         {
+           Bang(ExitX, ExitY);
+         }
+         else
+#endif
+         {
+           Feld[ExitX][ExitY] =
+             (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
+              element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
+              element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
+              element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
+              EL_EM_STEEL_EXIT_CLOSING);
+
+           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+         }
+       }
+
+       /* player disappears */
+       DrawLevelField(ExitX, ExitY);
       }
 
-      DrawGameValue_Time(TimeLeft);
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
 
-      BackToFront();
+       if (player->present)
+       {
+         RemovePlayer(player);
 
-      if (!tape.playing)
-       Delay(10);
+         /* player disappears */
+         DrawLevelField(player->jx, player->jy);
+       }
+      }
     }
 
-    if (!tape.playing && setup.sound_loops)
-      StopSound(SND_GAME_LEVELTIME_BONUS);
+    PlaySound(SND_GAME_WINNING);
   }
-  else if (level.time == 0)            /* level without time limit */
+
+  if (game_over_delay_1 > 0)
   {
-    if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
-                  SND_CTRL_PLAY_LOOP);
+    game_over_delay_1--;
 
-    while (TimePlayed < 999)
-    {
-      if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+    return;
+  }
 
-      if (TimePlayed < 900 && TimePlayed % 10 == 0)
-      {
-       TimePlayed += 10;
-       RaiseScore(level.score[SC_TIME_BONUS] * 10);
-      }
-      else
-      {
-       TimePlayed++;
-       RaiseScore(level.score[SC_TIME_BONUS]);
-      }
+  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);
 
-      DrawGameValue_Time(TimePlayed);
+    time  += time_count_steps * time_count_dir;
+    score += time_count_steps * level.score[SC_TIME_BONUS];
 
-      BackToFront();
+#if 1
+    game_control_value[GAME_CONTROL_TIME] = time;
+    game_control_value[GAME_CONTROL_SCORE] = score;
 
-      if (!tape.playing)
-       Delay(10);
-    }
+    DisplayGameControlValues();
+#else
+    DrawGameValue_Time(time);
+    DrawGameValue_Score(score);
+#endif
 
-    if (!tape.playing && setup.sound_loops)
+    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);
+
+    return;
   }
 
-  /* 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))
-  {
-    int element = Feld[ExitX][ExitY];
+  local_player->LevelSolved_PanelOff = TRUE;
 
-    Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
-                         EL_SP_EXIT_CLOSING);
+  if (game_over_delay_2 > 0)
+  {
+    game_over_delay_2--;
 
-    PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+    return;
   }
 
-  /* player disappears */
-  if (ExitX >= 0 && ExitY >= 0)
-    DrawLevelField(ExitX, ExitY);
+#if 1
+  GameEnd();
+#endif
+}
 
-  BackToFront();
+void GameEnd()
+{
+  int hi_pos;
+  boolean raise_level = FALSE;
 
+  local_player->LevelSolved_GameEnd = TRUE;
+
+  CloseDoor(DOOR_CLOSE_1);
+
+  if (local_player->LevelSolved_SaveTape)
+  {
 #if 0
-  if (tape.playing)
-    printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
-  else
-    printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
+    TapeStop();
 #endif
 
-  if (tape.playing)
-    return;
+#if 1
+    SaveTapeChecked(tape.level_nr);    /* ask to save tape */
+#else
+    SaveTape(tape.level_nr);           /* ask to save tape */
+#endif
+  }
 
-  CloseDoor(DOOR_CLOSE_1);
+  if (level_editor_test_game)
+  {
+    game_status = GAME_MODE_MAIN;
 
-  if (tape.recording)
+#if 1
+    DrawAndFadeInMainMenu(REDRAW_FIELD);
+#else
+    DrawMainMenu();
+#endif
+
+    return;
+  }
+
+  if (!local_player->LevelSolved_SaveScore)
   {
-    TapeStop();
-    SaveTape(tape.level_nr);           /* Ask to save tape */
+#if 1
+    FadeOut(REDRAW_FIELD);
+#endif
+
+    game_status = GAME_MODE_MAIN;
+
+    DrawAndFadeInMainMenu(REDRAW_FIELD);
+
+    return;
   }
 
   if (level_nr == leveldir_current->handicap_level)
@@ -2687,15 +4030,15 @@ void GameWon()
     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++;
@@ -2704,16 +4047,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()
@@ -2723,13 +4070,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 */
 
@@ -2739,7 +4086,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;
@@ -2757,7 +4104,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;
     }
@@ -2776,10 +4123,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);
@@ -2789,24 +4135,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)
@@ -2823,24 +4166,12 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
   }
 }
 
-static void ResetRandomAnimationValue(int x, int y)
-{
-  GfxRandom[x][y] = INIT_GFX_RANDOM();
-}
-
-static void ResetGfxAnimation(int x, int y)
+#if USE_GFX_RESET_GFX_ANIMATION
+static void ResetGfxFrame(int x, int y, boolean redraw)
 {
-#if 0
-  int element, graphic;
-#endif
-
-  GfxFrame[x][y] = 0;
-  GfxAction[x][y] = ACTION_DEFAULT;
-  GfxDir[x][y] = MovDir[x][y];
-
-#if 0
-  element = Feld[x][y];
-  graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+  int element = Feld[x][y];
+  int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+  int last_gfx_frame = GfxFrame[x][y];
 
   if (graphic_info[graphic].anim_global_sync)
     GfxFrame[x][y] = FrameCounter;
@@ -2848,42 +4179,87 @@ static void ResetGfxAnimation(int x, int y)
     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)
+{
+  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)
 {
   int element = Feld[x][y];
-#if 0
-  int graphic;
-#endif
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   int newx = x + dx;
   int newy = y + dy;
+  boolean is_moving_before, is_moving_after;
+#if 0
+  boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
+#endif
+
+  /* check if element was/is moving or being moved before/after mode change */
+#if 1
+#if 1
+  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);
-
-#if 0
-  graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[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;
 #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;
@@ -2904,13 +4280,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];
-#if 1
   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
-#else
-  int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
-  int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
-#endif
 
   *goes_to_x = newx;
   *goes_to_y = newy;
@@ -3035,8 +4406,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]);
 
@@ -3097,38 +4470,117 @@ void CheckDynamite(int x, int y)
   Bang(x, y);
 }
 
-void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
+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 (quick_relocation)
   {
-    int offset = (setup.scroll_delay ? 3 : 0);
+    int offset = game.scroll_delay_value;
 
-    if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
+    if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
     {
-      scroll_x = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-                 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-                 player->jx - MIDPOSX);
+      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);
 
-      scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-                 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-                 player->jy - MIDPOSY);
+       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);
+      /* 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);
+      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)
@@ -3143,23 +4595,65 @@ void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
   }
   else
   {
-    int scroll_xx = -999, scroll_yy = -999;
+#if 1
+    int scroll_xx, scroll_yy;
+
+    if (!level.shifted_relocation || center_screen)
+    {
+      /* visible relocation (with scrolling), with centering of screen */
+
+      scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                  x > SBX_Right + MIDPOSX ? SBX_Right :
+                  x - MIDPOSX);
+
+      scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                  y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                  y - MIDPOSY);
+    }
+    else
+    {
+      /* visible relocation (with scrolling), but do not center screen */
+
+      int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
+                            old_x - MIDPOSX);
+
+      int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                            old_y - MIDPOSY);
+
+      int offset_x = x + (scroll_x - center_scroll_x);
+      int offset_y = y + (scroll_y - center_scroll_y);
+
+      scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                  offset_x > SBX_Right + MIDPOSX ? SBX_Right :
+                  offset_x - MIDPOSX);
+
+      scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                  offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                  offset_y - MIDPOSY);
+    }
+
+#else
+
+    /* visible relocation (with scrolling), with centering of screen */
+
+    int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                    x > SBX_Right + MIDPOSX ? SBX_Right :
+                    x - MIDPOSX);
+
+    int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                    y - MIDPOSY);
+#endif
 
     ScrollScreen(NULL, SCROLL_GO_ON);  /* scroll last frame to full tile */
 
-    while (scroll_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 = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-                  player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-                  player->jx - MIDPOSX);
-
-      scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-                  player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-                  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);
 
@@ -3185,7 +4679,7 @@ void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
       Delay(wait_delay_value);
     }
 
-    DrawPlayer(player);
+    DrawAllPlayers();
     BackToFront();
     Delay(wait_delay_value);
   }
@@ -3266,14 +4760,9 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
     InitField(jx, jy, FALSE);
   }
 
-#if 1
   /* only visually relocate centered player */
-  if (player->index_nr == game.centered_player_nr)
-    DrawRelocatePlayer(player, level.instant_relocation);
-#else
-  if (player == local_player)  /* only visually relocate local player */
-    DrawRelocatePlayer(player, level.instant_relocation);
-#endif
+  DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
+                    FALSE, level.instant_relocation);
 
   TestIfPlayerTouchesBadThing(jx, jy);
   TestIfPlayerTouchesCustomElement(jx, jy);
@@ -3359,11 +4848,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++)
     {
@@ -3436,33 +4921,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))
@@ -3483,45 +4949,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)
@@ -3564,6 +4991,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");
@@ -3573,6 +5005,8 @@ void Explode(int ex, int ey, int phase, int mode)
 
     GfxElement[x][y] = EL_EMPTY;
   }
+#endif
+
 #endif
 
   border_element = Store2[x][y];
@@ -3761,7 +5195,7 @@ void Bang(int x, int y)
     }
   }
 
-  switch(element)
+  switch (element)
   {
     case EL_BUG:
     case EL_SPACESHIP:
@@ -3785,6 +5219,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:
@@ -3860,11 +5302,7 @@ static void InitBeltMovement()
     }
   }
 
-#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];
 
@@ -3944,11 +5382,7 @@ static void ToggleBeltSwitch(int x, int y)
       graphic_info[graphic].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];
 
@@ -3995,20 +5429,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);
     }
+    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;
+      DrawLevelField(xx, yy);
+    }
+#else
+    if (element == EL_SWITCHGATE_SWITCH_UP)
+    {
+      Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
+      DrawLevelField(xx, yy);
+    }
+    else if (element == EL_SWITCHGATE_SWITCH_DOWN)
+    {
+      Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
+      DrawLevelField(xx, yy);
+    }
+    else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
+    {
+      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
+      DrawLevelField(xx, yy);
+    }
+    else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+    {
+      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
+      DrawLevelField(xx, yy);
+    }
+#endif
     else if (element == EL_SWITCHGATE_OPEN ||
             element == EL_SWITCHGATE_OPENING)
     {
@@ -4046,11 +5505,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];
 
@@ -4111,11 +5566,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];
 
@@ -4164,11 +5615,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];
 
@@ -4228,11 +5675,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];
 
@@ -4240,7 +5683,7 @@ 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);
     }
 
     /*
@@ -4253,7 +5696,12 @@ static void ActivateTimegateSwitch(int x, int y)
 
   }
 
+#if 1
+  Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
+               EL_DC_TIMEGATE_SWITCH_ACTIVE);
+#else
   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
+#endif
 }
 
 void Impact(int x, int y)
@@ -4288,6 +5736,16 @@ void Impact(int x, int y)
 
       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;
+      DrawLevelField(x, y + 2);
+
+      object_hit = TRUE;
+    }
 #endif
 
     if (object_hit)
@@ -4316,7 +5774,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);
 
@@ -4349,30 +5808,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))
@@ -4452,7 +5914,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);
        }
@@ -4485,12 +5949,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;
   }
@@ -4847,7 +6314,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;
@@ -5314,9 +6784,6 @@ inline static void TurnRoundExt(int x, int y)
 static void TurnRound(int x, int y)
 {
   int direction = MovDir[x][y];
-#if 1
-  int element, graphic;
-#endif
 
   TurnRoundExt(x, y);
 
@@ -5328,17 +6795,7 @@ static void TurnRound(int x, int y)
   if (MovDelay[x][y])
     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
 
-#if 1
-  element = Feld[x][y];
-  graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[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;
-#endif
+  ResetGfxFrame(x, y, FALSE);
 }
 
 static boolean JustBeingPushed(int x, int y)
@@ -5373,26 +6830,68 @@ void StartMoving(int x, int y)
   if (MovDelay[x][y] == 0)
     GfxAction[x][y] = ACTION_DEFAULT;
 
-  if (CAN_FALL(element) && y < lev_fieldy - 1)
-  {
-    if ((x > 0              && IS_PLAYER(x - 1, y)) ||
-       (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
-      if (JustBeingPushed(x, y))
-       return;
+  if (CAN_FALL(element) && y < lev_fieldy - 1)
+  {
+    if ((x > 0              && IS_PLAYER(x - 1, y)) ||
+       (x < lev_fieldx - 1 && IS_PLAYER(x + 1, 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;
+
+       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;
+
+       if (MovDelay[x][y])
+       {
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
 
-    if (element == EL_QUICKSAND_FULL)
+       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 (element == EL_QUICKSAND_FAST_FULL)
     {
       if (IS_FREE(x, y + 1))
       {
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
 
-       Feld[x][y] = EL_QUICKSAND_EMPTYING;
+       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_EMPTY)
+      else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY + 1;
@@ -5404,8 +6903,8 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_QUICKSAND_EMPTY;
-       Feld[x][y + 1] = EL_QUICKSAND_FULL;
+       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;
 
@@ -5423,6 +6922,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))
@@ -5459,7 +6969,7 @@ 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)
       {
@@ -5475,20 +6985,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)
@@ -5500,9 +7042,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))) ||
@@ -5522,6 +7069,7 @@ void StartMoving(int x, int y)
         simply not covered here... :-/ ) */
 
       CheckCollision[x][y] = 0;
+      CheckImpact[x][y] = 0;
 
       Impact(x, y);
     }
@@ -5846,7 +7394,10 @@ 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);
@@ -5858,7 +7409,7 @@ 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;
       }
@@ -6264,6 +7815,17 @@ void ContinueMoving(int x, int y)
 
   if (ABS(MovPos[x][y]) < TILEX)
   {
+#if 0
+    int ee = Feld[x][y];
+    int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+    int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
+
+    printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
+          x, y, ABS(MovPos[x][y]),
+          ee, gg, ff,
+          GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
+#endif
+
     DrawLevelField(x, y);
 
     return;    /* element is still moving */
@@ -6295,6 +7857,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);
@@ -6327,6 +7899,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
@@ -6354,29 +7944,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
 #endif
 
   ChangeDelay[x][y] = 0;
@@ -6471,13 +8049,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 */
@@ -6542,12 +8125,10 @@ void ContinueMoving(int x, int y)
   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)
@@ -6616,11 +8197,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;
@@ -6646,11 +8223,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)
       {
@@ -6708,11 +8281,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 ||
@@ -6851,7 +8420,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;
@@ -6876,7 +8446,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;
@@ -6961,9 +8532,6 @@ void AmoebeAbleger(int ax, int ay)
 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);
@@ -7064,7 +8632,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)
@@ -7108,13 +8676,6 @@ static void ActivateMagicBall(int bx, int by)
   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
 }
 
-static void InitDiagonalMovingElement(int x, int y)
-{
-#if 0
-  MovDelay[x][y] = level.android_move_time;
-#endif
-}
-
 void CheckExit(int x, int y)
 {
   if (local_player->gems_still_needed > 0 ||
@@ -7138,6 +8699,75 @@ void CheckExit(int x, int y)
   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 CheckExitSteelEM(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_STEEL_EXIT_OPENING;
+
+  PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
+}
+
 void CheckExitSP(int x, int y)
 {
   if (local_player->gems_still_needed > 0)
@@ -7163,11 +8793,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];
 
@@ -7180,7 +8806,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;
@@ -7189,7 +8815,7 @@ 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 */
   {
@@ -7264,15 +8890,123 @@ void MauerWaechst(int x, int y)
          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);
+      Feld[x][y] = Store[x][y];
+      Store[x][y] = 0;
+      GfxDir[x][y] = MovDir[x][y] = MV_NONE;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
+void MauerAbleger(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_WALL_VERTICAL ||
+      element == EL_EXPANDABLE_WALL_ANY)
+  {
+    if (oben_frei)
+    {
+      Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
+      Store[ax][ay-1] = element;
+      GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
+      if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
+       DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
+                   IMG_EXPANDABLE_WALL_GROWING_UP, 0);
+      new_wall = TRUE;
+    }
+    if (unten_frei)
+    {
+      Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
+      Store[ax][ay+1] = element;
+      GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
+      if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
+       DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
+                   IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
+      new_wall = TRUE;
+    }
+  }
+
+  if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
+      element == EL_EXPANDABLE_WALL_ANY ||
+      element == EL_EXPANDABLE_WALL ||
+      element == EL_BD_EXPANDABLE_WALL)
+  {
+    if (links_frei)
+    {
+      Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
+      Store[ax-1][ay] = element;
+      GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
+      if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
+       DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
+                   IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
+      new_wall = TRUE;
+    }
+
+    if (rechts_frei)
+    {
+      Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
+      Store[ax+1][ay] = element;
+      GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
+      if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
+       DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
+                   IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
+      new_wall = TRUE;
     }
   }
+
+  if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
+    DrawLevelField(ax, ay);
+
+  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_WALL_HORIZONTAL ||
+       element == EL_EXPANDABLE_WALL) &&
+      ((links_massiv && rechts_massiv) ||
+       element == EL_EXPANDABLE_WALL_VERTICAL))
+    Feld[ax][ay] = EL_WALL;
+
+  if (new_wall)
+    PlayLevelSoundAction(ax, ay, ACTION_GROWING);
 }
 
-void MauerAbleger(int ax, int ay)
+void MauerAblegerStahl(int ax, int ay)
 {
   int element = Feld[ax][ay];
   int graphic = el2img(element);
@@ -7304,61 +9038,57 @@ void MauerAbleger(int ax, int ay)
   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
     rechts_frei = TRUE;
 
-  if (element == EL_EXPANDABLE_WALL_VERTICAL ||
-      element == EL_EXPANDABLE_WALL_ANY)
+  if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+      element == EL_EXPANDABLE_STEELWALL_ANY)
   {
     if (oben_frei)
     {
-      Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
+      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_WALL_GROWING_UP, 0);
+                   IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
       new_wall = TRUE;
     }
     if (unten_frei)
     {
-      Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
+      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_WALL_GROWING_DOWN, 0);
+                   IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
       new_wall = TRUE;
     }
   }
 
-  if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
-      element == EL_EXPANDABLE_WALL_ANY ||
-      element == EL_EXPANDABLE_WALL)
+  if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+      element == EL_EXPANDABLE_STEELWALL_ANY)
   {
     if (links_frei)
     {
-      Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
+      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_WALL_GROWING_LEFT, 0);
+                   IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
       new_wall = TRUE;
     }
 
     if (rechts_frei)
     {
-      Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
+      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_WALL_GROWING_RIGHT, 0);
+                   IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
       new_wall = TRUE;
     }
   }
 
-  if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
-    DrawLevelField(ax, ay);
-
   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]))
@@ -7369,10 +9099,9 @@ void MauerAbleger(int ax, int ay)
     rechts_massiv = TRUE;
 
   if (((oben_massiv && unten_massiv) ||
-       element == EL_EXPANDABLE_WALL_HORIZONTAL ||
-       element == EL_EXPANDABLE_WALL) &&
+       element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
       ((links_massiv && rechts_massiv) ||
-       element == EL_EXPANDABLE_WALL_VERTICAL))
+       element == EL_EXPANDABLE_STEELWALL_VERTICAL))
     Feld[ax][ay] = EL_WALL;
 
   if (new_wall)
@@ -7513,6 +9242,7 @@ 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;
@@ -7551,21 +9281,21 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      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 ? level.initial_player_stepsize :
+    (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 =
@@ -7576,18 +9306,20 @@ 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_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
+     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_CS_TARGET ? GET_CE_SCORE(target_element) :
+     action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
      -1);
@@ -7596,8 +9328,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     (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 =
@@ -7619,7 +9351,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
   /* ---------- execute action  -------------------------------------------- */
 
-  switch(action_type)
+  switch (action_type)
   {
     case CA_NO_ACTION:
     {
@@ -7652,7 +9384,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       {
        TimeLeft = action_arg_number_new;
 
+#if 1
+       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
@@ -7666,7 +9404,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->score = action_arg_number_new;
 
+#if 1
+      game_control_value[GAME_CONTROL_SCORE] = local_player->score;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Score(local_player->score);
+#endif
 
       break;
     }
@@ -7675,11 +9419,18 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->gems_still_needed = action_arg_number_new;
 
+#if 1
+      game_control_value[GAME_CONTROL_GEMS] = 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         :
@@ -7688,6 +9439,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                      game.gravity);
       break;
     }
+#endif
 
     case CA_SET_LEVEL_WIND:
     {
@@ -7712,7 +9464,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;
     }
@@ -7740,9 +9492,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();
          }
        }
       }
@@ -7770,6 +9520,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
            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,
@@ -7778,18 +9532,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                                    action_arg_number_min,
                                    action_arg_number_max);
 
-#if 1
          SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
-#else
-         /* 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);
-#endif
        }
       }
 
@@ -7822,6 +9565,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++)
@@ -7835,6 +9597,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);
@@ -7854,36 +9621,66 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     /* ---------- CE actions  ---------------------------------------------- */
 
-    case CA_SET_CE_SCORE:
+    case CA_SET_CE_VALUE:
     {
-      ei->collect_score = action_arg_number_new;
+#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_VALUE:
+    case CA_SET_CE_SCORE:
     {
 #if USE_NEW_CUSTOM_VALUE
-      int last_custom_value = CustomValue[x][y];
-
-      CustomValue[x][y] = action_arg_number_new;
+      int last_ce_score = ei->collect_score;
 
-#if 0
-      printf("::: Count == %d\n", CustomValue[x][y]);
-#endif
+      ei->collect_score = action_arg_number_new;
 
-      if (CustomValue[x][y] == 0 && last_custom_value > 0)
+      if (ei->collect_score != last_ce_score)
       {
-#if 0
-       printf("::: CE_VALUE_GETS_ZERO\n");
-#endif
-
-       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
-       CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
+       CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
 
-#if 0
-       printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
-#endif
+       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
 
@@ -7906,54 +9703,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);
 
+    new_element = Feld[x][y];  /* element may have changed */
+
+#if USE_GFX_RESET_GFX_ANIMATION
+    ResetGfxAnimation(x, y);
+    ResetRandomAnimationValue(x, y);
+#endif
+
     DrawLevelField(x, y);
 
-    if (GFX_CRUMBLED(Feld[x][y]))
+    if (GFX_CRUMBLED(new_element))
       DrawLevelFieldCrumbledSandNeighbours(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 */
@@ -7990,7 +9831,10 @@ static void CreateElementFromChange(int x, int y, int element)
 
 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];
 
@@ -8005,6 +9849,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
     change->actual_trigger_player = EL_PLAYER_1;
     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 */
@@ -8109,7 +9954,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);
 
@@ -8130,7 +9976,8 @@ 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)
@@ -8189,8 +10036,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);
@@ -8339,6 +10197,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;
@@ -8364,16 +10230,13 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
        change->actual_trigger_player = EL_PLAYER_1 + log_2(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 ((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)
            {
@@ -8410,6 +10273,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
     }
   }
 
+  RECURSION_LOOP_DETECTION_END();
+
   return change_done_any;
 }
 
@@ -8448,14 +10313,30 @@ static boolean CheckElementChangeExt(int x, int y,
     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);
+
   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] &&
@@ -8468,6 +10349,7 @@ static boolean CheckElementChangeExt(int x, int y,
       change->actual_trigger_player = EL_PLAYER_1 + log_2(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)
@@ -8490,6 +10372,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)
@@ -8517,6 +10400,8 @@ static boolean CheckElementChangeExt(int x, int y,
     }
   }
 
+  RECURSION_LOOP_DETECTION_END();
+
   return change_done;
 }
 
@@ -8558,6 +10443,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)
@@ -8569,13 +10455,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 +
@@ -8593,6 +10479,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)
@@ -8611,10 +10515,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;
        }
@@ -8637,16 +10541,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;
        }
@@ -8675,6 +10579,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;
@@ -8691,8 +10596,8 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
   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;
@@ -8736,10 +10641,103 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
     if (player->MovPos == 0)   /* needed for tape.playing */
       player->is_moving = FALSE;
 
-    player->is_dropping = FALSE;
+    player->is_dropping = FALSE;
+    player->is_dropping_pressed = FALSE;
+    player->drop_pressed_delay = 0;
+
+    return 0;
+  }
+}
+
+static void CheckLevelTime()
+{
+  int i;
+
+  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;
+  }
+
+  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
+       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+       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 (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      {
+       game_control_value[GAME_CONTROL_TIME] = TimePlayed;
+
+       DisplayGameControlValues();
+      }
+#else
+      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+       DrawGameValue_Time(TimePlayed);
+#endif
+
+      level.native_em_level->lev->time =
+       (level.time == 0 ? TimePlayed : TimeLeft);
+    }
 
-    return 0;
+    if (tape.recording || tape.playing)
+      DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
   }
+
+  DrawGameDoorValues();
 }
 
 void AdvanceFrameAndPlayerCounters(int player_nr)
@@ -8787,65 +10785,99 @@ 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,
+                     long random_seed)
+{
+  unsigned long 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;
   byte *recorded_player_action;
   byte summarized_player_action = 0;
   byte tape_action[MAX_PLAYERS];
+  int i;
 
-  if (game_status != GAME_MODE_PLAYING)
-    return;
+  /* 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 ?");
 
-  game_frame_delay_value =
-    (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
+    Error(ERR_WARN, "element '%s' caused endless loop in game engine",
+         EL_NAME(recursion_loop_element));
 
-  if (tape.playing && tape.warp_forward && !tape.pausing)
-    game_frame_delay_value = 0;
+    RequestQuitGameExt(FALSE, level_editor_test_game, message);
 
-  /* ---------- main game synchronization point ---------- */
+    recursion_loop_detected = FALSE;   /* if game should be continued */
 
-  WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+    free(message);
 
-  InitPlayfieldScanModeVars();
+    return;
+  }
+
+  if (game.restart_level)
+    StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
 
-  if (ScreenMovPos == 0)       /* screen currently aligned at tile position */
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
-    struct PlayerInfo *player;
-    int player_nr = game.centered_player_nr_next;
+    if (level.native_em_level->lev->home == 0) /* all players at home */
+    {
+      PlayerWins(local_player);
 
-    if (game.centered_player_nr_next == -1)
-      player_nr = local_player->index_nr;
+      AllPlayersGone = TRUE;
 
-    player = &stored_player[player_nr];
+      level.native_em_level->lev->home = -1;
+    }
 
-    if (!player->active)
-      game.centered_player_nr_next = game.centered_player_nr;
+    if (level.native_em_level->ply[0]->alive == 0 &&
+       level.native_em_level->ply[1]->alive == 0 &&
+       level.native_em_level->ply[2]->alive == 0 &&
+       level.native_em_level->ply[3]->alive == 0)      /* all dead */
+      AllPlayersGone = TRUE;
+  }
 
-    if (game.centered_player_nr != game.centered_player_nr_next)
-    {
-      DrawRelocatePlayer(player, setup.quick_switch);
+  if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
+    GameWon();
 
-      game.centered_player_nr = game.centered_player_nr_next;
-    }
-  }
+  if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
+    TapeStop();
 
-#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_status != GAME_MODE_PLAYING)                /* status might have changed */
+    return;
+
+  game_frame_delay_value =
+    (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
+
+  if (tape.playing && tape.warp_forward && !tape.pausing)
+    game_frame_delay_value = 0;
+
+  /* ---------- main game synchronization point ---------- */
+
+  WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
 
   if (network_playing && !network_player_action_received)
   {
@@ -8862,19 +10894,32 @@ 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;
@@ -8891,6 +10936,13 @@ void GameActions()
   if (!options.network && !setup.team_mode)
     local_player->effective_action = summarized_player_action;
 
+  if (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];
@@ -8899,14 +10951,101 @@ void GameActions()
   {
     tape_action[i] = stored_player[i].effective_action;
 
+    /* (this can only happen in the R'n'D game engine) */
     if (tape.recording && tape_action[i] && !tape.player_participates[i])
       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
   }
 
-  /* 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 (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+  {
+    GameActions_EM_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_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;
@@ -8926,20 +11065,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
@@ -8967,18 +11097,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;
@@ -9022,6 +11150,8 @@ void GameActions()
       WasJustFalling[x][y]--;
     if (CheckCollision[x][y] > 0)
       CheckCollision[x][y]--;
+    if (CheckImpact[x][y] > 0)
+      CheckImpact[x][y]--;
 
     GfxFrame[x][y]++;
 
@@ -9050,46 +11180,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))
@@ -9112,18 +11265,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);
@@ -9135,12 +11276,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)
+       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]);
 
-      element = Feld[x][y];
-      graphic = el_act_dir2img(element, 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);
@@ -9154,11 +11430,14 @@ void GameActions()
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       EdelsteinFunkeln(x, y);
+       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 ||
@@ -9184,15 +11463,27 @@ 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)
@@ -9201,20 +11492,14 @@ void GameActions()
             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);
@@ -9229,7 +11514,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;
@@ -9238,6 +11526,10 @@ 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))
@@ -9254,6 +11546,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))
       {
@@ -9275,11 +11568,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];
 
@@ -9304,6 +11593,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);
     }
@@ -9313,11 +11606,7 @@ void GameActions()
       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];
 
@@ -9333,6 +11622,12 @@ void GameActions()
            Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
            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;
+           DrawLevelField(x, y);
+         }
        }
 
        game.magic_wall_active = FALSE;
@@ -9385,48 +11680,7 @@ void GameActions()
     }
   }
 
-  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 (!level.use_step_counter)
-    {
-      TimePlayed++;
-
-      if (TimeLeft > 0)
-      {
-       TimeLeft--;
-
-       if (TimeLeft <= 10 && setup.time_limit)
-         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
-
-       DrawGameValue_Time(TimeLeft);
-
-       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 (tape.recording || tape.playing)
-      DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
-  }
+  CheckLevelTime();
 
   DrawAllPlayers();
   PlayAllPlayersSound();
@@ -9459,6 +11713,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);
@@ -9505,25 +11764,85 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
+#if 1
+  static Bitmap *bitmap_db_field2 = NULL;
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
   int x, y;
+#else
+  int i, x, y;
+#endif
+
+#if 0
+  /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
+  /* only horizontal XOR vertical scroll direction allowed */
+  if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
+    return;
+#endif
+
+#if 1
+  if (bitmap_db_field2 == NULL)
+    bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
+
+  /* needed when blitting directly to same bitmap -- should not be needed with
+     recent SDL libraries, but apparently does not work in 1.2.11 directly */
+  BlitBitmap(drawto_field, bitmap_db_field2,
+            FX + TILEX * (dx == -1) - softscroll_offset,
+            FY + TILEY * (dy == -1) - softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
+  BlitBitmap(bitmap_db_field2, drawto_field,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
+
+#else
+
+#if 1
+  /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
+  int xsize = (BX2 - BX1 + 1);
+  int ysize = (BY2 - BY1 + 1);
+  int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
+  int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
+  int step  = (start < end ? +1 : -1);
+
+  for (i = start; i != end; i += step)
+  {
+    BlitBitmap(drawto_field, drawto_field,
+              FX + TILEX * (dx != 0 ? i + step : 0),
+              FY + TILEY * (dy != 0 ? i + step : 0),
+              TILEX * (dx != 0 ? 1 : xsize),
+              TILEY * (dy != 0 ? 1 : ysize),
+              FX + TILEX * (dx != 0 ? i : 0),
+              FY + TILEY * (dy != 0 ? i : 0));
+  }
+
+#else
+
+  int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
 
   BlitBitmap(drawto_field, drawto_field,
             FX + TILEX * (dx == -1) - softscroll_offset,
             FY + TILEY * (dy == -1) - softscroll_offset,
-            SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
-            SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
+            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
 
-  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++)
@@ -9577,11 +11896,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 &&
@@ -9599,7 +11922,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 =
@@ -9645,7 +11972,6 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 
   if (!player_can_move)
   {
-#if 1
     if (player->MovPos == 0)
     {
       player->is_moving = FALSE;
@@ -9654,14 +11980,6 @@ 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
-
-#if 0
-    return MP_NO_ACTION;
-#endif
   }
 
 #if 1
@@ -9696,18 +12014,9 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 #endif
 
   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
-#if 0
-#if USE_FIXED_DONT_RUN_INTO
-  if (can_move == MP_DONT_RUN_INTO)
-    return MP_MOVING;
-#endif
-#endif
   if (can_move != MP_MOVING)
     return can_move;
 
-#if USE_FIXED_DONT_RUN_INTO
-#endif
-
   /* check if DigField() has caused relocation of the player */
   if (player->jx != jx || player->jy != jy)
     return MP_NO_ACTION;       /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
@@ -9732,7 +12041,14 @@ 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 MP_MOVING;
 }
@@ -9800,6 +12116,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)))
@@ -9811,6 +12129,17 @@ 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;
 
@@ -9824,7 +12153,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 #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)))
     {
@@ -9915,6 +12244,13 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     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
   {
@@ -9980,8 +12316,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) */
@@ -10001,12 +12342,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)
   {
@@ -10028,10 +12363,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--;
@@ -10050,6 +12381,9 @@ 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 ||
+       Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
+       Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
        Feld[jx][jy] == EL_SP_EXIT_OPEN ||
        Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
     {
@@ -10058,7 +12392,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 */
@@ -10106,7 +12440,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;
 
@@ -10117,16 +12451,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_control_value[GAME_CONTROL_TIME] = TimeLeft;
 
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
            KillPlayer(&stored_player[i]);
       }
+#if 1
+      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      {
+       game_control_value[GAME_CONTROL_TIME] = TimePlayed;
+
+       DisplayGameControlValues();
+      }
+#else
       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
+#endif
     }
 
     if (tape.single_step && tape.recording && !tape.pausing &&
@@ -10725,6 +13074,23 @@ void KillPlayer(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+  /* 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;
 
@@ -10903,50 +13269,18 @@ int DigField(struct PlayerInfo *player,
           game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
 
-#if 0
-#if USE_FIXED_DONT_RUN_INTO
-  if (player_can_move && DONT_RUN_INTO(element))
-  {
-    if (element == EL_ACID && dx == 0 && dy == 1)
-    {
-      SplashAcid(x, y);
-      Feld[jx][jy] = EL_PLAYER_1;
-      InitMovingField(jx, jy, MV_DOWN);
-      Store[jx][jy] = EL_ACID;
-      ContinueMoving(jx, jy);
-      BuryPlayer(player);
-    }
-    else
-      TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
-
-    return MP_DONT_RUN_INTO;
-  }
-#endif
-#endif
-
-#if 1
-#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
-#endif
-
   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
     return MP_NO_ACTION;       /* field has no opening in this direction */
 
   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
     return MP_NO_ACTION;       /* field has no opening in this direction */
 
-#if 1
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
   {
     SplashAcid(x, y);
-    Feld[jx][jy] = EL_PLAYER_1;
+
+    Feld[jx][jy] = player->artwork_element;
     InitMovingField(jx, jy, MV_DOWN);
     Store[jx][jy] = EL_ACID;
     ContinueMoving(jx, jy);
@@ -10955,28 +13289,15 @@ int DigField(struct PlayerInfo *player,
     return MP_DONT_RUN_INTO;
   }
 #endif
-#endif
 
-#if 0
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
   {
-    if (element == EL_ACID && dx == 0 && dy == 1)
-    {
-      SplashAcid(x, y);
-      Feld[jx][jy] = EL_PLAYER_1;
-      InitMovingField(jx, jy, MV_DOWN);
-      Store[jx][jy] = EL_ACID;
-      ContinueMoving(jx, jy);
-      BuryPlayer(player);
-    }
-    else
-      TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+    TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
 
     return MP_DONT_RUN_INTO;
   }
 #endif
-#endif
 
 #if USE_FIXED_DONT_RUN_INTO
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
@@ -11003,16 +13324,26 @@ 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 MP_ACTION;
 
     return MP_NO_ACTION;
   }
 
+#if USE_PLAYER_GRAVITY
+  if (player->gravity && is_player && !player->is_auto_moving &&
+      canFallDown(player) && move_direction != MV_DOWN &&
+      !canMoveToValidFieldWithGravity(jx, jy, move_direction))
+    return MP_NO_ACTION;       /* player cannot walk here due to gravity */
+#else
   if (game.gravity && is_player && !player->is_auto_moving &&
       canFallDown(player) && move_direction != MV_DOWN &&
       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
     return MP_NO_ACTION;       /* player cannot walk here due to gravity */
+#endif
 
   if (player_can_move &&
       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
@@ -11036,6 +13367,9 @@ int DigField(struct PlayerInfo *player,
        return MP_NO_ACTION;
     }
     else if (element == EL_EXIT_OPEN ||
+            element == EL_EM_EXIT_OPEN ||
+            element == EL_STEEL_EXIT_OPEN ||
+            element == EL_EM_STEEL_EXIT_OPEN ||
             element == EL_SP_EXIT_OPEN ||
             element == EL_SP_EXIT_OPENING)
     {
@@ -11076,23 +13410,59 @@ int DigField(struct PlayerInfo *player,
       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
        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))
     {
       if (element == EL_SP_GRAVITY_PORT_LEFT ||
          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 */
@@ -11154,7 +13524,14 @@ int DigField(struct PlayerInfo *player,
     else if (element == EL_EXTRA_TIME && level.time > 0)
     {
       TimeLeft += level.extra_time;
+
+#if 1
+      game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Time(TimeLeft);
+#endif
     }
     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
     {
@@ -11169,7 +13546,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 (element == EL_DYNABOMB_INCREASE_NUMBER)
     {
@@ -11188,9 +13565,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))
     {
@@ -11220,7 +13602,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)
     {
@@ -11228,7 +13610,13 @@ int DigField(struct PlayerInfo *player,
       if (local_player->gems_still_needed < 0)
        local_player->gems_still_needed = 0;
 
+#if 1
+      game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Emeralds(local_player->gems_still_needed);
+#endif
     }
 
     RaiseScoreElement(element);
@@ -11300,6 +13688,7 @@ int DigField(struct PlayerInfo *player,
     }
 
     player->is_pushing = TRUE;
+    player->is_active = TRUE;
 
     if (!(IN_LEV_FIELD(nextx, nexty) &&
          (IS_FREE(nextx, nexty) ||
@@ -11352,7 +13741,8 @@ int DigField(struct PlayerInfo *player,
       if (local_player->sokobanfields_still_needed == 0 &&
          game.emulation == EMU_SOKOBAN)
       {
-       player->LevelSolved = player->GameOver = TRUE;
+       PlayerWins(player);
+
        PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
       }
     }
@@ -11412,11 +13802,7 @@ int DigField(struct PlayerInfo *player,
     {
       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);
@@ -11429,7 +13815,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);
     }
@@ -11438,7 +13826,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);
     }
@@ -11471,7 +13860,14 @@ int DigField(struct PlayerInfo *player,
       if (level.time > 0 || level.use_time_orb_bug)
       {
        TimeLeft += level.time_orb_time;
+
+#if 1
+       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
       }
 
       ResetGfxAnimation(x, y);
@@ -11484,11 +13880,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];
 
@@ -11552,7 +13944,10 @@ int DigField(struct PlayerInfo *player,
   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;
@@ -11566,6 +13961,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;
@@ -11593,8 +13990,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;
 
@@ -11606,11 +14009,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) == MP_NO_ACTION)
     return FALSE;
 
   player->is_snapping = TRUE;
+  player->is_active = TRUE;
 
   if (player->MovPos == 0)
   {
@@ -11641,6 +14047,8 @@ boolean DropElement(struct PlayerInfo *player)
                      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
                      EL_UNDEFINED);
 
+  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
@@ -11668,6 +14076,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;
@@ -11689,7 +14101,7 @@ 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;
@@ -11749,12 +14161,21 @@ boolean DropElement(struct PlayerInfo *player)
     nexty = dropy + GET_DY_FROM_DIR(move_direction);
 
     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;
 
@@ -11879,9 +14300,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)
   {
@@ -12027,7 +14451,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:
@@ -12036,16 +14460,47 @@ void PlayLevelSound_EM(int x, int y, int element_em, int sample)
   }
 }
 
+#if 0
+void ChangeTime(int value)
+{
+  int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
+
+  *time += value;
+
+  /* EMC game engine uses value from time counter of RND game engine */
+  level.native_em_level->lev->time = *time;
+
+  DrawGameValue_Time(*time);
+}
+
+void RaiseScore(int value)
+{
+  /* EMC game engine and RND game engine have separate score counters */
+  int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+               &level.native_em_level->lev->score : &local_player->score);
+
+  *score += value;
+
+  DrawGameValue_Score(*score);
+}
+#endif
+
 void RaiseScore(int value)
 {
   local_player->score += value;
 
+#if 1
+  game_control_value[GAME_CONTROL_SCORE] = 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:
@@ -12114,6 +14569,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:
@@ -12122,25 +14578,51 @@ 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 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);
@@ -12152,6 +14634,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;
+
+  RequestQuitGameExt(skip_request, quick_quit,
+                    "Do you really want to quit the game ?");
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* random generator functions                                                */
+/* ------------------------------------------------------------------------- */
+
+unsigned int InitEngineRandom_RND(long 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                                   */
+/* ------------------------------------------------------------------------- */
+
+#define ARGS_ADDRESS_AND_SIZEOF(x)             (&(x)), (sizeof(x))
+
+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];
+};
+
+struct EngineSnapshotNodeInfo
+{
+  void *buffer_orig;
+  void *buffer_copy;
+  int size;
+};
+
+static struct EngineSnapshotInfo engine_snapshot_rnd;
+static ListNode *engine_snapshot_list = NULL;
+static char *snapshot_level_identifier = NULL;
+static int snapshot_level_nr = -1;
+
+void FreeEngineSnapshot()
+{
+  while (engine_snapshot_list != NULL)
+    deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
+                      checked_free);
+
+  setString(&snapshot_level_identifier, NULL);
+  snapshot_level_nr = -1;
+}
+
+static void SaveEngineSnapshotValues_RND()
+{
+  static 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;
+
+    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 * 4 + j] = graphic;
+      engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
+    }
+  }
+}
+
+static void LoadEngineSnapshotValues_RND()
+{
+  unsigned long 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 * 4 + j];
+      int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + 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");
+  }
+}
+
+static void SaveEngineSnapshotBuffer(void *buffer, int size)
+{
+  struct EngineSnapshotNodeInfo *bi =
+    checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
+
+  bi->buffer_orig = buffer;
+  bi->buffer_copy = checked_malloc(size);
+  bi->size = size;
+
+  memcpy(bi->buffer_copy, buffer, size);
+
+  addNodeToList(&engine_snapshot_list, NULL, bi);
+}
+
+void SaveEngineSnapshot()
+{
+  FreeEngineSnapshot();                /* free previous snapshot, if needed */
+
+  if (level_editor_test_game)  /* do not save snapshots from editor */
+    return;
+
+  /* copy some special values to a structure better suited for the snapshot */
+
+  SaveEngineSnapshotValues_RND();
+  SaveEngineSnapshotValues_EM();
+
+  /* save values stored in special snapshot structure */
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+
+  /* 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;
+  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
+}
+
+static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
+{
+  memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
+}
+
+void LoadEngineSnapshot()
+{
+  ListNode *node = engine_snapshot_list;
+
+  if (engine_snapshot_list == NULL)
+    return;
+
+  while (node != NULL)
+  {
+    LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
+
+    node = node->next;
+  }
+
+  /* restore special values from snapshot structure */
+
+  LoadEngineSnapshotValues_RND();
+  LoadEngineSnapshotValues_EM();
+}
+
+boolean CheckEngineSnapshot()
+{
+  return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
+         snapshot_level_nr == level_nr);
+}
+
 
 /* ---------- new game button stuff ---------------------------------------- */
 
@@ -12172,11 +14973,50 @@ void RequestQuitGame(boolean ask_if_really_quit)
 
 static struct
 {
-  int x, y;
+  int *x, *y;
+  int gd_x, gd_y;
   int gadget_id;
   char *infotext;
 } gamebutton_info[NUM_GAME_BUTTONS] =
 {
+#if 1
+  {
+    &game.button.stop.x,       &game.button.stop.y,
+    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_STOP,
+    "stop game"
+  },
+  {
+    &game.button.pause.x,      &game.button.pause.y,
+    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PAUSE,
+    "pause game"
+  },
+  {
+    &game.button.play.x,       &game.button.play.y,
+    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PLAY,
+    "play game"
+  },
+  {
+    &game.button.sound_music.x,        &game.button.sound_music.y,
+    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_MUSIC,
+    "background music on/off"
+  },
+  {
+    &game.button.sound_loops.x,        &game.button.sound_loops.y,
+    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_LOOPS,
+    "sound loops on/off"
+  },
+  {
+    &game.button.sound_simple.x,&game.button.sound_simple.y,
+    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_SIMPLE,
+    "normal sounds on/off"
+  }
+#else
   {
     GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
     GAME_CTRL_ID_STOP,
@@ -12207,6 +15047,7 @@ static struct
     SOUND_CTRL_ID_SIMPLE,
     "normal sounds on/off"
   }
+#endif
 };
 
 void CreateGameButtons()
@@ -12220,12 +15061,15 @@ void CreateGameButtons()
     int button_type;
     boolean checked;
     unsigned long event_mask;
+    int x, y;
     int gd_xoffset, gd_yoffset;
     int gd_x1, gd_x2, gd_y1, gd_y2;
     int id = i;
 
-    gd_xoffset = gamebutton_info[i].x;
-    gd_yoffset = gamebutton_info[i].y;
+    x = DX + *gamebutton_info[i].x;
+    y = DY + *gamebutton_info[i].y;
+    gd_xoffset = gamebutton_info[i].gd_x;
+    gd_yoffset = gamebutton_info[i].gd_y;
     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
 
@@ -12253,8 +15097,13 @@ void CreateGameButtons()
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_INFO_TEXT, gamebutton_info[i].infotext,
+#if 1
+                     GDI_X, x,
+                     GDI_Y, y,
+#else
                      GDI_X, DX + gd_xoffset,
                      GDI_Y, DY + gd_yoffset,
+#endif
                      GDI_WIDTH, GAME_BUTTON_XSIZE,
                      GDI_HEIGHT, GAME_BUTTON_YSIZE,
                      GDI_TYPE, button_type,
@@ -12309,7 +15158,10 @@ static void HandleGameButtons(struct GadgetInfo *gi)
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
-      RequestQuitGame(TRUE);
+      if (tape.playing)
+       TapeStop();
+      else
+       RequestQuitGame(TRUE);
       break;
 
     case GAME_CTRL_ID_PAUSE:
@@ -12336,7 +15188,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
 #endif
        {
          tape.pausing = FALSE;
-         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
        }
       }
       break;