updated contact info in source file headers
[rocksndiamonds.git] / src / game.c
index 6b920a51ca2d40b9ca2f097e6d3fa865ff0f2c81..28905e562b1ef07e942ca56e23be27b128cfade2 100644 (file)
@@ -1,15 +1,13 @@
-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
-*----------------------------------------------------------*
-* (c) 1995-2006 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* game.c                                                   *
-***********************************************************/
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// game.c
+// ============================================================================
 
 #include "libgame/libgame.h"
 
 #include "tape.h"
 #include "network.h"
 
+
+/* DEBUG SETTINGS */
+#define DEBUG_INIT_PLAYER      1
+#define DEBUG_PLAYER_ACTIONS   0
+
 /* EXPERIMENTAL STUFF */
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 
 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF          * 1)
 #define USE_FIX_IMPACT_COLLISION       (USE_NEW_STUFF          * 1)
+#define USE_FIX_CE_ACTION_WITH_PLAYER  (USE_NEW_STUFF          * 1)
+#define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF          * 1)
+
+#define USE_PLAYER_REANIMATION         (USE_NEW_STUFF          * 1)
 
 #define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
 
+#define USE_NEW_PLAYER_ASSIGNMENTS     (USE_NEW_STUFF          * 1)
+
+#define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 0)
+
+#if USE_DELAYED_GFX_REDRAW
+#define TEST_DrawLevelField(x, y)                              \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE
+#define TEST_DrawLevelFieldCrumbled(x, y)                      \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y)            \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
+#define TEST_DrawTwinkleOnField(x, y)                          \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
+#else
+#define TEST_DrawLevelField(x, y)                              \
+            DrawLevelField(x, y)
+#define TEST_DrawLevelFieldCrumbled(x, y)                      \
+            DrawLevelFieldCrumbled(x, y)
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y)            \
+            DrawLevelFieldCrumbledNeighbours(x, y)
+#define TEST_DrawTwinkleOnField(x, y)                          \
+            DrawTwinkleOnField(x, y)
+#endif
+
 
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define EX_TYPE_DYNA           (1 << 4)
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
-#if 1
 #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_MENU_XPOS(p))
-#define PANEL_YPOS(p)          (DY + ALIGNED_MENU_YPOS(p))
-#else
-#define        PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
-#define PANEL_XPOS(p)          (ALIGNED_XPOS((p).x, (p).width, (p).align))
-#define PANEL_YPOS(p)          ((p).y)
-#endif
-
-/* special positions in the game control window (relative to control window) */
-#define XX_LEVEL1              (PANEL_XPOS(game.panel.level))
-#define XX_LEVEL2              (PANEL_XPOS(game.panel.level) - 1)
-#define XX_LEVEL               (PANEL_XPOS(game.panel.level))
-#define YY_LEVEL               (PANEL_YPOS(game.panel.level))
-#define XX_EMERALDS            (PANEL_XPOS(game.panel.gems))
-#define YY_EMERALDS            (PANEL_YPOS(game.panel.gems))
-#define XX_DYNAMITE            (PANEL_XPOS(game.panel.inventory))
-#define YY_DYNAMITE            (PANEL_YPOS(game.panel.inventory))
-#define XX_KEYS                        (PANEL_XPOS(game.panel.keys))
-#define YY_KEYS                        (PANEL_YPOS(game.panel.keys))
-#define XX_SCORE               (PANEL_XPOS(game.panel.score))
-#define YY_SCORE               (PANEL_YPOS(game.panel.score))
-#define XX_TIME1               (PANEL_XPOS(game.panel.time))
-#define XX_TIME2               (PANEL_XPOS(game.panel.time) + 1)
-#define XX_TIME                        (PANEL_XPOS(game.panel.time))
-#define YY_TIME                        (PANEL_YPOS(game.panel.time))
-
-/* special positions in the game control window (relative to main window) */
-#define DX_LEVEL1              (DX + XX_LEVEL1)
-#define DX_LEVEL2              (DX + XX_LEVEL2)
-#define DX_LEVEL               (DX + XX_LEVEL)
-#define DY_LEVEL               (DY + YY_LEVEL)
-#define DX_EMERALDS            (DX + XX_EMERALDS)
-#define DY_EMERALDS            (DY + YY_EMERALDS)
-#define DX_DYNAMITE            (DX + XX_DYNAMITE)
-#define DY_DYNAMITE            (DY + YY_DYNAMITE)
-#define DX_KEYS                        (DX + XX_KEYS)
-#define DY_KEYS                        (DY + YY_KEYS)
-#define DX_SCORE               (DX + XX_SCORE)
-#define DY_SCORE               (DY + YY_SCORE)
-#define DX_TIME1               (DX + XX_TIME1)
-#define DX_TIME2               (DX + XX_TIME2)
-#define DX_TIME                        (DX + XX_TIME)
-#define DY_TIME                        (DY + YY_TIME)
+#define PANEL_XPOS(p)          (DX + ALIGNED_TEXT_XPOS(p))
+#define PANEL_YPOS(p)          (DY + ALIGNED_TEXT_YPOS(p))
 
-#if 0
 /* game panel display and control definitions */
+#define GAME_PANEL_LEVEL_NUMBER                        0
+#define GAME_PANEL_GEMS                                1
+#define GAME_PANEL_INVENTORY_COUNT             2
+#define GAME_PANEL_INVENTORY_FIRST_1           3
+#define GAME_PANEL_INVENTORY_FIRST_2           4
+#define GAME_PANEL_INVENTORY_FIRST_3           5
+#define GAME_PANEL_INVENTORY_FIRST_4           6
+#define GAME_PANEL_INVENTORY_FIRST_5           7
+#define GAME_PANEL_INVENTORY_FIRST_6           8
+#define GAME_PANEL_INVENTORY_FIRST_7           9
+#define GAME_PANEL_INVENTORY_FIRST_8           10
+#define GAME_PANEL_INVENTORY_LAST_1            11
+#define GAME_PANEL_INVENTORY_LAST_2            12
+#define GAME_PANEL_INVENTORY_LAST_3            13
+#define GAME_PANEL_INVENTORY_LAST_4            14
+#define GAME_PANEL_INVENTORY_LAST_5            15
+#define GAME_PANEL_INVENTORY_LAST_6            16
+#define GAME_PANEL_INVENTORY_LAST_7            17
+#define GAME_PANEL_INVENTORY_LAST_8            18
+#define GAME_PANEL_KEY_1                       19
+#define GAME_PANEL_KEY_2                       20
+#define GAME_PANEL_KEY_3                       21
+#define GAME_PANEL_KEY_4                       22
+#define GAME_PANEL_KEY_5                       23
+#define GAME_PANEL_KEY_6                       24
+#define GAME_PANEL_KEY_7                       25
+#define GAME_PANEL_KEY_8                       26
+#define GAME_PANEL_KEY_WHITE                   27
+#define GAME_PANEL_KEY_WHITE_COUNT             28
+#define GAME_PANEL_SCORE                       29
+#define GAME_PANEL_HIGHSCORE                   30
+#define GAME_PANEL_TIME                                31
+#define GAME_PANEL_TIME_HH                     32
+#define GAME_PANEL_TIME_MM                     33
+#define GAME_PANEL_TIME_SS                     34
+#define GAME_PANEL_FRAME                       35
+#define GAME_PANEL_SHIELD_NORMAL               36
+#define GAME_PANEL_SHIELD_NORMAL_TIME          37
+#define GAME_PANEL_SHIELD_DEADLY               38
+#define GAME_PANEL_SHIELD_DEADLY_TIME          39
+#define GAME_PANEL_EXIT                                40
+#define GAME_PANEL_EMC_MAGIC_BALL              41
+#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       42
+#define GAME_PANEL_LIGHT_SWITCH                        43
+#define GAME_PANEL_LIGHT_SWITCH_TIME           44
+#define GAME_PANEL_TIMEGATE_SWITCH             45
+#define GAME_PANEL_TIMEGATE_SWITCH_TIME                46
+#define GAME_PANEL_SWITCHGATE_SWITCH           47
+#define GAME_PANEL_EMC_LENSES                  48
+#define GAME_PANEL_EMC_LENSES_TIME             49
+#define GAME_PANEL_EMC_MAGNIFIER               50
+#define GAME_PANEL_EMC_MAGNIFIER_TIME          51
+#define GAME_PANEL_BALLOON_SWITCH              52
+#define GAME_PANEL_DYNABOMB_NUMBER             53
+#define GAME_PANEL_DYNABOMB_SIZE               54
+#define GAME_PANEL_DYNABOMB_POWER              55
+#define GAME_PANEL_PENGUINS                    56
+#define GAME_PANEL_SOKOBAN_OBJECTS             57
+#define GAME_PANEL_SOKOBAN_FIELDS              58
+#define GAME_PANEL_ROBOT_WHEEL                 59
+#define GAME_PANEL_CONVEYOR_BELT_1             60
+#define GAME_PANEL_CONVEYOR_BELT_2             61
+#define GAME_PANEL_CONVEYOR_BELT_3             62
+#define GAME_PANEL_CONVEYOR_BELT_4             63
+#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      64
+#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      65
+#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      66
+#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      67
+#define GAME_PANEL_MAGIC_WALL                  68
+#define GAME_PANEL_MAGIC_WALL_TIME             69
+#define GAME_PANEL_GRAVITY_STATE               70
+#define GAME_PANEL_GRAPHIC_1                   71
+#define GAME_PANEL_GRAPHIC_2                   72
+#define GAME_PANEL_GRAPHIC_3                   73
+#define GAME_PANEL_GRAPHIC_4                   74
+#define GAME_PANEL_GRAPHIC_5                   75
+#define GAME_PANEL_GRAPHIC_6                   76
+#define GAME_PANEL_GRAPHIC_7                   77
+#define GAME_PANEL_GRAPHIC_8                   78
+#define GAME_PANEL_ELEMENT_1                   79
+#define GAME_PANEL_ELEMENT_2                   80
+#define GAME_PANEL_ELEMENT_3                   81
+#define GAME_PANEL_ELEMENT_4                   82
+#define GAME_PANEL_ELEMENT_5                   83
+#define GAME_PANEL_ELEMENT_6                   84
+#define GAME_PANEL_ELEMENT_7                   85
+#define GAME_PANEL_ELEMENT_8                   86
+#define GAME_PANEL_ELEMENT_COUNT_1             87
+#define GAME_PANEL_ELEMENT_COUNT_2             88
+#define GAME_PANEL_ELEMENT_COUNT_3             89
+#define GAME_PANEL_ELEMENT_COUNT_4             90
+#define GAME_PANEL_ELEMENT_COUNT_5             91
+#define GAME_PANEL_ELEMENT_COUNT_6             92
+#define GAME_PANEL_ELEMENT_COUNT_7             93
+#define GAME_PANEL_ELEMENT_COUNT_8             94
+#define GAME_PANEL_CE_SCORE_1                  95
+#define GAME_PANEL_CE_SCORE_2                  96
+#define GAME_PANEL_CE_SCORE_3                  97
+#define GAME_PANEL_CE_SCORE_4                  98
+#define GAME_PANEL_CE_SCORE_5                  99
+#define GAME_PANEL_CE_SCORE_6                  100
+#define GAME_PANEL_CE_SCORE_7                  101
+#define GAME_PANEL_CE_SCORE_8                  102
+#define GAME_PANEL_CE_SCORE_1_ELEMENT          103
+#define GAME_PANEL_CE_SCORE_2_ELEMENT          104
+#define GAME_PANEL_CE_SCORE_3_ELEMENT          105
+#define GAME_PANEL_CE_SCORE_4_ELEMENT          106
+#define GAME_PANEL_CE_SCORE_5_ELEMENT          107
+#define GAME_PANEL_CE_SCORE_6_ELEMENT          108
+#define GAME_PANEL_CE_SCORE_7_ELEMENT          109
+#define GAME_PANEL_CE_SCORE_8_ELEMENT          110
+#define GAME_PANEL_PLAYER_NAME                 111
+#define GAME_PANEL_LEVEL_NAME                  112
+#define GAME_PANEL_LEVEL_AUTHOR                        113
+
+#define NUM_GAME_PANEL_CONTROLS                        114
+
+struct GamePanelOrderInfo
+{
+  int nr;
+  int sort_priority;
+};
+
+static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
 
-#define GAME_CONTROL_LEVEL                     0
-#define GAME_CONTROL_GEMS                      1
-#define GAME_CONTROL_INVENTORY                 2
-#define GAME_CONTROL_KEYS                      3
-#define GAME_CONTROL_SCORE                     4
-#define GAME_CONTROL_TIME                      5
-#define GAME_CONTROL_TIME_HH                   6
-#define GAME_CONTROL_TIME_MM                   7
-#define GAME_CONTROL_TIME_SS                   8
-#define GAME_CONTROL_DROP_NEXT_1               9
-#define GAME_CONTROL_DROP_NEXT_2               10
-#define GAME_CONTROL_DROP_NEXT_3               11
-#define GAME_CONTROL_DROP_NEXT_4               12
-#define GAME_CONTROL_DROP_NEXT_5               13
-#define GAME_CONTROL_DROP_NEXT_6               14
-#define GAME_CONTROL_DROP_NEXT_7               15
-#define GAME_CONTROL_DROP_NEXT_8               16
-#define GAME_CONTROL_EMC_KEYS                  17
-#define GAME_CONTROL_KEY_1                     18
-#define GAME_CONTROL_KEY_2                     19
-#define GAME_CONTROL_KEY_3                     20
-#define GAME_CONTROL_KEY_4                     21
-#define GAME_CONTROL_KEY_5                     22
-#define GAME_CONTROL_KEY_6                     23
-#define GAME_CONTROL_KEY_7                     24
-#define GAME_CONTROL_KEY_8                     25
-#define GAME_CONTROL_KEY_WHITE                 26
-#define GAME_CONTROL_KEY_WHITE_COUNT           27
-#define GAME_CONTROL_SHIELD_NORMAL             28
-#define GAME_CONTROL_SHIELD_NORMAL_TIME                29
-#define GAME_CONTROL_SHIELD_DEADLY             30
-#define GAME_CONTROL_SHIELD_DEADLY_TIME                31
-#define GAME_CONTROL_EXIT                      32
-#define GAME_CONTROL_EM_EXIT                   33
-#define GAME_CONTROL_SP_EXIT                   34
-#define GAME_CONTROL_STEEL_EXIT                        35
-#define GAME_CONTROL_EM_STEEL_EXIT             36
-#define GAME_CONTROL_EMC_MAGIC_BALL            37
-#define GAME_CONTROL_EMC_MAGIC_BALL_TIME       38
-#define GAME_CONTROL_LIGHT_SWITCH              39
-#define GAME_CONTROL_LIGHT_SWITCH_TIME         40
-#define GAME_CONTROL_TIMEGATE_SWITCH           41
-#define GAME_CONTROL_TIMEGATE_SWITCH_TIME      42
-#define GAME_CONTROL_SWITCHGATE_SWITCH         43
-#define GAME_CONTROL_EMC_LENSES                        44
-#define GAME_CONTROL_EMC_LENSES_TIME           45
-#define GAME_CONTROL_EMC_MAGNIFIER             46
-#define GAME_CONTROL_EMC_MAGNIFIER_TIME                47
-#define GAME_CONTROL_BALLOON_SWITCH            48
-#define GAME_CONTROL_DYNABOMB_NUMBER           49
-#define GAME_CONTROL_DYNABOMB_SIZE             50
-#define GAME_CONTROL_DYNABOMB_POWER            51
-#define GAME_CONTROL_PENGUINS                  52
-#define GAME_CONTROL_SOKOBAN_OBJECTS           53
-#define GAME_CONTROL_SOKOBAN_FIELDS            54
-#define GAME_CONTROL_ROBOT_WHEEL               55
-#define GAME_CONTROL_CONVEYOR_BELT_1           56
-#define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH    57
-#define GAME_CONTROL_CONVEYOR_BELT_2           58
-#define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH    59
-#define GAME_CONTROL_CONVEYOR_BELT_3           60
-#define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH    61
-#define GAME_CONTROL_CONVEYOR_BELT_4           62
-#define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH    63
-#define GAME_CONTROL_MAGIC_WALL                        64
-#define GAME_CONTROL_MAGIC_WALL_TIME           65
-#define GAME_CONTROL_BD_MAGIC_WALL             66
-#define GAME_CONTROL_DC_MAGIC_WALL             67
-#define GAME_CONTROL_PLAYER_NAME               68
-#define GAME_CONTROL_LEVEL_NAME                        69
-#define GAME_CONTROL_LEVEL_AUTHOR              70
-
-struct GameControlInfo
+struct GamePanelControlInfo
 {
   int nr;
 
-  struct TextPosInfo *pos_text;
+  struct TextPosInfo *pos;
   int type;
-  void *ptr;
+
+  int value, last_value;
+  int frame, last_frame;
+  int gfx_frame;
+  int gfx_random;
 };
 
-static struct GameControlInfo game_controls[] =
+static struct GamePanelControlInfo game_panel_controls[] =
 {
   {
-    GAME_CONTROL_LEVEL,
-    &game.panel.level,
+    GAME_PANEL_LEVEL_NUMBER,
+    &game.panel.level_number,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_GEMS,
+    GAME_PANEL_GEMS,
     &game.panel.gems,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_INVENTORY,
-    &game.panel.inventory,
+    GAME_PANEL_INVENTORY_COUNT,
+    &game.panel.inventory_count,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_KEYS,
-    &game.panel.keys,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_FIRST_1,
+    &game.panel.inventory_first[0],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_SCORE,
-    &game.panel.score,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_FIRST_2,
+    &game.panel.inventory_first[1],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_TIME,
-    &game.panel.time,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_FIRST_3,
+    &game.panel.inventory_first[2],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_TIME_HH,
-    &game.panel.time_hh,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_FIRST_4,
+    &game.panel.inventory_first[3],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_TIME_MM,
-    &game.panel.time_mm,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_FIRST_5,
+    &game.panel.inventory_first[4],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_TIME_SS,
-    &game.panel.time_ss,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_FIRST_6,
+    &game.panel.inventory_first[5],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DROP_NEXT_1,
-    &game.panel.drop_next_1,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_FIRST_7,
+    &game.panel.inventory_first[6],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DROP_NEXT_2,
-    &game.panel.drop_next_2,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_FIRST_8,
+    &game.panel.inventory_first[7],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DROP_NEXT_3,
-    &game.panel.drop_next_3,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_LAST_1,
+    &game.panel.inventory_last[0],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DROP_NEXT_4,
-    &game.panel.drop_next_4,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_LAST_2,
+    &game.panel.inventory_last[1],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DROP_NEXT_5,
-    &game.panel.drop_next_5,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_LAST_3,
+    &game.panel.inventory_last[2],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DROP_NEXT_6,
-    &game.panel.drop_next_6,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_LAST_4,
+    &game.panel.inventory_last[3],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DROP_NEXT_7,
-    &game.panel.drop_next_7,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_LAST_5,
+    &game.panel.inventory_last[4],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DROP_NEXT_8,
-    &game.panel.drop_next_8,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_LAST_6,
+    &game.panel.inventory_last[5],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_EMC_KEYS,
-    &game.panel.emc_keys,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_LAST_7,
+    &game.panel.inventory_last[6],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_1,
-    &game.panel.key_1,
-    TYPE_INTEGER,
+    GAME_PANEL_INVENTORY_LAST_8,
+    &game.panel.inventory_last[7],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_2,
-    &game.panel.key_2,
-    TYPE_INTEGER,
+    GAME_PANEL_KEY_1,
+    &game.panel.key[0],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_3,
-    &game.panel.key_3,
-    TYPE_INTEGER,
+    GAME_PANEL_KEY_2,
+    &game.panel.key[1],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_4,
-    &game.panel.key_4,
-    TYPE_INTEGER,
+    GAME_PANEL_KEY_3,
+    &game.panel.key[2],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_5,
-    &game.panel.key_5,
-    TYPE_INTEGER,
+    GAME_PANEL_KEY_4,
+    &game.panel.key[3],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_6,
-    &game.panel.key_6,
-    TYPE_INTEGER,
+    GAME_PANEL_KEY_5,
+    &game.panel.key[4],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_7,
-    &game.panel.key_7,
-    TYPE_INTEGER,
+    GAME_PANEL_KEY_6,
+    &game.panel.key[5],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_8,
-    &game.panel.key_8,
-    TYPE_INTEGER,
+    GAME_PANEL_KEY_7,
+    &game.panel.key[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_KEY_8,
+    &game.panel.key[7],
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_WHITE,
+    GAME_PANEL_KEY_WHITE,
     &game.panel.key_white,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_KEY_WHITE_COUNT,
+    GAME_PANEL_KEY_WHITE_COUNT,
     &game.panel.key_white_count,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_SHIELD_NORMAL,
-    &game.panel.shield_normal,
+    GAME_PANEL_SCORE,
+    &game.panel.score,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_SHIELD_NORMAL_TIME,
-    &game.panel.shield_normal_time,
+    GAME_PANEL_HIGHSCORE,
+    &game.panel.highscore,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_SHIELD_DEADLY,
-    &game.panel.shield_deadly,
+    GAME_PANEL_TIME,
+    &game.panel.time,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_SHIELD_DEADLY_TIME,
-    &game.panel.shield_deadly_time,
+    GAME_PANEL_TIME_HH,
+    &game.panel.time_hh,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_EXIT,
-    &game.panel.exit,
+    GAME_PANEL_TIME_MM,
+    &game.panel.time_mm,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_EM_EXIT,
-    &game.panel.em_exit,
+    GAME_PANEL_TIME_SS,
+    &game.panel.time_ss,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_SP_EXIT,
-    &game.panel.sp_exit,
+    GAME_PANEL_FRAME,
+    &game.panel.frame,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_STEEL_EXIT,
-    &game.panel.steel_exit,
+    GAME_PANEL_SHIELD_NORMAL,
+    &game.panel.shield_normal,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_SHIELD_NORMAL_TIME,
+    &game.panel.shield_normal_time,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_EM_STEEL_EXIT,
-    &game.panel.em_steel_exit,
+    GAME_PANEL_SHIELD_DEADLY,
+    &game.panel.shield_deadly,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_SHIELD_DEADLY_TIME,
+    &game.panel.shield_deadly_time,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_EMC_MAGIC_BALL,
+    GAME_PANEL_EXIT,
+    &game.panel.exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_EMC_MAGIC_BALL,
     &game.panel.emc_magic_ball,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_EMC_MAGIC_BALL_TIME,
-    &game.panel.emc_magic_ball_time,
-    TYPE_INTEGER,
+    GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
+    &game.panel.emc_magic_ball_switch,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_LIGHT_SWITCH,
+    GAME_PANEL_LIGHT_SWITCH,
     &game.panel.light_switch,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_LIGHT_SWITCH_TIME,
+    GAME_PANEL_LIGHT_SWITCH_TIME,
     &game.panel.light_switch_time,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_TIMEGATE_SWITCH,
+    GAME_PANEL_TIMEGATE_SWITCH,
     &game.panel.timegate_switch,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_TIMEGATE_SWITCH_TIME,
+    GAME_PANEL_TIMEGATE_SWITCH_TIME,
     &game.panel.timegate_switch_time,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_SWITCHGATE_SWITCH,
+    GAME_PANEL_SWITCHGATE_SWITCH,
     &game.panel.switchgate_switch,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_EMC_LENSES,
+    GAME_PANEL_EMC_LENSES,
     &game.panel.emc_lenses,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_EMC_LENSES_TIME,
+    GAME_PANEL_EMC_LENSES_TIME,
     &game.panel.emc_lenses_time,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_EMC_MAGNIFIER,
+    GAME_PANEL_EMC_MAGNIFIER,
     &game.panel.emc_magnifier,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_EMC_MAGNIFIER_TIME,
+    GAME_PANEL_EMC_MAGNIFIER_TIME,
     &game.panel.emc_magnifier_time,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_BALLOON_SWITCH,
+    GAME_PANEL_BALLOON_SWITCH,
     &game.panel.balloon_switch,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_DYNABOMB_NUMBER,
+    GAME_PANEL_DYNABOMB_NUMBER,
     &game.panel.dynabomb_number,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_DYNABOMB_SIZE,
+    GAME_PANEL_DYNABOMB_SIZE,
     &game.panel.dynabomb_size,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_DYNABOMB_POWER,
+    GAME_PANEL_DYNABOMB_POWER,
     &game.panel.dynabomb_power,
-    TYPE_INTEGER,
+    TYPE_ELEMENT,
   },
   {
-    GAME_CONTROL_PENGUINS,
+    GAME_PANEL_PENGUINS,
     &game.panel.penguins,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_SOKOBAN_OBJECTS,
+    GAME_PANEL_SOKOBAN_OBJECTS,
     &game.panel.sokoban_objects,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_SOKOBAN_FIELDS,
+    GAME_PANEL_SOKOBAN_FIELDS,
     &game.panel.sokoban_fields,
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_ROBOT_WHEEL,
+    GAME_PANEL_ROBOT_WHEEL,
     &game.panel.robot_wheel,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_1,
+    &game.panel.conveyor_belt[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_2,
+    &game.panel.conveyor_belt[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_3,
+    &game.panel.conveyor_belt[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_4,
+    &game.panel.conveyor_belt[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
+    &game.panel.conveyor_belt_switch[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
+    &game.panel.conveyor_belt_switch[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
+    &game.panel.conveyor_belt_switch[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
+    &game.panel.conveyor_belt_switch[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_MAGIC_WALL,
+    &game.panel.magic_wall,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_MAGIC_WALL_TIME,
+    &game.panel.magic_wall_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_GRAVITY_STATE,
+    &game.panel.gravity_state,
+    TYPE_STRING,
+  },
+  {
+    GAME_PANEL_GRAPHIC_1,
+    &game.panel.graphic[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_2,
+    &game.panel.graphic[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_3,
+    &game.panel.graphic[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_4,
+    &game.panel.graphic[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_5,
+    &game.panel.graphic[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_6,
+    &game.panel.graphic[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_7,
+    &game.panel.graphic[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_8,
+    &game.panel.graphic[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_1,
+    &game.panel.element[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_2,
+    &game.panel.element[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_3,
+    &game.panel.element[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_4,
+    &game.panel.element[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_5,
+    &game.panel.element[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_6,
+    &game.panel.element[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_7,
+    &game.panel.element[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_8,
+    &game.panel.element[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_1,
+    &game.panel.element_count[0],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_CONVEYOR_BELT_1,
-    &game.panel.conveyor_belt_1,
+    GAME_PANEL_ELEMENT_COUNT_2,
+    &game.panel.element_count[1],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
-    &game.panel.conveyor_belt_1_switch,
+    GAME_PANEL_ELEMENT_COUNT_3,
+    &game.panel.element_count[2],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_CONVEYOR_BELT_2,
-    &game.panel.conveyor_belt_2,
+    GAME_PANEL_ELEMENT_COUNT_4,
+    &game.panel.element_count[3],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
-    &game.panel.conveyor_belt_2_switch,
+    GAME_PANEL_ELEMENT_COUNT_5,
+    &game.panel.element_count[4],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_CONVEYOR_BELT_3,
-    &game.panel.conveyor_belt_3,
+    GAME_PANEL_ELEMENT_COUNT_6,
+    &game.panel.element_count[5],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
-    &game.panel.conveyor_belt_3_switch,
+    GAME_PANEL_ELEMENT_COUNT_7,
+    &game.panel.element_count[6],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_CONVEYOR_BELT_4,
-    &game.panel.conveyor_belt_4,
+    GAME_PANEL_ELEMENT_COUNT_8,
+    &game.panel.element_count[7],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
-    &game.panel.conveyor_belt_4_switch,
+    GAME_PANEL_CE_SCORE_1,
+    &game.panel.ce_score[0],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_MAGIC_WALL,
-    &game.panel.magic_wall,
+    GAME_PANEL_CE_SCORE_2,
+    &game.panel.ce_score[1],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_MAGIC_WALL_TIME,
-    &game.panel.magic_wall_time,
+    GAME_PANEL_CE_SCORE_3,
+    &game.panel.ce_score[2],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_BD_MAGIC_WALL,
-    &game.panel.bd_magic_wall,
+    GAME_PANEL_CE_SCORE_4,
+    &game.panel.ce_score[3],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_DC_MAGIC_WALL,
-    &game.panel.dc_magic_wall,
+    GAME_PANEL_CE_SCORE_5,
+    &game.panel.ce_score[4],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_PLAYER_NAME,
-    &game.panel.player_name,
+    GAME_PANEL_CE_SCORE_6,
+    &game.panel.ce_score[5],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_LEVEL_NAME,
-    &game.panel.level_name,
+    GAME_PANEL_CE_SCORE_7,
+    &game.panel.ce_score[6],
     TYPE_INTEGER,
   },
   {
-    GAME_CONTROL_LEVEL_AUTHOR,
-    &game.panel.level_author,
+    GAME_PANEL_CE_SCORE_8,
+    &game.panel.ce_score[7],
     TYPE_INTEGER,
   },
+  {
+    GAME_PANEL_CE_SCORE_1_ELEMENT,
+    &game.panel.ce_score_element[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_2_ELEMENT,
+    &game.panel.ce_score_element[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_3_ELEMENT,
+    &game.panel.ce_score_element[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_4_ELEMENT,
+    &game.panel.ce_score_element[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_5_ELEMENT,
+    &game.panel.ce_score_element[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_6_ELEMENT,
+    &game.panel.ce_score_element[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_7_ELEMENT,
+    &game.panel.ce_score_element[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_CE_SCORE_8_ELEMENT,
+    &game.panel.ce_score_element[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_PLAYER_NAME,
+    &game.panel.player_name,
+    TYPE_STRING,
+  },
+  {
+    GAME_PANEL_LEVEL_NAME,
+    &game.panel.level_name,
+    TYPE_STRING,
+  },
+  {
+    GAME_PANEL_LEVEL_AUTHOR,
+    &game.panel.level_author,
+    TYPE_STRING,
+  },
 
   {
     -1,
     NULL,
     -1,
-    NULL
   }
 };
-#endif
-
 
 /* values for delayed check of falling and moving elements and for collision */
 #define CHECK_DELAY_MOVING     3
@@ -643,6 +899,9 @@ static struct GameControlInfo game_controls[] =
         (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
         (be) + (e) - EL_SELF)
 
+#define GET_PLAYER_FROM_BITS(p)                                                \
+       (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
+
 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                          \
        ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
         (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
@@ -753,14 +1012,18 @@ static struct GameControlInfo game_controls[] =
 #define SOUND_CTRL_ID_MUSIC            3
 #define SOUND_CTRL_ID_LOOPS            4
 #define SOUND_CTRL_ID_SIMPLE           5
+#define GAME_CTRL_ID_SAVE              6
+#define GAME_CTRL_ID_LOAD              7
 
-#define NUM_GAME_BUTTONS               6
+#define NUM_GAME_BUTTONS               8
 
 
 /* forward declaration for internal use */
 
 static void CreateField(int, int, int);
 
+static void ResetGfxAnimation(int, int);
+
 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
 static void AdvanceFrameAndPlayerCounters(int);
 
@@ -769,7 +1032,10 @@ static boolean MovePlayer(struct PlayerInfo *, int, int);
 static void ScrollPlayer(struct PlayerInfo *, int);
 static void ScrollScreen(struct PlayerInfo *, int);
 
-int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static boolean DigFieldByCE(int, int, int);
+static boolean SnapField(struct PlayerInfo *, int, int);
+static boolean DropElement(struct PlayerInfo *);
 
 static void InitBeltMovement(void);
 static void CloseAllOpenTimegates(void);
@@ -816,7 +1082,6 @@ static void PlayLevelSoundActionIfLoop(int, int, int);
 static void StopLevelSoundActionIfLoop(int, int, int);
 static void PlayLevelMusic();
 
-static void MapGameButtons();
 static void HandleGameButtons(struct GadgetInfo *);
 
 int AmoebeNachbarNr(int, int);
@@ -836,14 +1101,12 @@ void TestIfBadThingRunsIntoPlayer(int, int, int);
 void TestIfFriendTouchesBadThing(int, int);
 void TestIfBadThingTouchesFriend(int, int);
 void TestIfBadThingTouchesOtherBadThing(int, int);
+void TestIfGoodThingGetsHitByBadThing(int, int, int);
 
 void KillPlayer(struct PlayerInfo *);
 void BuryPlayer(struct PlayerInfo *);
 void RemovePlayer(struct PlayerInfo *);
 
-boolean SnapField(struct PlayerInfo *, int, int);
-boolean DropElement(struct PlayerInfo *);
-
 static int getInvisibleActiveFromInvisibleElement(int);
 static int getInvisibleFromInvisibleActiveElement(int);
 
@@ -876,6 +1139,8 @@ static int recursion_loop_depth;
 static boolean recursion_loop_detected;
 static boolean recursion_loop_element;
 
+static int map_player_action[MAX_PLAYERS];
+
 
 /* ------------------------------------------------------------------------- */
 /* definition of elements that automatically change to other elements after  */
@@ -1434,6 +1699,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
       }
       else
       {
+       stored_player[0].initial_element = element;
        stored_player[0].use_murphy = TRUE;
 
        if (!level.use_artwork_element[0])
@@ -1478,20 +1744,33 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
 
       StorePlayer[x][y] = Feld[x][y];
 
+#if DEBUG_INIT_PLAYER
       if (options.debug)
       {
-       printf("Player %d activated.\n", player->element_nr);
-       printf("[Local player is %d and currently %s.]\n",
+       printf("- player element %d activated", player->element_nr);
+       printf(" (local player is %d and currently %s)\n",
               local_player->element_nr,
               local_player->active ? "active" : "not active");
       }
     }
+#endif
 
     Feld[x][y] = EL_EMPTY;
 
     player->jx = player->last_jx = x;
     player->jy = player->last_jy = y;
   }
+
+#if USE_PLAYER_REANIMATION
+  if (!init_game)
+  {
+    int player_nr = GET_PLAYER_NR(element);
+    struct PlayerInfo *player = &stored_player[player_nr];
+
+    if (player->active && player->killed)
+      player->reanimated = TRUE; /* if player was just killed, reanimate him */
+  }
+#endif
 }
 
 static void InitField(int x, int y, boolean init_game)
@@ -1679,6 +1958,34 @@ static void InitField(int x, int y, boolean init_game)
        Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
       break;
 
+    case EL_TRIGGER_PLAYER:
+    case EL_TRIGGER_ELEMENT:
+    case EL_TRIGGER_CE_VALUE:
+    case EL_TRIGGER_CE_SCORE:
+    case EL_SELF:
+    case EL_ANY_ELEMENT:
+    case EL_CURRENT_CE_VALUE:
+    case EL_CURRENT_CE_SCORE:
+    case EL_PREV_CE_1:
+    case EL_PREV_CE_2:
+    case EL_PREV_CE_3:
+    case EL_PREV_CE_4:
+    case EL_PREV_CE_5:
+    case EL_PREV_CE_6:
+    case EL_PREV_CE_7:
+    case EL_PREV_CE_8:
+    case EL_NEXT_CE_1:
+    case EL_NEXT_CE_2:
+    case EL_NEXT_CE_3:
+    case EL_NEXT_CE_4:
+    case EL_NEXT_CE_5:
+    case EL_NEXT_CE_6:
+    case EL_NEXT_CE_7:
+    case EL_NEXT_CE_8:
+      /* reference elements should not be used on the playfield */
+      Feld[x][y] = EL_EMPTY;
+      break;
+
     default:
       if (IS_CUSTOM_ELEMENT(element))
       {
@@ -1735,299 +2042,899 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
   */
 }
 
-#if 1
-
-void DrawGameValue_Emeralds(int value)
+static int get_key_element_from_nr(int key_nr)
 {
-  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;
-
-  if (PANEL_DEACTIVATED(pos))
-    return;
+  int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
+                         level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                         EL_EM_KEY_1 : EL_KEY_1);
 
-  pos->width = chars * font_width;
+  return key_base_element + key_nr;
+}
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+static int get_next_dropped_element(struct PlayerInfo *player)
+{
+  return (player->inventory_size > 0 ?
+         player->inventory_element[player->inventory_size - 1] :
+         player->inventory_infinite_element != EL_UNDEFINED ?
+         player->inventory_infinite_element :
+         player->dynabombs_left > 0 ?
+         EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
+         EL_UNDEFINED);
 }
 
-void DrawGameValue_Dynamite(int value)
+static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
 {
-  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;
+  /* pos >= 0: get element from bottom of the stack;
+     pos <  0: get element from top of the stack */
 
-  if (PANEL_DEACTIVATED(pos))
-    return;
+  if (pos < 0)
+  {
+    int min_inventory_size = -pos;
+    int inventory_pos = player->inventory_size - min_inventory_size;
+    int min_dynabombs_left = min_inventory_size - player->inventory_size;
 
-  pos->width = chars * font_width;
+    return (player->inventory_size >= min_inventory_size ?
+           player->inventory_element[inventory_pos] :
+           player->inventory_infinite_element != EL_UNDEFINED ?
+           player->inventory_infinite_element :
+           player->dynabombs_left >= min_dynabombs_left ?
+           EL_DYNABOMB_PLAYER_1 + player->index_nr :
+           EL_UNDEFINED);
+  }
+  else
+  {
+    int min_dynabombs_left = pos + 1;
+    int min_inventory_size = pos + 1 - player->dynabombs_left;
+    int inventory_pos = pos - player->dynabombs_left;
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+    return (player->inventory_infinite_element != EL_UNDEFINED ?
+           player->inventory_infinite_element :
+           player->dynabombs_left >= min_dynabombs_left ?
+           EL_DYNABOMB_PLAYER_1 + player->index_nr :
+           player->inventory_size >= min_inventory_size ?
+           player->inventory_element[inventory_pos] :
+           EL_UNDEFINED);
+  }
 }
 
-void DrawGameValue_Score(int value)
+static int compareGamePanelOrderInfo(const void *object1, const void *object2)
 {
-  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;
+  const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
+  const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
+  int compare_result;
 
-  if (PANEL_DEACTIVATED(pos))
-    return;
-
-  pos->width = chars * font_width;
+  if (gpo1->sort_priority != gpo2->sort_priority)
+    compare_result = gpo1->sort_priority - gpo2->sort_priority;
+  else
+    compare_result = gpo1->nr - gpo2->nr;
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+  return compare_result;
 }
 
-void DrawGameValue_Time(int value)
+void InitGameControlValues()
 {
-  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 (PANEL_DEACTIVATED(pos))
-    return;
+  int i;
 
-  if (use_dynamic_chars)               /* use dynamic number of chars */
+  for (i = 0; game_panel_controls[i].nr != -1; i++)
   {
-    chars   = (value < 1000 ? chars1   : chars2);
-    font_nr = (value < 1000 ? font1_nr : font2_nr);
-  }
+    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+    struct GamePanelOrderInfo *gpo = &game_panel_order[i];
+    struct TextPosInfo *pos = gpc->pos;
+    int nr = gpc->nr;
+    int type = gpc->type;
 
-  /* 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));
+    if (nr != i)
+    {
+      Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
+      Error(ERR_EXIT, "this should not happen -- please debug");
+    }
 
-    pos->width = max_width;
+    /* force update of game controls after initialization */
+    gpc->value = gpc->last_value = -1;
+    gpc->frame = gpc->last_frame = -1;
+    gpc->gfx_frame = -1;
 
-    ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
-                              max_width, max_height);
+    /* determine panel value width for later calculation of alignment */
+    if (type == TYPE_INTEGER || type == TYPE_STRING)
+    {
+      pos->width = pos->size * getFontWidth(pos->font);
+      pos->height = getFontHeight(pos->font);
+    }
+    else if (type == TYPE_ELEMENT)
+    {
+      pos->width = pos->size;
+      pos->height = pos->size;
+    }
+
+    /* fill structure for game panel draw order */
+    gpo->nr = gpc->nr;
+    gpo->sort_priority = pos->sort_priority;
   }
 
-  pos->width = chars * getFontWidth(font_nr);
+  /* sort game panel controls according to sort_priority and control number */
+  qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
+       sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
+}
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+void UpdatePlayfieldElementCount()
+{
+  boolean use_element_count = FALSE;
+  int i, j, x, y;
 
-  last_value = value;
+  /* first check if it is needed at all to calculate playfield element count */
+  for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
+    if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
+      use_element_count = TRUE;
+
+  if (!use_element_count)
+    return;
+
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    element_info[i].element_count = 0;
+
+  SCAN_PLAYFIELD(x, y)
+  {
+    element_info[Feld[x][y]].element_count++;
+  }
+
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+    for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+      if (IS_IN_GROUP(j, i))
+       element_info[EL_GROUP_START + i].element_count +=
+         element_info[j].element_count;
 }
 
-void DrawGameValue_Level(int value)
+void UpdateGameControlValues()
 {
-  struct TextPosInfo *pos = &game.panel.level;
-  int chars1 = 2;
-  int chars2 = 3;
-  int chars = pos->chars;
-#if 1
-  int font1_nr = pos->font;
-  int font2_nr = pos->font_alt;
+  int i, k;
+  int time = (local_player->LevelSolved ?
+             local_player->LevelSolved_CountingTime :
+             level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+             level.native_em_level->lev->time :
+             level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+             level.native_sp_level->game_sp->time_played :
+             game.no_time_limit ? TimePlayed : TimeLeft);
+  int score = (local_player->LevelSolved ?
+              local_player->LevelSolved_CountingScore :
+              level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+              level.native_em_level->lev->score :
+              level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+              level.native_sp_level->game_sp->score :
+              local_player->score);
+  int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+             level.native_em_level->lev->required :
+             level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+             level.native_sp_level->game_sp->infotrons_still_needed :
+             local_player->gems_still_needed);
+  int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                    level.native_em_level->lev->required > 0 :
+                    level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+                    level.native_sp_level->game_sp->infotrons_still_needed > 0 :
+                    local_player->gems_still_needed > 0 ||
+                    local_player->sokobanfields_still_needed > 0 ||
+                    local_player->lights_still_needed > 0);
+
+  UpdatePlayfieldElementCount();
+
+  /* update game panel control values */
+
+  game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
+  game_panel_controls[GAME_PANEL_GEMS].value = gems;
+
+  game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
+  for (i = 0; i < MAX_NUM_KEYS; i++)
+    game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
+  game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
+  game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
+
+  if (game.centered_player_nr == -1)
+  {
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      /* only one player in Supaplex game engine */
+      if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
+       break;
+
+      for (k = 0; k < MAX_NUM_KEYS; k++)
+      {
+       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+       {
+         if (level.native_em_level->ply[i]->keys & (1 << k))
+           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+             get_key_element_from_nr(k);
+       }
+       else if (stored_player[i].key[k])
+         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+           get_key_element_from_nr(k);
+      }
+
+      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         level.native_em_level->ply[i]->dynamite;
+      else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         level.native_sp_level->game_sp->red_disk_count;
+      else
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         stored_player[i].inventory_size;
+
+      if (stored_player[i].num_white_keys > 0)
+       game_panel_controls[GAME_PANEL_KEY_WHITE].value =
+         EL_DC_KEY_WHITE;
+
+      game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
+       stored_player[i].num_white_keys;
+    }
+  }
+  else
+  {
+    int player_nr = game.centered_player_nr;
+
+    for (k = 0; k < MAX_NUM_KEYS; k++)
+    {
+      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      {
+       if (level.native_em_level->ply[player_nr]->keys & (1 << k))
+         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+           get_key_element_from_nr(k);
+      }
+      else if (stored_player[player_nr].key[k])
+       game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+         get_key_element_from_nr(k);
+    }
+
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       level.native_em_level->ply[player_nr]->dynamite;
+    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       level.native_sp_level->game_sp->red_disk_count;
+    else
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       stored_player[player_nr].inventory_size;
+
+    if (stored_player[player_nr].num_white_keys > 0)
+      game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
+
+    game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
+      stored_player[player_nr].num_white_keys;
+  }
+
+  for (i = 0; i < NUM_PANEL_INVENTORY; i++)
+  {
+    game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
+      get_inventory_element_from_pos(local_player, i);
+    game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
+      get_inventory_element_from_pos(local_player, -i - 1);
+  }
+
+  game_panel_controls[GAME_PANEL_SCORE].value = score;
+  game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
+
+  game_panel_controls[GAME_PANEL_TIME].value = time;
+
+  game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
+  game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
+  game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
+
+  game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
+
+  game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
+    (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
+     EL_EMPTY);
+  game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
+    local_player->shield_normal_time_left;
+  game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
+    (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
+     EL_EMPTY);
+  game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
+    local_player->shield_deadly_time_left;
+
+  game_panel_controls[GAME_PANEL_EXIT].value =
+    (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
+
+  game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
+    (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
+  game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
+    (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
+     EL_EMC_MAGIC_BALL_SWITCH);
+
+  game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
+    (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
+  game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
+    game.light_time_left;
+
+  game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
+    (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
+  game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
+    game.timegate_time_left;
+
+  game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
+    EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
+
+  game_panel_controls[GAME_PANEL_EMC_LENSES].value =
+    (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
+  game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
+    game.lenses_time_left;
+
+  game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
+    (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
+  game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
+    game.magnify_time_left;
+
+  game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
+    (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
+     game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
+     game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
+     game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
+     EL_BALLOON_SWITCH_NONE);
+
+  game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
+    local_player->dynabomb_count;
+  game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
+    local_player->dynabomb_size;
+  game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
+    (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
+
+  game_panel_controls[GAME_PANEL_PENGUINS].value =
+    local_player->friends_still_needed;
+
+  game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
+    local_player->sokobanfields_still_needed;
+  game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
+    local_player->sokobanfields_still_needed;
+
+  game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
+    (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
+
+  for (i = 0; i < NUM_BELTS; i++)
+  {
+    game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
+      (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
+       EL_CONVEYOR_BELT_1_MIDDLE) + i;
+    game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
+      getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
+  }
+
+  game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
+    (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
+  game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
+    game.magic_wall_time_left;
+
+#if USE_PLAYER_GRAVITY
+  game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
+    local_player->gravity;
 #else
-  int font1_nr = FONT_TEXT_2;
-  int font2_nr = FONT_TEXT_1;
+  game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
 #endif
-  int font_nr = font1_nr;
-  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
 
-  if (PANEL_DEACTIVATED(pos))
-    return;
+  for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
+    game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
 
-  if (use_dynamic_chars)               /* use dynamic number of chars */
+  for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+    game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
+      (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
+       game.panel.element[i].id : EL_UNDEFINED);
+
+  for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+    game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
+      (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
+       element_info[game.panel.element_count[i].id].element_count : 0);
+
+  for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+    game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
+      (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
+       element_info[game.panel.ce_score[i].id].collect_score : 0);
+
+  for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+    game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
+      (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
+       element_info[game.panel.ce_score_element[i].id].collect_score :
+       EL_UNDEFINED);
+
+  game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
+  game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
+  game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
+
+  /* update game panel control frames */
+
+  for (i = 0; game_panel_controls[i].nr != -1; i++)
   {
-    chars   = (level_nr < 100 ? chars1   : chars2);
-    font_nr = (level_nr < 100 ? font1_nr : font2_nr);
-  }
+    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
 
-  pos->width = chars * getFontWidth(font_nr);
+    if (gpc->type == TYPE_ELEMENT)
+    {
+      if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
+      {
+       int last_anim_random_frame = gfx.anim_random_frame;
+       int element = gpc->value;
+       int graphic = el2panelimg(element);
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+       if (gpc->value != gpc->last_value)
+       {
+         gpc->gfx_frame = 0;
+         gpc->gfx_random = INIT_GFX_RANDOM();
+       }
+       else
+       {
+         gpc->gfx_frame++;
+
+         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
+           gpc->gfx_random = INIT_GFX_RANDOM();
+       }
+
+       if (ANIM_MODE(graphic) == ANIM_RANDOM)
+         gfx.anim_random_frame = gpc->gfx_random;
+
+       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+         gpc->gfx_frame = element_info[element].collect_score;
+
+       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
+                                             gpc->gfx_frame);
+
+       if (ANIM_MODE(graphic) == ANIM_RANDOM)
+         gfx.anim_random_frame = last_anim_random_frame;
+      }
+    }
+  }
 }
 
-void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+void DisplayGameControlValues()
 {
-  struct TextPosInfo *pos = &game.panel.keys;
-  int base_key_graphic = EL_KEY_1;
+  boolean redraw_panel = FALSE;
   int i;
 
-  if (PANEL_DEACTIVATED(pos))
+  for (i = 0; game_panel_controls[i].nr != -1; i++)
+  {
+    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+
+    if (PANEL_DEACTIVATED(gpc->pos))
+      continue;
+
+    if (gpc->value == gpc->last_value &&
+       gpc->frame == gpc->last_frame)
+      continue;
+
+    redraw_panel = TRUE;
+  }
+
+  if (!redraw_panel)
     return;
 
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    base_key_graphic = EL_EM_KEY_1;
+  /* copy default game door content to main double buffer */
+#if 1
+  /* !!! CHECK AGAIN !!! */
+  SetPanelBackground();
+  // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+  DrawBackground(DX, DY, DXSIZE, DYSIZE);
+#else
+  BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+            DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+#endif
+
+  /* redraw game control buttons */
+#if 1
+  RedrawGameButtons();
+#else
+  UnmapGameButtons();
+  MapGameButtons();
+#endif
 
-  pos->width = 4 * MINI_TILEX;
+  game_status = GAME_MODE_PSEUDO_PANEL;
 
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
+#if 1
+  for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
+#else
+  for (i = 0; game_panel_controls[i].nr != -1; i++)
+#endif
   {
-    int src_x = DOOR_GFX_PAGEX5 + 18;
-    int src_y = DOOR_GFX_PAGEY1 + 123;
-    int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
-    int dst_y = PANEL_YPOS(pos);
+#if 1
+    int nr = game_panel_order[i].nr;
+    struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
+#else
+    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+    int nr = gpc->nr;
+#endif
+    struct TextPosInfo *pos = gpc->pos;
+    int type = gpc->type;
+    int value = gpc->value;
+    int frame = gpc->frame;
+#if 0
+    int last_value = gpc->last_value;
+    int last_frame = gpc->last_frame;
+#endif
+    int size = pos->size;
+    int font = pos->font;
+    boolean draw_masked = pos->draw_masked;
+    int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
+
+    if (PANEL_DEACTIVATED(pos))
+      continue;
 
 #if 0
-    /* masked blit with tiles from half-size scaled bitmap does not work yet
-       (no mask bitmap created for these sizes after loading and scaling) --
-       solution: load without creating mask, scale, then create final mask */
+    if (value == last_value && frame == last_frame)
+      continue;
+#endif
 
-    BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
-              MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+    gpc->last_value = value;
+    gpc->last_frame = frame;
 
-    if (key[i])
+#if 0
+    printf("::: value %d changed from %d to %d\n", nr, last_value, value);
+#endif
+
+    if (type == TYPE_INTEGER)
     {
-      int graphic = el2edimg(base_key_graphic + i);
+      if (nr == GAME_PANEL_LEVEL_NUMBER ||
+         nr == GAME_PANEL_TIME)
+      {
+       boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
+
+       if (use_dynamic_size)           /* use dynamic number of digits */
+       {
+         int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
+         int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
+         int size2 = size1 + 1;
+         int font1 = pos->font;
+         int font2 = pos->font_alt;
+
+         size = (value < value_change ? size1 : size2);
+         font = (value < value_change ? font1 : font2);
+
+#if 0
+         /* clear background if value just changed its size (dynamic digits) */
+         if ((last_value < value_change) != (value < value_change))
+         {
+           int width1 = size1 * getFontWidth(font1);
+           int width2 = size2 * getFontWidth(font2);
+           int max_width = MAX(width1, width2);
+           int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
+
+           pos->width = max_width;
+
+           ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                                      max_width, max_height);
+         }
+#endif
+       }
+      }
+
+#if 1
+      /* correct text size if "digits" is zero or less */
+      if (size <= 0)
+       size = strlen(int2str(value, size));
+
+      /* dynamically correct text alignment */
+      pos->width = size * getFontWidth(font);
+#endif
+
+      DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                 int2str(value, size), font, mask_mode);
+    }
+    else if (type == TYPE_ELEMENT)
+    {
+      int element, graphic;
       Bitmap *src_bitmap;
       int src_x, src_y;
+      int width, height;
+      int dst_x = PANEL_XPOS(pos);
+      int dst_y = PANEL_YPOS(pos);
 
-      getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
+#if 1
+      if (value != EL_UNDEFINED && value != EL_EMPTY)
+      {
+       element = value;
+       graphic = el2panelimg(value);
 
-      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);
-    }
+       // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
+
+#if 1
+       if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
+         size = TILESIZE;
+#endif
+
+       getSizedGraphicSource(graphic, frame, size, &src_bitmap,
+                             &src_x, &src_y);
+
+       width  = graphic_info[graphic].width  * size / TILESIZE;
+       height = graphic_info[graphic].height * size / TILESIZE;
+
+       if (draw_masked)
+       {
+         SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+                       dst_x - src_x, dst_y - src_y);
+         BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
+                          dst_x, dst_y);
+       }
+       else
+       {
+         BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
+                    dst_x, dst_y);
+       }
+      }
 #else
-    if (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);
+      if (value == EL_UNDEFINED || value == EL_EMPTY)
+      {
+       element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
+       graphic = el2panelimg(element);
+
+       src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+       src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
+       src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
+      }
+      else
+      {
+       element = value;
+       graphic = el2panelimg(value);
+
+       getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
+      }
+
+      width  = graphic_info[graphic].width  * size / TILESIZE;
+      height = graphic_info[graphic].height * size / TILESIZE;
+
+      BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
+#endif
+    }
+    else if (type == TYPE_STRING)
+    {
+      boolean active = (value != 0);
+      char *state_normal = "off";
+      char *state_active = "on";
+      char *state = (active ? state_active : state_normal);
+      char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
+                nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
+                nr == GAME_PANEL_LEVEL_NAME    ? level.name :
+                nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
+
+      if (nr == GAME_PANEL_GRAVITY_STATE)
+      {
+       int font1 = pos->font;          /* (used for normal state) */
+       int font2 = pos->font_alt;      /* (used for active state) */
+#if 0
+       int size1 = strlen(state_normal);
+       int size2 = strlen(state_active);
+       int width1 = size1 * getFontWidth(font1);
+       int width2 = size2 * getFontWidth(font2);
+       int max_width = MAX(width1, width2);
+       int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
+
+       pos->width = max_width;
+
+       /* clear background for values that may have changed its size */
+       ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                                  max_width, max_height);
 #endif
+
+       font = (active ? font2 : font1);
+      }
+
+      if (s != NULL)
+      {
+       char *s_cut;
+
+#if 1
+       if (size <= 0)
+       {
+         /* don't truncate output if "chars" is zero or less */
+         size = strlen(s);
+
+         /* dynamically correct text alignment */
+         pos->width = size * getFontWidth(font);
+       }
+#endif
+
+       s_cut = getStringCopyN(s, size);
+
+       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                   s_cut, font, mask_mode);
+
+       free(s_cut);
+      }
+    }
+
+    redraw_mask |= REDRAW_DOOR_1;
   }
+
+  game_status = GAME_MODE_PLAYING;
 }
 
-#else
+void UpdateAndDisplayGameControlValues()
+{
+  if (tape.warp_forward)
+    return;
+
+  UpdateGameControlValues();
+  DisplayGameControlValues();
+}
 
 void DrawGameValue_Emeralds(int value)
 {
-  int font_nr = FONT_TEXT_2;
-  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
+  struct TextPosInfo *pos = &game.panel.gems;
+  int font_nr = pos->font;
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->size;
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-  if (PANEL_DEACTIVATED(game.panel.gems))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
 void DrawGameValue_Dynamite(int value)
 {
-  int font_nr = FONT_TEXT_2;
-  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
+  struct TextPosInfo *pos = &game.panel.inventory_count;
+  int font_nr = pos->font;
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->size;
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-  if (PANEL_DEACTIVATED(game.panel.inventory))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
 void DrawGameValue_Score(int value)
 {
-  int font_nr = FONT_TEXT_2;
-  int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
+  struct TextPosInfo *pos = &game.panel.score;
+  int font_nr = pos->font;
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->size;
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-  if (PANEL_DEACTIVATED(game.panel.score))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
 void DrawGameValue_Time(int value)
 {
-  int font1_nr = FONT_TEXT_2;
+  struct TextPosInfo *pos = &game.panel.time;
+  static int last_value = -1;
+  int chars1 = 3;
+  int chars2 = 4;
+  int chars = pos->size;
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+  int font_nr = font1_nr;
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
+
 #if 1
-  int font2_nr = FONT_TEXT_1;
-#else
-  int font2_nr = FONT_LEVEL_NUMBER;
+  return;      /* !!! USE NEW STUFF !!! */
 #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))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  /* clear background if value just changed its size */
-  if (value == 999 || value == 1000)
-    ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
+  if (use_dynamic_chars)               /* use dynamic number of chars */
+  {
+    chars   = (value < 1000 ? chars1   : chars2);
+    font_nr = (value < 1000 ? font1_nr : font2_nr);
+  }
 
-  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);
+  /* 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)
 {
-  int font1_nr = FONT_TEXT_2;
+  struct TextPosInfo *pos = &game.panel.level_number;
+  int chars1 = 2;
+  int chars2 = 3;
+  int chars = pos->size;
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+  int font_nr = font1_nr;
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
+
 #if 1
-  int font2_nr = FONT_TEXT_1;
-#else
-  int font2_nr = FONT_LEVEL_NUMBER;
+  return;      /* !!! USE NEW STUFF !!! */
 #endif
 
-  if (PANEL_DEACTIVATED(game.panel.level))
+  if (PANEL_DEACTIVATED(pos))
     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);
+  if (use_dynamic_chars)               /* use dynamic number of chars */
+  {
+    chars   = (level_nr < 100 ? chars1   : chars2);
+    font_nr = (level_nr < 100 ? font1_nr : font2_nr);
+  }
+
+  pos->width = chars * getFontWidth(font_nr);
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
 {
-  int 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;
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
+  for (i = 0; i < MAX_NUM_KEYS; i++)
   {
-    int x = XX_KEYS + i * MINI_TILEX;
-    int y = YY_KEYS;
+    struct TextPosInfo *pos = &game.panel.key[i];
+    int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
+    int src_y = DOOR_GFX_PAGEY1 + 123;
+    int dst_x = PANEL_XPOS(pos);
+    int dst_y = PANEL_YPOS(pos);
+
+    int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
+                  level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
+                  EL_KEY_1) + i;
+    int graphic = el2edimg(element);
+
+    if (PANEL_DEACTIVATED(pos))
+      continue;
+
+#if 0
+    /* masked blit with tiles from half-size scaled bitmap does not work yet
+       (no mask bitmap created for these sizes after loading and scaling) --
+       solution: load without creating mask, scale, then create final mask */
 
+    BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+              MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+
+    if (key[i])
+    {
+      Bitmap *src_bitmap;
+      int src_x, src_y;
+
+      getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
+
+      SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+                   dst_x - src_x, dst_y - src_y);
+      BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
+                      dst_x, dst_y);
+    }
+#else
     if (key[i])
-      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
+      DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
     else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+#endif
   }
 }
 
-#endif
-
 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
                       int key_bits)
 {
@@ -2052,9 +2959,19 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
   DrawGameValue_Keys(key);
 }
 
+void UpdateGameDoorValues()
+{
+  UpdateGameControlValues();
+}
+
 void DrawGameDoorValues()
 {
-  int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
+  DisplayGameControlValues();
+}
+
+void DrawGameDoorValues_OLD()
+{
+  int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
   int dynamite_value = 0;
   int score_value = (local_player->LevelSolved ? local_player->score_final :
                     local_player->score);
@@ -2112,6 +3029,21 @@ static void InitGameEngine()
   game.engine_version = (tape.playing ? tape.engine_version :
                         level.game_version);
 
+  /* set single or multi-player game mode (needed for re-playing tapes) */
+  game.team_mode = setup.team_mode;
+
+  if (tape.playing)
+  {
+    int num_players = 0;
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (tape.player_participates[i])
+       num_players++;
+
+    /* multi-player tapes contain input data for more than one player */
+    game.team_mode = (num_players > 1);
+  }
+
   /* ---------------------------------------------------------------------- */
   /* set flags for bugs and changes according to active game engine version */
   /* ---------------------------------------------------------------------- */
@@ -2294,7 +3226,7 @@ static void InitGameEngine()
     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
   }
 
-  /* ---------- initialize internal run-time variables ------------- */
+  /* ---------- initialize internal run-time variables --------------------- */
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
@@ -2332,6 +3264,27 @@ static void InitGameEngine()
     }
   }
 
+#if 1
+  /* ---------- initialize reference elements in change conditions --------- */
+
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+    struct ElementInfo *ei = &element_info[element];
+
+    for (j = 0; j < ei->num_change_pages; j++)
+    {
+      int trigger_element = ei->change_page[j].initial_trigger_element;
+
+      if (trigger_element >= EL_PREV_CE_8 &&
+         trigger_element <= EL_NEXT_CE_8)
+       trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
+
+      ei->change_page[j].trigger_element = trigger_element;
+    }
+  }
+#endif
+
   /* ---------- initialize run-time trigger player and element ------------- */
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -2341,7 +3294,8 @@ static void InitGameEngine()
     for (j = 0; j < ei->num_change_pages; j++)
     {
       ei->change_page[j].actual_trigger_element = EL_EMPTY;
-      ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
+      ei->change_page[j].actual_trigger_player = EL_EMPTY;
+      ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
       ei->change_page[j].actual_trigger_ce_value = 0;
       ei->change_page[j].actual_trigger_ce_score = 0;
@@ -2521,6 +3475,13 @@ static void InitGameEngine()
   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)
@@ -2557,15 +3518,74 @@ int get_num_special_action(int element, int action_first, int action_last)
 
 void InitGame()
 {
+  int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
+  int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
+
   boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
+#if 0
   boolean do_fading = (game_status == GAME_MODE_MAIN);
+#endif
+#if 1
+  int initial_move_dir = MV_DOWN;
+#else
+  int initial_move_dir = MV_NONE;
+#endif
   int i, j, x, y;
 
+#if 1
+  game_status = GAME_MODE_PLAYING;
+#endif
+
+#if 1
+
+  StopAnimation();
+
+  if (!game.restart_level)
+    CloseDoor(DOOR_CLOSE_1);
+
+#if 1
+  if (level_editor_test_game)
+    FadeSkipNextFadeIn();
+  else
+    FadeSetEnterScreen();
+#else
+  if (level_editor_test_game)
+    fading = fading_none;
+  else
+    fading = menu.destination;
+#endif
+
+#if 1
+  FadeOut(REDRAW_FIELD);
+#else
+  if (do_fading)
+    FadeOut(REDRAW_FIELD);
+#endif
+
+#endif
+
+#if 0
+  printf("::: FADING OUT: DONE\n");
+  Delay(1000);
+#endif
+
+#if 0
   game_status = GAME_MODE_PLAYING;
+#endif
+
+#if 1
+  /* needed if different viewport properties defined for playing */
+  ChangeViewportPropertiesIfNeeded();
+#endif
+
+#if 1
+  DrawCompleteVideoDisplay();
+#endif
 
   InitGameEngine();
+  InitGameControlValues();
 
   /* don't play tapes over network */
   network_playing = (options.network && !tape.playing);
@@ -2580,7 +3600,10 @@ void InitGame()
 
     player->present = FALSE;
     player->active = FALSE;
+    player->mapped = FALSE;
+
     player->killed = FALSE;
+    player->reanimated = FALSE;
 
     player->action = 0;
     player->effective_action = 0;
@@ -2604,18 +3627,19 @@ void InitGame()
     player->dynabombs_left = 0;
     player->dynabomb_xl = FALSE;
 
-    player->MovDir = MV_NONE;
+    player->MovDir = initial_move_dir;
     player->MovPos = 0;
     player->GfxPos = 0;
-    player->GfxDir = MV_NONE;
+    player->GfxDir = initial_move_dir;
     player->GfxAction = ACTION_DEFAULT;
     player->Frame = 0;
     player->StepFrame = 0;
 
-    player->use_murphy = FALSE;
+    player->initial_element = player->element_nr;
     player->artwork_element =
       (level.use_artwork_element[i] ? level.artwork_element[i] :
        player->element_nr);
+    player->use_murphy = FALSE;
 
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
     player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
@@ -2628,7 +3652,7 @@ void InitGame()
 
     player->step_counter = 0;
 
-    player->last_move_dir = MV_NONE;
+    player->last_move_dir = initial_move_dir;
 
     player->is_active = FALSE;
 
@@ -2652,7 +3676,7 @@ void InitGame()
     player->anim_delay_counter = 0;
     player->post_delay_counter = 0;
 
-    player->dir_waiting = MV_NONE;
+    player->dir_waiting = initial_move_dir;
     player->action_waiting = ACTION_DEFAULT;
     player->last_action_waiting = ACTION_DEFAULT;
     player->special_action_bored = ACTION_DEFAULT;
@@ -2685,6 +3709,26 @@ void InitGame()
     player->inventory_infinite_element = EL_UNDEFINED;
     player->inventory_size = 0;
 
+    if (level.use_initial_inventory[i])
+    {
+      for (j = 0; j < level.initial_inventory_size[i]; j++)
+      {
+       int element = level.initial_inventory_content[i][j];
+       int collect_count = element_info[element].collect_count_initial;
+       int k;
+
+       if (!IS_CUSTOM_ELEMENT(element))
+         collect_count = 1;
+
+       if (collect_count == 0)
+         player->inventory_infinite_element = element;
+       else
+         for (k = 0; k < collect_count; k++)
+           if (player->inventory_size < MAX_INVENTORY_SIZE)
+             player->inventory_element[player->inventory_size++] = element;
+      }
+    }
+
     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
 
@@ -2696,6 +3740,10 @@ void InitGame()
     player->LevelSolved_PanelOff = FALSE;
     player->LevelSolved_SaveTape = FALSE;
     player->LevelSolved_SaveScore = FALSE;
+    player->LevelSolved_CountingTime = 0;
+    player->LevelSolved_CountingScore = 0;
+
+    map_player_action[i] = i;
   }
 
   network_player_action_received = FALSE;
@@ -2723,7 +3771,10 @@ void InitGame()
 
   AllPlayersGone = FALSE;
 
+  game.no_time_limit = (level.time == 0);
+
   game.yamyam_content_nr = 0;
+  game.robot_wheel_active = FALSE;
   game.magic_wall_active = FALSE;
   game.magic_wall_time_left = 0;
   game.light_time_left = 0;
@@ -2765,6 +3816,13 @@ void InitGame()
   for (i = 0; i < MAX_NUM_AMOEBA; i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
 
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+  {
+    printf("Player status at level initialization:\n");
+  }
+#endif
+
   SCAN_PLAYFIELD(x, y)
   {
     Feld[x][y] = level.field[x][y];
@@ -2798,6 +3856,7 @@ void InitGame()
     GfxElement[x][y] = EL_UNDEFINED;
     GfxAction[x][y] = ACTION_DEFAULT;
     GfxDir[x][y] = MV_NONE;
+    GfxRedraw[x][y] = GFX_REDRAW_NONE;
   }
 
   SCAN_PLAYFIELD(x, y)
@@ -2810,6 +3869,8 @@ void InitGame()
       emulate_sp = FALSE;
 
     InitField(x, y, TRUE);
+
+    ResetGfxAnimation(x, y);
   }
 
   InitBeltMovement();
@@ -2827,62 +3888,250 @@ void InitGame()
                             ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
   }
 
-  game.emulation = (emulate_bd ? EMU_BOULDERDASH :
-                   emulate_sb ? EMU_SOKOBAN :
-                   emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+  game.emulation = (emulate_bd ? EMU_BOULDERDASH :
+                   emulate_sb ? EMU_SOKOBAN :
+                   emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+
+#if USE_NEW_ALL_SLIPPERY
+  /* initialize type of slippery elements */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    if (!IS_CUSTOM_ELEMENT(i))
+    {
+      /* default: elements slip down either to the left or right randomly */
+      element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
+
+      /* SP style elements prefer to slip down on the left side */
+      if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
+       element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+
+      /* BD style elements prefer to slip down on the left side */
+      if (game.emulation == EMU_BOULDERDASH)
+       element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+    }
+  }
+#endif
+
+  /* initialize explosion and ignition delay */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    if (!IS_CUSTOM_ELEMENT(i))
+    {
+      int num_phase = 8;
+      int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
+                   game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
+                  game.emulation == EMU_SUPAPLEX ? 3 : 2);
+      int last_phase = (num_phase + 1) * delay;
+      int half_phase = (num_phase / 2) * delay;
+
+      element_info[i].explosion_delay = last_phase - 1;
+      element_info[i].ignition_delay = half_phase;
+
+      if (i == EL_BLACK_ORB)
+       element_info[i].ignition_delay = 1;
+    }
+
+#if 0
+    if (element_info[i].explosion_delay < 1)   /* !!! check again !!! */
+      element_info[i].explosion_delay = 1;
+
+    if (element_info[i].ignition_delay < 1)    /* !!! check again !!! */
+      element_info[i].ignition_delay = 1;
+#endif
+  }
+
+  /* correct non-moving belts to start moving left */
+  for (i = 0; i < NUM_BELTS; i++)
+    if (game.belt_dir[i] == MV_NONE)
+      game.belt_dir_nr[i] = 3;         /* not moving, next moving left */
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+  /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
+  /* choose default local player */
+  local_player = &stored_player[0];
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    stored_player[i].connected = FALSE;
+
+  local_player->connected = TRUE;
+  /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
+
+#if 0
+  printf("::: TEAM MODE: %d\n", game.team_mode);
+#endif
+
+  if (tape.playing)
+  {
+#if 1
+    for (i = 0; i < MAX_PLAYERS; i++)
+      stored_player[i].connected = tape.player_participates[i];
+#else
+    /* try to guess locally connected team mode players (needed for correct
+       assignment of player figures from level to locally playing players) */
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (tape.player_participates[i])
+       stored_player[i].connected = TRUE;
+#endif
+  }
+  else if (game.team_mode && !options.network)
+  {
+    /* try to guess locally connected team mode players (needed for correct
+       assignment of player figures from level to locally playing players) */
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (setup.input[i].use_joystick ||
+         setup.input[i].key.left != KSYM_UNDEFINED)
+       stored_player[i].connected = TRUE;
+  }
+
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+  {
+    printf("Player status after level initialization:\n");
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
+
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
+            player->present,
+            player->connected,
+            player->active);
+
+      if (local_player == player)
+       printf(" (local player)");
+
+      printf("\n");
+    }
+  }
+#endif
+
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+    printf("Reassigning players ...\n");
+#endif
+
+  /* check if any connected player was not found in playfield */
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+    if (player->connected && !player->present)
+    {
+      struct PlayerInfo *field_player = NULL;
+
+#if DEBUG_INIT_PLAYER
+      if (options.debug)
+       printf("- looking for field player for player %d ...\n", i + 1);
+#endif
+
+      /* assign first free player found that is present in the playfield */
+
+#if 1
+      /* first try: look for unmapped playfield player that is not connected */
+      for (j = 0; j < MAX_PLAYERS; j++)
+       if (field_player == NULL &&
+           stored_player[j].present &&
+           !stored_player[j].mapped &&
+           !stored_player[j].connected)
+         field_player = &stored_player[j];
+
+      /* second try: look for *any* unmapped playfield player */
+      for (j = 0; j < MAX_PLAYERS; j++)
+       if (field_player == NULL &&
+           stored_player[j].present &&
+           !stored_player[j].mapped)
+         field_player = &stored_player[j];
+#else
+      /* first try: look for unmapped playfield player that is not connected */
+      if (field_player == NULL)
+       for (j = 0; j < MAX_PLAYERS; j++)
+         if (stored_player[j].present &&
+             !stored_player[j].mapped &&
+             !stored_player[j].connected)
+           field_player = &stored_player[j];
+
+      /* second try: look for *any* unmapped playfield player */
+      if (field_player == NULL)
+       for (j = 0; j < MAX_PLAYERS; j++)
+         if (stored_player[j].present &&
+             !stored_player[j].mapped)
+           field_player = &stored_player[j];
+#endif
+
+      if (field_player != NULL)
+      {
+       int jx = field_player->jx, jy = field_player->jy;
+
+#if DEBUG_INIT_PLAYER
+       if (options.debug)
+         printf("- found player %d\n", field_player->index_nr + 1);
+#endif
+
+       player->present = FALSE;
+       player->active = FALSE;
+
+       field_player->present = TRUE;
+       field_player->active = TRUE;
+
+       /*
+       player->initial_element = field_player->initial_element;
+       player->artwork_element = field_player->artwork_element;
+
+       player->block_last_field       = field_player->block_last_field;
+       player->block_delay_adjustment = field_player->block_delay_adjustment;
+       */
+
+       StorePlayer[jx][jy] = field_player->element_nr;
 
-#if USE_NEW_ALL_SLIPPERY
-  /* initialize type of slippery elements */
-  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
-  {
-    if (!IS_CUSTOM_ELEMENT(i))
-    {
-      /* default: elements slip down either to the left or right randomly */
-      element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
+       field_player->jx = field_player->last_jx = jx;
+       field_player->jy = field_player->last_jy = jy;
 
-      /* SP style elements prefer to slip down on the left side */
-      if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
-       element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+       if (local_player == player)
+         local_player = field_player;
 
-      /* BD style elements prefer to slip down on the left side */
-      if (game.emulation == EMU_BOULDERDASH)
-       element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+       map_player_action[field_player->index_nr] = i;
+
+       field_player->mapped = TRUE;
+
+#if DEBUG_INIT_PLAYER
+       if (options.debug)
+         printf("- map_player_action[%d] == %d\n",
+                field_player->index_nr + 1, i + 1);
+#endif
+      }
     }
+
+    if (player->connected && player->present)
+      player->mapped = TRUE;
   }
-#endif
 
-  /* initialize explosion and ignition delay */
-  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
   {
-    if (!IS_CUSTOM_ELEMENT(i))
-    {
-      int num_phase = 8;
-      int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
-                   game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
-                  game.emulation == EMU_SUPAPLEX ? 3 : 2);
-      int last_phase = (num_phase + 1) * delay;
-      int half_phase = (num_phase / 2) * delay;
+    printf("Player status after player assignment (first stage):\n");
 
-      element_info[i].explosion_delay = last_phase - 1;
-      element_info[i].ignition_delay = half_phase;
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
 
-      if (i == EL_BLACK_ORB)
-       element_info[i].ignition_delay = 1;
-    }
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
+            player->present,
+            player->connected,
+            player->active);
 
-#if 0
-    if (element_info[i].explosion_delay < 1)   /* !!! check again !!! */
-      element_info[i].explosion_delay = 1;
+      if (local_player == player)
+       printf(" (local player)");
 
-    if (element_info[i].ignition_delay < 1)    /* !!! check again !!! */
-      element_info[i].ignition_delay = 1;
-#endif
+      printf("\n");
+    }
   }
+#endif
 
-  /* correct non-moving belts to start moving left */
-  for (i = 0; i < NUM_BELTS; i++)
-    if (game.belt_dir[i] == MV_NONE)
-      game.belt_dir_nr[i] = 3;         /* not moving, next moving left */
+#else
 
   /* check if any connected player was not found in playfield */
   for (i = 0; i < MAX_PLAYERS; i++)
@@ -2893,24 +4142,26 @@ void InitGame()
     {
       for (j = 0; j < MAX_PLAYERS; j++)
       {
-       struct PlayerInfo *some_player = &stored_player[j];
-       int jx = some_player->jx, jy = some_player->jy;
+       struct PlayerInfo *field_player = &stored_player[j];
+       int jx = field_player->jx, jy = field_player->jy;
 
        /* assign first free player found that is present in the playfield */
-       if (some_player->present && !some_player->connected)
+       if (field_player->present && !field_player->connected)
        {
          player->present = TRUE;
          player->active = TRUE;
 
-         some_player->present = FALSE;
-         some_player->active = FALSE;
+         field_player->present = FALSE;
+         field_player->active = FALSE;
 
-         player->artwork_element = some_player->artwork_element;
+         player->initial_element = field_player->initial_element;
+         player->artwork_element = field_player->artwork_element;
 
-         player->block_last_field       = some_player->block_last_field;
-         player->block_delay_adjustment = some_player->block_delay_adjustment;
+         player->block_last_field       = field_player->block_last_field;
+         player->block_delay_adjustment = field_player->block_delay_adjustment;
 
          StorePlayer[jx][jy] = player->element_nr;
+
          player->jx = player->last_jx = jx;
          player->jy = player->last_jy = jy;
 
@@ -2919,14 +4170,47 @@ void InitGame()
       }
     }
   }
+#endif
+
+#if 0
+  printf("::: local_player->present == %d\n", local_player->present);
+#endif
 
   if (tape.playing)
   {
     /* when playing a tape, eliminate all players who do not participate */
 
+#if USE_NEW_PLAYER_ASSIGNMENTS
+
+#if 1
+    if (!game.team_mode)
+#endif
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      if (stored_player[i].active &&
+         !tape.player_participates[map_player_action[i]])
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int jx = player->jx, jy = player->jy;
+
+#if DEBUG_INIT_PLAYER
+       if (options.debug)
+         printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
+#endif
+
+       player->active = FALSE;
+       StorePlayer[jx][jy] = 0;
+       Feld[jx][jy] = EL_EMPTY;
+      }
+    }
+
+#else
+
     for (i = 0; i < MAX_PLAYERS; i++)
     {
-      if (stored_player[i].active && !tape.player_participates[i])
+      if (stored_player[i].active &&
+         !tape.player_participates[i])
       {
        struct PlayerInfo *player = &stored_player[i];
        int jx = player->jx, jy = player->jy;
@@ -2936,8 +4220,9 @@ void InitGame()
        Feld[jx][jy] = EL_EMPTY;
       }
     }
+#endif
   }
-  else if (!options.network && !setup.team_mode)       /* && !tape.playing */
+  else if (!options.network && !game.team_mode)                /* && !tape.playing */
   {
     /* when in single player mode, eliminate all but the first active player */
 
@@ -2966,26 +4251,39 @@ void InitGame()
   /* when recording the game, store which players take part in the game */
   if (tape.recording)
   {
+#if USE_NEW_PLAYER_ASSIGNMENTS
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (stored_player[i].connected)
+       tape.player_participates[i] = TRUE;
+#else
     for (i = 0; i < MAX_PLAYERS; i++)
       if (stored_player[i].active)
        tape.player_participates[i] = TRUE;
+#endif
   }
 
+#if DEBUG_INIT_PLAYER
   if (options.debug)
   {
+    printf("Player status after player assignment (final stage):\n");
+
     for (i = 0; i < MAX_PLAYERS; i++)
     {
       struct PlayerInfo *player = &stored_player[i];
 
-      printf("Player %d: present == %d, connected == %d, active == %d.\n",
-            i+1,
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
             player->present,
             player->connected,
             player->active);
+
       if (local_player == player)
-       printf("Player  %d is local player.\n", i+1);
+       printf(" (local player)");
+
+      printf("\n");
     }
   }
+#endif
 
   if (BorderElement == EL_EMPTY)
   {
@@ -3002,11 +4300,55 @@ void InitGame()
     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
   }
 
+#if NEW_TILESIZE
+
+  // printf("::: START-0: %d, %d\n", lev_fieldx, SCR_FIELDX);
+  // printf("::: START-1: %d, %d\n", SBX_Left, SBX_Right);
+
+#if 1
+  if (full_lev_fieldx <= SCR_FIELDX)
+    SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+
+  if (full_lev_fieldy <= SCR_FIELDY)
+    SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+#else
+  if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
+    SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+
+  if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
+    SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+#endif
+
+  /*
+  printf("::: START-2: %d, %d (%d)\n", SBX_Left, SBX_Right,
+        SBX_Right - SBX_Left + 1);
+  */
+
+#if 1
+  if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
+    SBX_Left--;
+  if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
+    SBY_Upper--;
+#else
+  if (EVEN(SCR_FIELDX))
+    SBX_Left--;
+  if (EVEN(SCR_FIELDY))
+    SBY_Upper--;
+#endif
+
+#if 0
+  printf("::: START-3: %d, %d\n", SBX_Left, SBX_Right);
+  printf("\n");
+#endif
+
+#else
+
   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
 
   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+#endif
 
   /* if local player not found, look for custom element that might create
      the player (make some assumptions about the right custom element) */
@@ -3046,7 +4388,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;
@@ -3063,7 +4406,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;
@@ -3083,7 +4427,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;
@@ -3114,55 +4459,138 @@ void InitGame()
                local_player->jy - MIDPOSY);
   }
 
+#if 0
+  printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
+#endif
+
+#if 0
+  /* do not use PLAYING mask for fading out from main screen */
+  game_status = GAME_MODE_MAIN;
+#endif
+
+#if 0
+
   StopAnimation();
 
   if (!game.restart_level)
     CloseDoor(DOOR_CLOSE_1);
 
+#if 1
+  if (level_editor_test_game)
+    FadeSkipNextFadeIn();
+  else
+    FadeSetEnterScreen();
+#else
+  if (level_editor_test_game)
+    fading = fading_none;
+  else
+    fading = menu.destination;
+#endif
+
+#if 1
+  FadeOut(REDRAW_FIELD);
+#else
   if (do_fading)
     FadeOut(REDRAW_FIELD);
+#endif
+
+#endif
+
+#if 0
+  game_status = GAME_MODE_PLAYING;
+#endif
 
   /* !!! FIX THIS (START) !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     InitGameEngine_EM();
 
+#if 0
     /* blit playfield from scroll buffer to normal back buffer for fading in */
     BlitScreenToBitmap_EM(backbuffer);
+#endif
+  }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    InitGameEngine_SP();
+
+#if 0
+    /* blit playfield from scroll buffer to normal back buffer for fading in */
+    BlitScreenToBitmap_SP(backbuffer);
+#endif
   }
   else
   {
-    DrawLevel();
+    DrawLevel(REDRAW_FIELD);
     DrawAllPlayers();
 
     /* after drawing the level, correct some elements */
     if (game.timegate_time_left == 0)
       CloseAllOpenTimegates();
 
+#if 0
     /* blit playfield from scroll buffer to normal back buffer for fading in */
+#if NEW_TILESIZE
+    BlitScreenToBitmap(backbuffer);
+#else
     if (setup.soft_scrolling)
       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+#endif
+#endif
 
+#if 0
     redraw_mask |= REDRAW_FROM_BACKBUFFER;
+#endif
   }
+#if 1
+  /* blit playfield from scroll buffer to normal back buffer for fading in */
+  BlitScreenToBitmap(backbuffer);
+
+  redraw_mask |= REDRAW_FROM_BACKBUFFER;
+#endif
   /* !!! FIX THIS (END) !!! */
 
+#if 1
+  FadeIn(REDRAW_FIELD);
+#else
   if (do_fading)
     FadeIn(REDRAW_FIELD);
 
   BackToFront();
+#endif
 
   if (!game.restart_level)
   {
     /* copy default game door content to main double buffer */
+#if 1
+#if 1
+    /* !!! CHECK AGAIN !!! */
+    SetPanelBackground();
+    // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+    DrawBackground(DX, DY, DXSIZE, DYSIZE);
+#else
+    struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
+
+    /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
+    ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
+    BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
+              MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
+#endif
+#else
     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
               DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+#endif
   }
 
   SetPanelBackground();
   SetDrawBackgroundMask(REDRAW_DOOR_1);
 
+#if 1
+  UpdateAndDisplayGameControlValues();
+#else
+  UpdateGameDoorValues();
   DrawGameDoorValues();
+#endif
 
   if (!game.restart_level)
   {
@@ -3175,8 +4603,12 @@ void InitGame()
     MapTapeButtons();
 
     /* copy actual game door content to door double buffer for OpenDoor() */
+#if 1
+    BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
+#else
     BlitBitmap(drawto, bitmap_db_door,
               DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+#endif
 
     OpenDoor(DOOR_OPEN_ALL);
 
@@ -3187,12 +4619,28 @@ void InitGame()
 
     KeyboardAutoRepeatOffUnlessAutoplay();
 
+#if DEBUG_INIT_PLAYER
     if (options.debug)
     {
+      printf("Player status (final):\n");
+
       for (i = 0; i < MAX_PLAYERS; i++)
-       printf("Player %d %sactive.\n",
-              i + 1, (stored_player[i].active ? "" : "not "));
+      {
+       struct PlayerInfo *player = &stored_player[i];
+
+       printf("- player %d: present == %d, connected == %d, active == %d",
+              i + 1,
+              player->present,
+              player->connected,
+              player->active);
+
+       if (local_player == player)
+         printf(" (local player)");
+
+       printf("\n");
+      }
     }
+#endif
   }
 
 #if 1
@@ -3202,6 +4650,17 @@ void InitGame()
   MapTapeButtons();
 #endif
 
+  if (!game.restart_level && !tape.playing)
+  {
+    LevelStats_incPlayed(level_nr);
+
+    SaveLevelSetup_SeriesInfo();
+
+#if 0
+    printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
+#endif
+  }
+
   game.restart_level = FALSE;
 }
 
@@ -3419,6 +4878,10 @@ static void PlayerWins(struct PlayerInfo *player)
 
   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
                         level.native_em_level->lev->score : player->score);
+
+  player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
+                                     TimeLeft);
+  player->LevelSolved_CountingScore = player->score_final;
 }
 
 void GameWon()
@@ -3442,6 +4905,17 @@ void GameWon()
     local_player->LevelSolved_SaveTape = tape.recording;
     local_player->LevelSolved_SaveScore = !tape.playing;
 
+    if (!tape.playing)
+    {
+      LevelStats_incSolved(level_nr);
+
+      SaveLevelSetup_SeriesInfo();
+
+#if 0
+      printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
+#endif
+    }
+
     if (tape.auto_play)                /* tape might already be stopped here */
       tape.auto_play_level_solved = TRUE;
 
@@ -3452,7 +4926,7 @@ void GameWon()
     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);
+    time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
     score = score_final = local_player->score_final;
 
     if (TimeLeft > 0)
@@ -3460,7 +4934,7 @@ void GameWon()
       time_final = 0;
       score_final += TimeLeft * level.score[SC_TIME_BONUS];
     }
-    else if (level.time == 0 && TimePlayed < 999)
+    else if (game.no_time_limit && TimePlayed < 999)
     {
       time_final = 999;
       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
@@ -3473,8 +4947,18 @@ void GameWon()
       time = time_final;
       score = score_final;
 
+#if 1
+      local_player->LevelSolved_CountingTime = time;
+      local_player->LevelSolved_CountingScore = score;
+
+      game_panel_controls[GAME_PANEL_TIME].value = time;
+      game_panel_controls[GAME_PANEL_SCORE].value = score;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Time(time);
       DrawGameValue_Score(score);
+#endif
     }
 
     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
@@ -3548,8 +5032,18 @@ void GameWon()
     time  += time_count_steps * time_count_dir;
     score += time_count_steps * level.score[SC_TIME_BONUS];
 
+#if 1
+    local_player->LevelSolved_CountingTime = time;
+    local_player->LevelSolved_CountingScore = score;
+
+    game_panel_controls[GAME_PANEL_TIME].value = time;
+    game_panel_controls[GAME_PANEL_SCORE].value = score;
+
+    DisplayGameControlValues();
+#else
     DrawGameValue_Time(time);
     DrawGameValue_Score(score);
+#endif
 
     if (time == time_final)
       StopSound(SND_GAME_LEVELTIME_BONUS);
@@ -3601,14 +5095,20 @@ void GameEnd()
   {
     game_status = GAME_MODE_MAIN;
 
+#if 1
+    DrawAndFadeInMainMenu(REDRAW_FIELD);
+#else
     DrawMainMenu();
+#endif
 
     return;
   }
 
   if (!local_player->LevelSolved_SaveScore)
   {
+#if 1
     FadeOut(REDRAW_FIELD);
+#endif
 
     game_status = GAME_MODE_MAIN;
 
@@ -3620,6 +5120,7 @@ void GameEnd()
   if (level_nr == leveldir_current->handicap_level)
   {
     leveldir_current->handicap_level++;
+
     SaveLevelSetup_SeriesInfo();
   }
 
@@ -3640,7 +5141,9 @@ void GameEnd()
   }
   else
   {
+#if 1
     FadeOut(REDRAW_FIELD);
+#endif
 
     game_status = GAME_MODE_MAIN;
 
@@ -3958,6 +5461,10 @@ static void RemoveField(int x, int y)
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MV_NONE;
+#if 0
+  /* !!! this would prevent the removed tile from being redrawn !!! */
+  GfxRedraw[x][y] = GFX_REDRAW_NONE;
+#endif
 }
 
 void RemoveMovingField(int x, int y)
@@ -3983,7 +5490,7 @@ void RemoveMovingField(int x, int y)
 
       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
 
-      DrawLevelField(oldx, oldy);
+      TEST_DrawLevelField(oldx, oldy);
 
       return;
     }
@@ -4012,8 +5519,8 @@ void RemoveMovingField(int x, int y)
   if (next_element != EL_UNDEFINED)
     Feld[oldx][oldy] = next_element;
 
-  DrawLevelField(oldx, oldy);
-  DrawLevelField(newx, newy);
+  TEST_DrawLevelField(oldx, oldy);
+  TEST_DrawLevelField(newx, newy);
 }
 
 void DrawDynamite(int x, int y)
@@ -4121,12 +5628,51 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
 
   if (quick_relocation)
   {
-    int offset = (setup.scroll_delay ? 3 : 0);
-
     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
     {
-      if (center_screen)
+      if (!level.shifted_relocation || center_screen)
+      {
+       /* quick relocation (without scrolling), with centering of screen */
+
+       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                   x > SBX_Right + MIDPOSX ? SBX_Right :
+                   x - MIDPOSX);
+
+       scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                   y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                   y - MIDPOSY);
+      }
+      else
+      {
+       /* quick relocation (without scrolling), but do not center screen */
+
+       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
+                              old_x - MIDPOSX);
+
+       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                              old_y - MIDPOSY);
+
+       int offset_x = x + (scroll_x - center_scroll_x);
+       int offset_y = y + (scroll_y - center_scroll_y);
+
+       scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
+                   offset_x - MIDPOSX);
+
+       scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                   offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                   offset_y - MIDPOSY);
+      }
+    }
+    else
+    {
+#if 1
+      if (!level.shifted_relocation || center_screen)
       {
+       /* quick relocation (without scrolling), with centering of screen */
+
        scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
                    x > SBX_Right + MIDPOSX ? SBX_Right :
                    x - MIDPOSX);
@@ -4158,9 +5704,11 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
                    offset_y - MIDPOSY);
       }
-    }
-    else
-    {
+#else
+      /* quick relocation (without scrolling), inside visible screen area */
+
+      int offset = game.scroll_delay_value;
+
       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
          (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
        scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
@@ -4176,12 +5724,56 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
       /* don't scroll over playfield boundaries */
       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
        scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+#endif
     }
 
     RedrawPlayfield(TRUE, 0,0,0,0);
   }
   else
   {
+#if 1
+    int scroll_xx, scroll_yy;
+
+    if (!level.shifted_relocation || center_screen)
+    {
+      /* visible relocation (with scrolling), with centering of screen */
+
+      scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                  x > SBX_Right + MIDPOSX ? SBX_Right :
+                  x - MIDPOSX);
+
+      scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                  y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                  y - MIDPOSY);
+    }
+    else
+    {
+      /* visible relocation (with scrolling), but do not center screen */
+
+      int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
+                            old_x - MIDPOSX);
+
+      int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                            old_y - MIDPOSY);
+
+      int offset_x = x + (scroll_x - center_scroll_x);
+      int offset_y = y + (scroll_y - center_scroll_y);
+
+      scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
+                  offset_x > SBX_Right + MIDPOSX ? SBX_Right :
+                  offset_x - MIDPOSX);
+
+      scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                  offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                  offset_y - MIDPOSY);
+    }
+
+#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);
@@ -4189,6 +5781,7 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
     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 */
 
@@ -4297,10 +5890,19 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   Feld[jx][jy] = el_player;
   InitPlayerField(jx, jy, el_player, TRUE);
 
+  /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
+     possible that the relocation target field did not contain a player element,
+     but a walkable element, to which the new player was relocated -- in this
+     case, restore that (already initialized!) element on the player field */
   if (!ELEM_IS_PLAYER(element))        /* player may be set on walkable element */
   {
-    Feld[jx][jy] = element;
+    Feld[jx][jy] = element;    /* restore previously existing element */
+#if 0
+    /* !!! do not initialize already initialized element a second time !!! */
+    /* (this causes at least problems with "element creation" CE trigger for
+       already existing elements, and existing Sokoban fields counted twice) */
     InitField(jx, jy, FALSE);
+#endif
   }
 
   /* only visually relocate centered player */
@@ -4316,6 +5918,21 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
 
   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
                                      player->index_bit, enter_side);
+
+#if 1
+  if (player->is_switching)
+  {
+    /* ensure that relocation while still switching an element does not cause
+       a new element to be treated as also switched directly after relocation
+       (this is important for teleporter switches that teleport the player to
+       a place where another teleporter switch is in the same direction, which
+       would then incorrectly be treated as immediately switched before the
+       direction key that caused the switch was released) */
+
+    player->switch_x += jx - old_jx;
+    player->switch_y += jy - old_jy;
+  }
+#endif
 }
 
 void Explode(int ex, int ey, int phase, int mode)
@@ -4628,12 +6245,12 @@ void Explode(int ex, int ey, int phase, int mode)
 
     InitField_WithBug2(x, y, FALSE);
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     TestIfElementTouchesCustomElement(x, y);
 
     if (GFX_CRUMBLED(element))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
       StorePlayer[x][y] = 0;
@@ -4647,7 +6264,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
 
     if (phase == delay)
-      DrawLevelFieldCrumbledSand(x, y);
+      TEST_DrawLevelFieldCrumbled(x, y);
 
     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
@@ -4724,8 +6341,12 @@ void Bang(int x, int y)
   {
     struct PlayerInfo *player = PLAYERINFO(x, y);
 
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+    element = Feld[x][y] = player->initial_element;
+#else
     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
                            player->element_nr);
+#endif
 
     if (level.use_explosion_element[player->index_nr])
     {
@@ -4836,12 +6457,19 @@ static void InitBeltMovement()
     for (j = 0; j < NUM_BELT_PARTS; j++)
     {
       int element = belt_base_active_element[belt_nr] + j;
-      int graphic = el2img(element);
+      int graphic_1 = el2img(element);
+      int graphic_2 = el2panelimg(element);
 
       if (game.belt_dir[i] == MV_LEFT)
-       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
+      {
+       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
+       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
+      }
       else
-       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
+      {
+       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
+       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
+      }
     }
   }
 
@@ -4917,12 +6545,19 @@ static void ToggleBeltSwitch(int x, int y)
   for (i = 0; i < NUM_BELT_PARTS; i++)
   {
     int element = belt_base_active_element[belt_nr] + i;
-    int graphic = el2img(element);
+    int graphic_1 = el2img(element);
+    int graphic_2 = el2panelimg(element);
 
     if (belt_dir == MV_LEFT)
-      graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
+    {
+      graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
+      graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
+    }
     else
-      graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
+    {
+      graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
+      graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
+    }
   }
 
   SCAN_PLAYFIELD(xx, yy)
@@ -4936,7 +6571,7 @@ static void ToggleBeltSwitch(int x, int y)
       if (e_belt_nr == belt_nr)
       {
        Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
     else if (IS_BELT(element) && belt_dir != MV_NONE)
@@ -4948,7 +6583,7 @@ static void ToggleBeltSwitch(int x, int y)
        int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
 
        Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
@@ -4960,7 +6595,7 @@ static void ToggleBeltSwitch(int x, int y)
        int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
 
        Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
   }
@@ -4981,34 +6616,34 @@ static void ToggleSwitchgateSwitch(int x, int y)
        element == EL_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
             element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
 #else
     if (element == EL_SWITCHGATE_SWITCH_UP)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
 #endif
     else if (element == EL_SWITCHGATE_OPEN ||
@@ -5056,25 +6691,25 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
        game.light_time_left > 0)
     {
       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
             game.light_time_left == 0)
     {
       Feld[x][y] = EL_LIGHT_SWITCH;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER &&
             game.light_time_left > 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.light_time_left == 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
             element == EL_INVISIBLE_WALL ||
@@ -5083,11 +6718,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
       if (game.light_time_left > 0)
        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -5096,11 +6731,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
       if (game.light_time_left == 0)
        Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
   }
 }
@@ -5117,13 +6752,13 @@ static void RedrawAllInvisibleElementsForLenses()
        game.lenses_time_left > 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.lenses_time_left == 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
             element == EL_INVISIBLE_WALL ||
@@ -5132,11 +6767,11 @@ static void RedrawAllInvisibleElementsForLenses()
       if (game.lenses_time_left > 0)
        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -5145,11 +6780,11 @@ static void RedrawAllInvisibleElementsForLenses()
       if (game.lenses_time_left == 0)
        Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
   }
 }
@@ -5166,13 +6801,13 @@ static void RedrawAllInvisibleElementsForMagnifier()
        game.magnify_time_left > 0)
     {
       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
             game.magnify_time_left == 0)
     {
       Feld[x][y] = EL_EMC_FAKE_GRASS;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY(element) &&
             game.magnify_time_left > 0)
@@ -5183,8 +6818,10 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
                    IS_EMC_GATE_GRAY(element) ?
                    element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+                   IS_DC_GATE_GRAY(element) ?
+                   EL_DC_GATE_WHITE_GRAY_ACTIVE :
                    element);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY_ACTIVE(element) &&
             game.magnify_time_left == 0)
@@ -5195,8 +6832,10 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
                    IS_EMC_GATE_GRAY_ACTIVE(element) ?
                    element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+                   IS_DC_GATE_GRAY_ACTIVE(element) ?
+                   EL_DC_GATE_WHITE_GRAY :
                    element);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -5233,7 +6872,7 @@ static void ActivateTimegateSwitch(int x, int y)
     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
     {
       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     */
 
@@ -5275,7 +6914,7 @@ void Impact(int x, int y)
       RemoveMovingField(x, y + 1);
       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
       Feld[x][y + 2] = EL_ROCK;
-      DrawLevelField(x, y + 2);
+      TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
     }
@@ -5285,7 +6924,7 @@ void Impact(int x, int y)
       RemoveMovingField(x, y + 1);
       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
       Feld[x][y + 2] = EL_ROCK;
-      DrawLevelField(x, y + 2);
+      TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
     }
@@ -5309,7 +6948,7 @@ void Impact(int x, int y)
       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
   {
     ResetGfxAnimation(x, y);
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
   }
 
   if (impact && CAN_EXPLODE_IMPACT(element))
@@ -5783,7 +7422,7 @@ inline static void TurnRoundExt(int x, int y)
       {
        Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
        ResetGfxAnimation(move_x, move_y);
-       DrawLevelField(move_x, move_y);
+       TEST_DrawLevelField(move_x, move_y);
 
        MovDir[x][y] = back_dir;
       }
@@ -6400,10 +8039,18 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
       {
        if (!MovDelay[x][y])
+       {
          MovDelay[x][y] = TILEY + 1;
 
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
        if (MovDelay[x][y])
        {
+         DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
          MovDelay[x][y]--;
          if (MovDelay[x][y])
            return;
@@ -6414,6 +8061,33 @@ void StartMoving(int x, int y)
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 
+       PlayLevelSoundAction(x, y, ACTION_FILLING);
+      }
+      else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+      {
+       if (!MovDelay[x][y])
+       {
+         MovDelay[x][y] = TILEY + 1;
+
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
+       if (MovDelay[x][y])
+       {
+         DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_QUICKSAND_EMPTY;
+       Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+       Store[x][y + 1] = Store[x][y];
+       Store[x][y] = 0;
+
        PlayLevelSoundAction(x, y, ACTION_FILLING);
       }
     }
@@ -6437,10 +8111,18 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
       {
        if (!MovDelay[x][y])
+       {
          MovDelay[x][y] = TILEY + 1;
 
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
        if (MovDelay[x][y])
        {
+         DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
          MovDelay[x][y]--;
          if (MovDelay[x][y])
            return;
@@ -6451,6 +8133,33 @@ void StartMoving(int x, int y)
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 
+       PlayLevelSoundAction(x, y, ACTION_FILLING);
+      }
+      else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+      {
+       if (!MovDelay[x][y])
+       {
+         MovDelay[x][y] = TILEY + 1;
+
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
+       if (MovDelay[x][y])
+       {
+         DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
+       Feld[x][y + 1] = EL_QUICKSAND_FULL;
+       Store[x][y + 1] = Store[x][y];
+       Store[x][y] = 0;
+
        PlayLevelSoundAction(x, y, ACTION_FILLING);
       }
     }
@@ -6489,7 +8198,7 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -6517,7 +8226,7 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -6545,7 +8254,7 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -6818,7 +8527,7 @@ void StartMoving(int x, int y)
                               element == EL_SP_SNIKSNAK ||
                               element == EL_SP_ELECTRON ||
                               element == EL_MOLE))
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
       }
     }
 
@@ -6852,7 +8561,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
 
@@ -6892,7 +8601,7 @@ void StartMoving(int x, int y)
 
            if (IN_SCR_FIELD(sx, sy))
            {
-             DrawLevelFieldCrumbledSand(xx, yy);
+             TEST_DrawLevelFieldCrumbled(xx, yy);
              DrawGraphic(sx, sy, flame_graphic, frame);
            }
          }
@@ -6900,7 +8609,7 @@ void StartMoving(int x, int y)
          {
            if (Feld[xx][yy] == EL_FLAMES)
              Feld[xx][yy] = EL_EMPTY;
-           DrawLevelField(xx, yy);
+           TEST_DrawLevelField(xx, yy);
          }
        }
       }
@@ -6943,7 +8652,7 @@ void StartMoving(int x, int y)
          Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
       {
        RemoveField(x, y);
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
        if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
@@ -6959,7 +8668,7 @@ void StartMoving(int x, int y)
       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
       {
        if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        else
          GfxDir[x][y] = MovDir[x][y] = MV_NONE;
       }
@@ -6970,7 +8679,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -6984,7 +8693,7 @@ void StartMoving(int x, int y)
        else
        {
          Feld[newx][newy] = EL_EMPTY;
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        }
 
        PlayLevelSound(x, y, SND_PIG_DIGGING);
@@ -6994,7 +8703,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -7073,7 +8782,7 @@ void StartMoving(int x, int y)
        else
        {
          Feld[newx][newy] = EL_EMPTY;
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
 
          PlayLevelSoundAction(x, y, ACTION_DIGGING);
        }
@@ -7084,7 +8793,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 #endif
 
        return;
@@ -7093,6 +8802,10 @@ void StartMoving(int x, int y)
     else if (IS_CUSTOM_ELEMENT(element) &&
             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
     {
+#if 1
+      if (!DigFieldByCE(newx, newy, element))
+       return;
+#else
       int new_element = Feld[newx][newy];
 
       if (!IS_FREE(newx, newy))
@@ -7121,7 +8834,7 @@ void StartMoving(int x, int y)
        else
        {
          RemoveField(newx, newy);
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        }
 
        /* if digged element was about to explode, prevent the explosion */
@@ -7131,6 +8844,7 @@ void StartMoving(int x, int y)
       }
 
       Store[newx][newy] = EL_EMPTY;
+
 #if 1
       /* this makes it possible to leave the removed element again */
       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
@@ -7144,6 +8858,8 @@ void StartMoving(int x, int y)
        Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
                             new_element : move_leave_element);
       }
+#endif
+
 #endif
 
       if (move_pattern & MV_MAZE_RUNNER_STYLE)
@@ -7159,7 +8875,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -7186,7 +8902,7 @@ void StartMoving(int x, int y)
          if (IS_PLAYER(x, y))
            DrawPlayerField(x, y);
          else
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
 
          PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
 
@@ -7224,7 +8940,7 @@ void StartMoving(int x, int y)
       else
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
@@ -7255,7 +8971,7 @@ void StartMoving(int x, int y)
       else
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
@@ -7278,7 +8994,7 @@ void StartMoving(int x, int y)
 
        ResetGfxAnimation(x, y);
        GfxAction[x][y] = ACTION_DIGGING;
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
 
@@ -7287,7 +9003,7 @@ void StartMoving(int x, int y)
       else     /* element == EL_PACMAN */
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
        PlayLevelSound(x, y, SND_PACMAN_DIGGING);
       }
     }
@@ -7369,7 +9085,7 @@ void ContinueMoving(int x, int y)
           GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
 #endif
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     return;    /* element is still moving */
   }
@@ -7388,7 +9104,7 @@ void ContinueMoving(int x, int y)
   {
     Feld[x][y] = EL_SAND;
 
-    DrawLevelFieldCrumbledSandNeighbours(x, y);
+    TEST_DrawLevelFieldCrumbledNeighbours(x, y);
   }
   else if (element == EL_QUICKSAND_FILLING)
   {
@@ -7497,7 +9213,7 @@ void ContinueMoving(int x, int y)
   }
 
 #if USE_NEW_CUSTOM_VALUE
-    CustomValue[newx][newy] = CustomValue[x][y];
+  CustomValue[newx][newy] = CustomValue[x][y];
 #endif
 
   ChangeDelay[x][y] = 0;
@@ -7555,7 +9271,7 @@ void ContinueMoving(int x, int y)
     InitField(x, y, FALSE);
 
     if (GFX_CRUMBLED(Feld[x][y]))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
     if (ELEM_IS_PLAYER(move_leave_element))
       RelocatePlayer(x, y, move_leave_element);
@@ -7571,8 +9287,8 @@ void ContinueMoving(int x, int y)
        element_info[element].move_pattern == MV_WHEN_DROPPED)))
     GfxDir[x][y] = MovDir[newx][newy] = 0;
 
-  DrawLevelField(x, y);
-  DrawLevelField(newx, newy);
+  TEST_DrawLevelField(x, y);
+  TEST_DrawLevelField(newx, newy);
 
   Stop[newx][newy] = TRUE;     /* ignore this element until the next frame */
 
@@ -7617,6 +9333,11 @@ void ContinueMoving(int x, int y)
   else if (element == EL_PENGUIN)
     TestIfFriendTouchesBadThing(newx, newy);
 
+  if (DONT_GET_HIT_BY(element))
+  {
+    TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
+  }
+
   /* give the player one last chance (one more frame) to move away */
   if (CAN_FALL(element) && direction == MV_DOWN &&
       (last_line || (!IS_FREE(x, newy + 1) &&
@@ -7834,7 +9555,7 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
       AmoebaNr[x][y] = 0;
       Feld[x][y] = new_element;
       InitField(x, y, FALSE);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
       done = TRUE;
     }
   }
@@ -7847,8 +9568,8 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
 
 void AmoebeWaechst(int x, int y)
 {
-  static unsigned long sound_delay = 0;
-  static unsigned long sound_delay_value = 0;
+  static unsigned int sound_delay = 0;
+  static unsigned int sound_delay_value = 0;
 
   if (!MovDelay[x][y])         /* start new growing cycle */
   {
@@ -7876,15 +9597,15 @@ void AmoebeWaechst(int x, int y)
     {
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
 
 void AmoebaDisappearing(int x, int y)
 {
-  static unsigned long sound_delay = 0;
-  static unsigned long sound_delay_value = 0;
+  static unsigned int sound_delay = 0;
+  static unsigned int sound_delay_value = 0;
 
   if (!MovDelay[x][y])         /* start new shrinking cycle */
   {
@@ -7908,7 +9629,7 @@ void AmoebaDisappearing(int x, int y)
     if (!MovDelay[x][y])
     {
       Feld[x][y] = EL_EMPTY;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* don't let mole enter this field in this cycle;
         (give priority to objects falling to this field from above) */
@@ -7935,7 +9656,7 @@ void AmoebeAbleger(int ax, int ay)
   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
   {
     Feld[ax][ay] = EL_AMOEBA_DEAD;
-    DrawLevelField(ax, ay);
+    TEST_DrawLevelField(ax, ay);
     return;
   }
 
@@ -8005,7 +9726,7 @@ void AmoebeAbleger(int ax, int ay)
       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
       {
        Feld[ax][ay] = EL_AMOEBA_DEAD;
-       DrawLevelField(ax, ay);
+       TEST_DrawLevelField(ax, ay);
        AmoebaCnt[AmoebaNr[ax][ay]]--;
 
        if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
@@ -8069,7 +9790,7 @@ void AmoebeAbleger(int ax, int ay)
     return;
   }
 
-  DrawLevelField(newax, neway);
+  TEST_DrawLevelField(newax, neway);
 }
 
 void Life(int ax, int ay)
@@ -8127,7 +9848,7 @@ void Life(int ax, int ay)
       {
        Feld[xx][yy] = EL_EMPTY;
        if (!Stop[xx][yy])
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
@@ -8140,7 +9861,7 @@ void Life(int ax, int ay)
        Feld[xx][yy] = element;
        MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
        if (!Stop[xx][yy])
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
@@ -8165,7 +9886,11 @@ static void RunRobotWheel(int x, int y)
 static void StopRobotWheel(int x, int y)
 {
   if (ZX == x && ZY == y)
+  {
     ZX = ZY = -1;
+
+    game.robot_wheel_active = FALSE;
+  }
 }
 
 static void InitTimegateWheel(int x, int y)
@@ -8364,9 +10089,6 @@ void DrawTwinkleOnField(int x, int y)
   {
     MovDelay[x][y]--;
 
-    if (setup.direct_draw && MovDelay[x][y])
-      SetDrawtoField(DRAW_BUFFERED);
-
     DrawLevelElementAnimation(x, y, Feld[x][y]);
 
     if (MovDelay[x][y] != 0)
@@ -8375,18 +10097,6 @@ void DrawTwinkleOnField(int x, int y)
                                           10 - MovDelay[x][y]);
 
       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
-
-      if (setup.direct_draw)
-      {
-       int dest_x, dest_y;
-
-       dest_x = FX + SCREENX(x) * TILEX;
-       dest_y = FY + SCREENY(y) * TILEY;
-
-       BlitBitmap(drawto_field, window,
-                  dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
-       SetDrawtoField(DRAW_DIRECT);
-      }
     }
   }
 }
@@ -8415,28 +10125,28 @@ void MauerWaechst(int x, int y)
       if (MovDir[x][y] == MV_LEFT)
       {
        if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
-         DrawLevelField(x - 1, y);
+         TEST_DrawLevelField(x - 1, y);
       }
       else if (MovDir[x][y] == MV_RIGHT)
       {
        if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
-         DrawLevelField(x + 1, y);
+         TEST_DrawLevelField(x + 1, y);
       }
       else if (MovDir[x][y] == MV_UP)
       {
        if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
-         DrawLevelField(x, y - 1);
+         TEST_DrawLevelField(x, y - 1);
       }
       else
       {
        if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
-         DrawLevelField(x, y + 1);
+         TEST_DrawLevelField(x, y + 1);
       }
 
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -8527,7 +10237,7 @@ void MauerAbleger(int ax, int ay)
   }
 
   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
-    DrawLevelField(ax, ay);
+    TEST_DrawLevelField(ax, ay);
 
   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
     oben_massiv = TRUE;
@@ -8645,7 +10355,7 @@ void MauerAblegerStahl(int ax, int ay)
        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
       ((links_massiv && rechts_massiv) ||
        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
-    Feld[ax][ay] = EL_WALL;
+    Feld[ax][ay] = EL_STEELWALL;
 
   if (new_wall)
     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
@@ -8691,7 +10401,7 @@ void CheckForDragon(int x, int y)
        if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
        {
          Feld[xx][yy] = EL_EMPTY;
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        }
        else
          break;
@@ -8755,7 +10465,7 @@ static void ChangeActiveTrap(int x, int y)
 
   /* if new animation frame was drawn, correct crumbled sand border */
   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
-    DrawLevelFieldCrumbledSand(x, y);
+    TEST_DrawLevelFieldCrumbled(x, y);
 }
 
 static int getSpecialActionElement(int element, int number, int base_element)
@@ -8789,6 +10499,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
   int action_type = change->action_type;
   int action_mode = change->action_mode;
   int action_arg = change->action_arg;
+  int action_element = change->action_element;
   int i;
 
   if (!change->has_action)
@@ -8800,11 +10511,21 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     (level.time > 0 ? TimeLeft :
      TimePlayed);
 
-  int action_arg_element =
+  int action_arg_element_raw =
     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
+     action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
+     action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
+     action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
+     action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
      EL_EMPTY);
+  int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
+
+#if 0
+  if (action_arg_element_raw == EL_GROUP_START)
+    printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
+#endif
 
   int action_arg_direction =
     (action_arg >= CA_ARG_DIRECTION_LEFT &&
@@ -8861,10 +10582,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
+     action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
+     action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
+     action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
      -1);
 
   int action_arg_number_old =
@@ -8880,16 +10604,23 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                            action_mode, action_arg_number,
                            action_arg_number_min, action_arg_number_max);
 
+#if 1
+  int trigger_player_bits =
+    (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
+     change->actual_trigger_player_bits : change->trigger_player);
+#else
   int trigger_player_bits =
     (change->actual_trigger_player >= EL_PLAYER_1 &&
      change->actual_trigger_player <= EL_PLAYER_4 ?
      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
      PLAYER_BITS_ANY);
+#endif
 
   int action_arg_player_bits =
     (action_arg >= CA_ARG_PLAYER_1 &&
      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
+     action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
      PLAYER_BITS_ANY);
 
   /* ---------- execute action  -------------------------------------------- */
@@ -8927,7 +10658,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       {
        TimeLeft = action_arg_number_new;
 
+#if 1
+       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
@@ -8941,7 +10678,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->score = action_arg_number_new;
 
+#if 1
+      game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Score(local_player->score);
+#endif
 
       break;
     }
@@ -8950,7 +10693,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->gems_still_needed = action_arg_number_new;
 
+#if 1
+      game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Emeralds(local_player->gems_still_needed);
+#endif
 
       break;
     }
@@ -8973,6 +10722,33 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_LEVEL_RANDOM_SEED:
+    {
+#if 1
+      /* ensure that setting a new random seed while playing is predictable */
+      InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
+#else
+      InitRND(action_arg_number_new);
+#endif
+
+#if 0
+      printf("::: %d -> %d\n", action_arg_number_new, RND(10));
+#endif
+
+#if 0
+      {
+       int i;
+
+       printf("::: ");
+       for (i = 0; i < 9; i++)
+         printf("%d, ", RND(2));
+       printf("\n");
+      }
+#endif
+
+      break;
+    }
+
     /* ---------- player actions  ------------------------------------------ */
 
     case CA_MOVE_PLAYER:
@@ -9027,6 +10803,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_PLAYER_SPEED:
     {
+#if 0
+      printf("::: trigger_player_bits == %d\n", trigger_player_bits);
+#endif
+
       for (i = 0; i < MAX_PLAYERS; i++)
       {
        if (trigger_player_bits & (1 << i))
@@ -9144,6 +10924,104 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_PLAYER_INVENTORY:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int j, k;
+
+       if (trigger_player_bits & (1 << i))
+       {
+         int inventory_element = action_arg_element;
+
+         if (action_arg == CA_ARG_ELEMENT_TARGET ||
+             action_arg == CA_ARG_ELEMENT_TRIGGER ||
+             action_arg == CA_ARG_ELEMENT_ACTION)
+         {
+           int element = inventory_element;
+           int collect_count = element_info[element].collect_count_initial;
+
+           if (!IS_CUSTOM_ELEMENT(element))
+             collect_count = 1;
+
+           if (collect_count == 0)
+             player->inventory_infinite_element = element;
+           else
+             for (k = 0; k < collect_count; k++)
+               if (player->inventory_size < MAX_INVENTORY_SIZE)
+                 player->inventory_element[player->inventory_size++] =
+                   element;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
+                  action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
+                  action_arg == CA_ARG_INVENTORY_RM_ACTION)
+         {
+           if (player->inventory_infinite_element != EL_UNDEFINED &&
+               IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
+                                    action_arg_element_raw))
+             player->inventory_infinite_element = EL_UNDEFINED;
+
+           for (k = 0, j = 0; j < player->inventory_size; j++)
+           {
+             if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
+                                       action_arg_element_raw))
+               player->inventory_element[k++] = player->inventory_element[j];
+           }
+
+           player->inventory_size = k;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
+         {
+           if (player->inventory_size > 0)
+           {
+             for (j = 0; j < player->inventory_size - 1; j++)
+               player->inventory_element[j] = player->inventory_element[j + 1];
+
+             player->inventory_size--;
+           }
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
+         {
+           if (player->inventory_size > 0)
+             player->inventory_size--;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
+         {
+           player->inventory_infinite_element = EL_UNDEFINED;
+           player->inventory_size = 0;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RESET)
+         {
+           player->inventory_infinite_element = EL_UNDEFINED;
+           player->inventory_size = 0;
+
+           if (level.use_initial_inventory[i])
+           {
+             for (j = 0; j < level.initial_inventory_size[i]; j++)
+             {
+               int element = level.initial_inventory_content[i][j];
+               int collect_count = element_info[element].collect_count_initial;
+
+               if (!IS_CUSTOM_ELEMENT(element))
+                 collect_count = 1;
+
+               if (collect_count == 0)
+                 player->inventory_infinite_element = element;
+               else
+                 for (k = 0; k < collect_count; k++)
+                   if (player->inventory_size < MAX_INVENTORY_SIZE)
+                     player->inventory_element[player->inventory_size++] =
+                       element;
+             }
+           }
+         }
+       }
+      }
+
+      break;
+    }
+
     /* ---------- CE actions  ---------------------------------------------- */
 
     case CA_SET_CE_VALUE:
@@ -9212,6 +11090,38 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_CE_ARTWORK:
+    {
+      int artwork_element = action_arg_element;
+      boolean reset_frame = FALSE;
+      int xx, yy;
+
+      if (action_arg == CA_ARG_ELEMENT_RESET)
+       artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
+                          element);
+
+      if (ei->gfx_element != artwork_element)
+       reset_frame = TRUE;
+
+      ei->gfx_element = artwork_element;
+
+      SCAN_PLAYFIELD(xx, yy)
+      {
+       if (Feld[xx][yy] == element)
+       {
+         if (reset_frame)
+         {
+           ResetGfxAnimation(xx, yy);
+           ResetRandomAnimationValue(xx, yy);
+         }
+
+         TEST_DrawLevelField(xx, yy);
+       }
+      }
+
+      break;
+    }
+
     /* ---------- engine actions  ------------------------------------------ */
 
     case CA_SET_ENGINE_SCAN_MODE:
@@ -9288,10 +11198,10 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
     ResetRandomAnimationValue(x, y);
 #endif
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     if (GFX_CRUMBLED(new_element))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
   }
 
 #if 1
@@ -9371,7 +11281,8 @@ static boolean ChangeElement(int x, int y, int element, int page)
   {
     /* reset actual trigger element, trigger player and action element */
     change->actual_trigger_element = EL_EMPTY;
-    change->actual_trigger_player = EL_PLAYER_1;
+    change->actual_trigger_player = EL_EMPTY;
+    change->actual_trigger_player_bits = CH_PLAYER_NONE;
     change->actual_trigger_side = CH_SIDE_NONE;
     change->actual_trigger_ce_value = 0;
     change->actual_trigger_ce_score = 0;
@@ -9531,6 +11442,7 @@ static void HandleElementChange(int x, int y, int page)
   int element = MovingOrBlocked2Element(x, y);
   struct ElementInfo *ei = &element_info[element];
   struct ElementChangeInfo *change = &ei->change_page[page];
+  boolean handle_action_before_change = FALSE;
 
 #ifdef DEBUG
   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
@@ -9613,6 +11525,15 @@ static void HandleElementChange(int x, int y, int page)
       return;
     }
 
+#if 1
+    /* special case: set new level random seed before changing element */
+    if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
+      handle_action_before_change = TRUE;
+
+    if (change->has_action && handle_action_before_change)
+      ExecuteCustomElementAction(x, y, element, page);
+#endif
+
     if (change->can_change)
     {
       if (ChangeElement(x, y, element, page))
@@ -9622,7 +11543,7 @@ static void HandleElementChange(int x, int y, int page)
       }
     }
 
-    if (change->has_action)
+    if (change->has_action && !handle_action_before_change)
       ExecuteCustomElementAction(x, y, element, page);
   }
 }
@@ -9752,11 +11673,17 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
       {
        change->actual_trigger_element = trigger_element;
-       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+       change->actual_trigger_player_bits = trigger_player;
        change->actual_trigger_side = trigger_side;
        change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
        change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
 
+#if 0
+       printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
+              element, EL_NAME(element), p);
+#endif
+
        if ((change->can_change && !change_done) || change->has_action)
        {
          int x, y;
@@ -9767,6 +11694,21 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
            {
              if (change->can_change && !change_done)
              {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+               /* if element already changed in this frame, not only prevent
+                  another element change (checked in ChangeElement()), but
+                  also prevent additional element actions for this element */
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+#endif
+
+#if 0
+               printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
+                      element, EL_NAME(element), p);
+#endif
+
                ChangeDelay[x][y] = 1;
                ChangeEvent[x][y] = trigger_event;
 
@@ -9775,6 +11717,22 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
 #if USE_NEW_DELAYED_ACTION
              else if (change->has_action)
              {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+               /* if element already changed in this frame, not only prevent
+                  another element change (checked in ChangeElement()), but
+                  also prevent additional element actions for this element */
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+#endif
+
+
+#if 0
+               printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
+                      element, EL_NAME(element), p);
+#endif
+
                ExecuteCustomElementAction(x, y, element, p);
                PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
              }
@@ -9792,6 +11750,12 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
          {
            change_done = TRUE;
            change_done_any = TRUE;
+
+#if 0
+           printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
+                  element, EL_NAME(element), p);
+#endif
+
          }
        }
       }
@@ -9846,6 +11810,10 @@ static boolean CheckElementChangeExt(int x, int y,
 
   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
 
+#if 0
+  printf("::: X: trigger_player_bits == %d\n", trigger_player);
+#endif
+
   for (p = 0; p < element_info[element].num_change_pages; p++)
   {
     struct ElementChangeInfo *change = &element_info[element].change_page[p];
@@ -9871,7 +11839,8 @@ static boolean CheckElementChangeExt(int x, int y,
         IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
     {
       change->actual_trigger_element = trigger_element;
-      change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+      change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+      change->actual_trigger_player_bits = trigger_player;
       change->actual_trigger_side = trigger_side;
       change->actual_trigger_ce_value = CustomValue[x][y];
       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
@@ -10112,9 +12081,22 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
   }
 }
 
+static void CheckSingleStepMode(struct PlayerInfo *player)
+{
+  if (tape.single_step && tape.recording && !tape.pausing)
+  {
+    /* as it is called "single step mode", just return to pause mode when the
+       player stopped moving after one tile (or never starts moving at all) */
+    if (!player->is_moving && !player->is_pushing)
+    {
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+      SnapField(player, 0, 0);                 /* stop snapping */
+    }
+  }
+}
+
 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
 {
-  boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
   int left     = player_action & JOY_LEFT;
   int right    = player_action & JOY_RIGHT;
   int up       = player_action & JOY_UP;
@@ -10130,23 +12112,16 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
   if (player_action)
   {
     if (button1)
-      snapped = SnapField(player, dx, dy);
+      SnapField(player, dx, dy);
     else
     {
       if (button2)
-       dropped = DropElement(player);
+       DropElement(player);
 
-      moved = MovePlayer(player, dx, dy);
+      MovePlayer(player, dx, dy);
     }
 
-    if (tape.single_step && tape.recording && !tape.pausing)
-    {
-      if (button1 || (dropped && !moved))
-      {
-       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
-       SnapField(player, 0, 0);                /* stop snapping */
-      }
-    }
+    CheckSingleStepMode(player);
 
     SetPlayerWaiting(player, FALSE);
 
@@ -10170,6 +12145,8 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
     player->is_dropping_pressed = FALSE;
     player->drop_pressed_delay = 0;
 
+    CheckSingleStepMode(player);
+
     return 0;
   }
 }
@@ -10178,6 +12155,7 @@ static void CheckLevelTime()
 {
   int i;
 
+  /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     if (level.native_em_level->lev->home == 0) /* all players at home */
@@ -10195,6 +12173,21 @@ static void CheckLevelTime()
        level.native_em_level->ply[3]->alive == 0)      /* all dead */
       AllPlayersGone = TRUE;
   }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    if (game_sp.LevelSolved &&
+       !game_sp.GameOver)                              /* game won */
+    {
+      PlayerWins(local_player);
+
+      game_sp.GameOver = TRUE;
+
+      AllPlayersGone = TRUE;
+    }
+
+    if (game_sp.GameOver)                              /* game lost */
+      AllPlayersGone = TRUE;
+  }
 
   if (TimeFrames >= FRAMES_PER_SECOND)
   {
@@ -10225,7 +12218,17 @@ static void CheckLevelTime()
        if (TimeLeft <= 10 && setup.time_limit)
          PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
+#if 1
+       /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
+          is reset from other values in UpdateGameDoorValues() -- FIX THIS */
+
+       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+       /* (already called by UpdateAndDisplayGameControlValues() below) */
+       // DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
        {
@@ -10236,16 +12239,33 @@ static void CheckLevelTime()
              KillPlayer(&stored_player[i]);
        }
       }
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+#if 1
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
+      {
+       game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
+
+       /* (already called by UpdateAndDisplayGameControlValues() below) */
+       // DisplayGameControlValues();
+      }
+#else
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
+#endif
 
       level.native_em_level->lev->time =
-       (level.time == 0 ? TimePlayed : TimeLeft);
+       (game.no_time_limit ? TimePlayed : TimeLeft);
     }
 
     if (tape.recording || tape.playing)
       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
   }
+
+#if 1
+  UpdateAndDisplayGameControlValues();
+#else
+  UpdateGameDoorValues();
+  DrawGameDoorValues();
+#endif
 }
 
 void AdvanceFrameAndPlayerCounters(int player_nr)
@@ -10300,9 +12320,9 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
 }
 
 void StartGameActions(boolean init_network_game, boolean record_tape,
-                     long random_seed)
+                     int random_seed)
 {
-  unsigned long new_random_seed = InitRND(random_seed);
+  unsigned int new_random_seed = InitRND(random_seed);
 
   if (record_tape)
     TapeStartRecording(new_random_seed);
@@ -10321,8 +12341,8 @@ void StartGameActions(boolean init_network_game, boolean record_tape,
 
 void GameActions()
 {
-  static unsigned long game_frame_delay = 0;
-  unsigned long game_frame_delay_value;
+  static unsigned int game_frame_delay = 0;
+  unsigned int game_frame_delay_value;
   byte *recorded_player_action;
   byte summarized_player_action = 0;
   byte tape_action[MAX_PLAYERS];
@@ -10331,9 +12351,9 @@ void GameActions()
   /* detect endless loops, caused by custom element programming */
   if (recursion_loop_detected && recursion_loop_depth == 0)
   {
-    char *message = getStringCat3("Internal Error ! Element ",
+    char *message = getStringCat3("Internal Error! Element ",
                                  EL_NAME(recursion_loop_element),
-                                 " caused endless loop ! Quit the game ?");
+                                 " caused endless loop! Quit the game?");
 
     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
          EL_NAME(recursion_loop_element));
@@ -10348,8 +12368,9 @@ void GameActions()
   }
 
   if (game.restart_level)
-    StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
+    StartGameActions(options.network, setup.autorecord, level.random_seed);
 
+  /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     if (level.native_em_level->lev->home == 0) /* all players at home */
@@ -10367,6 +12388,21 @@ void GameActions()
        level.native_em_level->ply[3]->alive == 0)      /* all dead */
       AllPlayersGone = TRUE;
   }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    if (game_sp.LevelSolved &&
+       !game_sp.GameOver)                              /* game won */
+    {
+      PlayerWins(local_player);
+
+      game_sp.GameOver = TRUE;
+
+      AllPlayersGone = TRUE;
+    }
+
+    if (game_sp.GameOver)                              /* game lost */
+      AllPlayersGone = TRUE;
+  }
 
   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
     GameWon();
@@ -10432,8 +12468,13 @@ void GameActions()
   {
     summarized_player_action |= stored_player[i].action;
 
+#if 1
+    if (!network_playing && (game.team_mode || tape.playing))
+      stored_player[i].effective_action = stored_player[i].action;
+#else
     if (!network_playing)
       stored_player[i].effective_action = stored_player[i].action;
+#endif
   }
 
 #if defined(NETWORK_AVALIABLE)
@@ -10441,10 +12482,13 @@ void GameActions()
     SendToServer_MovePlayer(summarized_player_action);
 #endif
 
-  if (!options.network && !setup.team_mode)
+  if (!options.network && !game.team_mode)
     local_player->effective_action = summarized_player_action;
 
-  if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
+  if (tape.recording &&
+      setup.team_mode &&
+      setup.input_on_focus &&
+      game.centered_player_nr != -1)
   {
     for (i = 0; i < MAX_PLAYERS; i++)
       stored_player[i].effective_action =
@@ -10459,19 +12503,105 @@ void GameActions()
   {
     tape_action[i] = stored_player[i].effective_action;
 
+#if 1
+    /* (this may happen in the RND game engine if a player was not present on
+       the playfield on level start, but appeared later from a custom element */
+    if (tape.recording &&
+       setup.team_mode &&
+       tape_action[i] &&
+       !tape.player_participates[i])
+      tape.player_participates[i] = TRUE;
+#else
     /* (this can only happen in the R'n'D game engine) */
     if (tape.recording && tape_action[i] && !tape.player_participates[i])
       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
+#endif
   }
 
   /* only record actions from input devices, but not programmed actions */
   if (tape.recording)
     TapeRecordAction(tape_action);
 
+#if USE_NEW_PLAYER_ASSIGNMENTS
+#if 1
+  if (game.team_mode)
+#endif
+  {
+    byte mapped_action[MAX_PLAYERS];
+
+#if DEBUG_PLAYER_ACTIONS
+    printf(":::");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %d, ", stored_player[i].effective_action);
+#endif
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      mapped_action[i] = stored_player[map_player_action[i]].effective_action;
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      stored_player[i].effective_action = mapped_action[i];
+
+#if DEBUG_PLAYER_ACTIONS
+    printf(" =>");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %d, ", stored_player[i].effective_action);
+    printf("\n");
+#endif
+  }
+#if DEBUG_PLAYER_ACTIONS
+  else
+  {
+    printf(":::");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %d, ", stored_player[i].effective_action);
+    printf("\n");
+  }
+#endif
+#endif
+
+#if 0
+  printf("::: summarized_player_action == %d\n",
+        local_player->effective_action);
+#endif
+
+
+
+
+#if 0
+#if DEBUG_INIT_PLAYER
+    if (options.debug)
+    {
+      printf("Player status (final):\n");
+
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+
+       printf("- player %d: present == %d, connected == %d, active == %d",
+              i + 1,
+              player->present,
+              player->connected,
+              player->active);
+
+       if (local_player == player)
+         printf(" (local player)");
+
+       printf("\n");
+      }
+    }
+#endif
+#endif
+
+
+
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     GameActions_EM_Main();
   }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    GameActions_SP_Main();
+  }
   else
   {
     GameActions_RND();
@@ -10494,6 +12624,22 @@ void GameActions_EM_Main()
   AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
 }
 
+void GameActions_SP_Main()
+{
+  byte effective_action[MAX_PLAYERS];
+  boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+  int i;
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    effective_action[i] = stored_player[i].effective_action;
+
+  GameActions_SP(effective_action, warp_mode);
+
+  CheckLevelTime();
+
+  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
+}
+
 void GameActions_RND()
 {
   int magic_wall_x = 0, magic_wall_y = 0;
@@ -10634,7 +12780,7 @@ void GameActions_RND()
       if (MovDelay[x][y] <= 0)
       {
        RemoveField(x, y);
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        TestIfElementTouchesCustomElement(x, y);        /* for empty space */
       }
@@ -10668,7 +12814,7 @@ void GameActions_RND()
     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
     {
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
 
 #if DEBUG
@@ -10803,7 +12949,7 @@ void GameActions_RND()
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       DrawTwinkleOnField(x, y);
+       TEST_DrawTwinkleOnField(x, y);
     }
     else if (IS_MOVING(x, y))
       ContinueMoving(x, y);
@@ -10938,7 +13084,7 @@ void GameActions_RND()
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       DrawTwinkleOnField(x, y);
+       TEST_DrawTwinkleOnField(x, y);
     }
     else if ((element == EL_ACID ||
              element == EL_EXIT_OPEN ||
@@ -11042,7 +13188,7 @@ void GameActions_RND()
   /* new experimental amoeba growth stuff */
   if (!(FrameCounter % 8))
   {
-    static unsigned long random = 1684108901;
+    static unsigned int random = 1684108901;
 
     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
     {
@@ -11112,6 +13258,7 @@ void GameActions_RND()
     if (game.magic_wall_time_left > 0)
     {
       game.magic_wall_time_left--;
+
       if (!game.magic_wall_time_left)
       {
        SCAN_PLAYFIELD(x, y)
@@ -11122,19 +13269,19 @@ void GameActions_RND()
              element == EL_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
          else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
                   element == EL_BD_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
          else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
                   element == EL_DC_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
        }
 
@@ -11188,6 +13335,36 @@ void GameActions_RND()
     }
   }
 
+#if USE_DELAYED_GFX_REDRAW
+  SCAN_PLAYFIELD(x, y)
+  {
+#if 1
+    if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
+#else
+    if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
+       GfxRedraw[x][y] != GFX_REDRAW_NONE)
+#endif
+    {
+      /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
+        !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
+       DrawLevelField(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
+       DrawLevelFieldCrumbled(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
+       DrawLevelFieldCrumbledNeighbours(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
+       DrawTwinkleOnField(x, y);
+    }
+
+    GfxRedraw[x][y] = GFX_REDRAW_NONE;
+  }
+#endif
+
   CheckLevelTime();
 
   DrawAllPlayers();
@@ -11195,9 +13372,9 @@ void GameActions_RND()
 
   if (options.debug)                   /* calculate frames per second */
   {
-    static unsigned long fps_counter = 0;
+    static unsigned int fps_counter = 0;
     static int fps_frames = 0;
-    unsigned long fps_delay_ms = Counter() - fps_counter;
+    unsigned int fps_delay_ms = Counter() - fps_counter;
 
     fps_frames++;
 
@@ -11272,12 +13449,13 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
-#if 1
+#if 0
+  /* (directly solved in BlitBitmap() now) */
   static Bitmap *bitmap_db_field2 = NULL;
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
   int x, y;
 #else
-  int i, x, y;
+  int x, y;
 #endif
 
 #if 0
@@ -11287,7 +13465,8 @@ void ScrollLevel(int dx, int dy)
     return;
 #endif
 
-#if 1
+#if 0
+  /* (directly solved in BlitBitmap() now) */
   if (bitmap_db_field2 == NULL)
     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
 
@@ -11310,7 +13489,7 @@ void ScrollLevel(int dx, int dy)
 
 #else
 
-#if 1
+#if 0
   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
   int xsize = (BX2 - BX1 + 1);
   int ysize = (BY2 - BY1 + 1);
@@ -11331,8 +13510,29 @@ void ScrollLevel(int dx, int dy)
 
 #else
 
+#if NEW_TILESIZE
+#if NEW_SCROLL
+  int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
+#else
+  int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
+#endif
+#else
+#if NEW_SCROLL
+  int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
+#else
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
+#endif
+#endif
 
+#if NEW_TILESIZE
+  BlitBitmap(drawto_field, drawto_field,
+            FX + TILEX_VAR * (dx == -1) - softscroll_offset,
+            FY + TILEY_VAR * (dy == -1) - softscroll_offset,
+            SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX_VAR * (dx == 1) - softscroll_offset,
+            FY + TILEY_VAR * (dy == 1) - softscroll_offset);
+#else
   BlitBitmap(drawto_field, drawto_field,
             FX + TILEX * (dx == -1) - softscroll_offset,
             FY + TILEY * (dy == -1) - softscroll_offset,
@@ -11341,6 +13541,8 @@ void ScrollLevel(int dx, int dy)
             FX + TILEX * (dx == 1) - softscroll_offset,
             FY + TILEY * (dy == 1) - softscroll_offset);
 #endif
+
+#endif
 #endif
 
   if (dx != 0)
@@ -11603,7 +13805,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     int original_move_delay_value = player->move_delay_value;
 
 #if DEBUG
-    printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
+    printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
           tape.counter);
 #endif
 
@@ -11661,7 +13863,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)))
     {
@@ -11745,7 +13947,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     else if (old_jx == jx && old_jy != jy)
       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
 
-    DrawLevelField(jx, jy);    /* for "crumbled sand" */
+    TEST_DrawLevelField(jx, jy);       /* for "crumbled sand" */
 
     player->last_move_dir = player->MovDir;
     player->is_moving = TRUE;
@@ -11890,8 +14092,14 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
     if (Feld[jx][jy] == EL_EXIT_OPEN ||
        Feld[jx][jy] == EL_EM_EXIT_OPEN ||
+#if 1
+       Feld[jx][jy] == EL_EM_EXIT_OPENING ||
+#endif
        Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
        Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
+#if 1
+       Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
+#endif
        Feld[jx][jy] == EL_SP_EXIT_OPEN ||
        Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
     {
@@ -11930,8 +14138,13 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                          CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
 
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
+                                       CE_MOVE_OF_X, move_direction);
+#else
       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
                                        CE_MOVE_OF_X, move_direction);
+#endif
     }
 
     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
@@ -11961,14 +14174,29 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        if (TimeLeft <= 10 && setup.time_limit)
          PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
+#if 1
+       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
            KillPlayer(&stored_player[i]);
       }
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+#if 1
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
+      {
+       game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
+
+       DisplayGameControlValues();
+      }
+#else
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
+#endif
     }
 
     if (tape.single_step && tape.recording && !tape.pausing &&
@@ -11979,7 +14207,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
 void ScrollScreen(struct PlayerInfo *player, int mode)
 {
-  static unsigned long screen_frame_counter = 0;
+  static unsigned int screen_frame_counter = 0;
 
   if (mode == SCROLL_INIT)
   {
@@ -12043,7 +14271,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     if (!IN_LEV_FIELD(xx, yy))
       continue;
 
-    if (IS_PLAYER(x, y))
+    if (IS_PLAYER(x, y))               /* player found at center element */
     {
       struct PlayerInfo *player = PLAYERINFO(x, y);
 
@@ -12061,8 +14289,21 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, border_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(x, y)->initial_element;
+
+       CheckElementChangeBySide(xx, yy, border_element, player_element,
+                                CE_TOUCHING_X, border_side);
+      }
+#endif
     }
-    else if (IS_PLAYER(xx, yy))
+    else if (IS_PLAYER(xx, yy))                /* player found at border element */
     {
       struct PlayerInfo *player = PLAYERINFO(xx, yy);
 
@@ -12077,6 +14318,20 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       CheckTriggeredElementChangeByPlayer(x, y, center_element,
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, center_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+       CheckElementChangeBySide(x, y, center_element, player_element,
+                                CE_TOUCHING_X, center_side);
+      }
+#endif
+
       break;
     }
   }
@@ -12149,10 +14404,14 @@ void TestIfElementTouchesCustomElement(int x, int y)
     /* check for change of border element */
     CheckElementChangeBySide(xx, yy, border_element, center_element,
                             CE_TOUCHING_X, center_side);
+
+    /* (center element cannot be player, so we dont have to check this here) */
   }
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
   {
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
     int border_side = trigger_sides[i][1];
     int border_element = border_element_old[i];
 
@@ -12164,6 +14423,20 @@ void TestIfElementTouchesCustomElement(int x, int y)
       change_center_element =
        CheckElementChangeBySide(x, y, center_element, border_element,
                                 CE_TOUCHING_X, border_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+    if (IS_PLAYER(xx, yy))
+    {
+      /* use player element that is initially defined in the level playfield,
+        not the player element that corresponds to the runtime player number
+        (example: a level that contains EL_PLAYER_3 as the only player would
+        incorrectly give EL_PLAYER_1 for "player->element_nr") */
+      int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+      CheckElementChangeBySide(x, y, center_element, player_element,
+                              CE_TOUCHING_X, border_side);
+    }
+#endif
   }
 }
 
@@ -12261,11 +14534,25 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
       CheckElementChangeBySide(x, y, hitting_element, touched_element,
                               CE_HITTING_X, touched_side);
 
-      CheckElementChangeBySide(hitx, hity, touched_element,
-                              hitting_element, CE_HIT_BY_X, hitting_side);
+      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+                              CE_HIT_BY_X, hitting_side);
 
       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
                               CE_HIT_BY_SOMETHING, opposite_direction);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      if (IS_PLAYER(hitx, hity))
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(hitx, hity)->initial_element;
+
+       CheckElementChangeBySide(x, y, hitting_element, player_element,
+                                CE_HITTING_X, touched_side);
+      }
+#endif
     }
   }
 
@@ -12438,6 +14725,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
     test_x = bad_x + test_xy[i][0];
     test_y = bad_y + test_xy[i][1];
+
     if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
@@ -12446,36 +14734,95 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
     test_element = Feld[test_x][test_y];
 
-    /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
-       2nd case: DONT_TOUCH style bad thing does not move away from good thing
-    */
-    if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
-       (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
+    /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
+       2nd case: DONT_TOUCH style bad thing does not move away from good thing
+    */
+    if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
+       (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
+    {
+      /* good thing is player or penguin that does not move away */
+      if (IS_PLAYER(test_x, test_y))
+      {
+       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
+
+       if (bad_element == EL_ROBOT && player->is_moving)
+         continue;     /* robot does not kill player if he is moving */
+
+       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
+       {
+         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
+           continue;           /* center and border element do not touch */
+       }
+
+       kill_x = test_x;
+       kill_y = test_y;
+
+       break;
+      }
+      else if (test_element == EL_PENGUIN)
+      {
+       kill_x = test_x;
+       kill_y = test_y;
+
+       break;
+      }
+    }
+  }
+
+  if (kill_x != -1 || kill_y != -1)
+  {
+    if (IS_PLAYER(kill_x, kill_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
+
+      if (player->shield_deadly_time_left > 0 &&
+         !IS_INDESTRUCTIBLE(bad_element))
+       Bang(bad_x, bad_y);
+      else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
+       KillPlayer(player);
+    }
+    else
+      Bang(kill_x, kill_y);
+  }
+}
+
+void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
+{
+  int bad_element = Feld[bad_x][bad_y];
+  int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
+  int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
+  int test_x = bad_x + dx, test_y = bad_y + dy;
+  int test_move_dir, test_element;
+  int kill_x = -1, kill_y = -1;
+
+  if (!IN_LEV_FIELD(test_x, test_y))
+    return;
+
+  test_move_dir =
+    (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
+
+  test_element = Feld[test_x][test_y];
+
+  if (test_move_dir != bad_move_dir)
+  {
+    /* good thing can be player or penguin that does not move away */
+    if (IS_PLAYER(test_x, test_y))
     {
-      /* good thing is player or penguin that does not move away */
-      if (IS_PLAYER(test_x, test_y))
-      {
-       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
-
-       if (bad_element == EL_ROBOT && player->is_moving)
-         continue;     /* robot does not kill player if he is moving */
+      struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
 
-       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
-       {
-         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
-           continue;           /* center and border element do not touch */
-       }
+      /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
+        player as being hit when he is moving towards the bad thing, because
+        the "get hit by" condition would be lost after the player stops) */
+      if (player->MovPos != 0 && player->MovDir == bad_move_dir)
+       return;         /* player moves away from bad thing */
 
-       kill_x = test_x;
-       kill_y = test_y;
-       break;
-      }
-      else if (test_element == EL_PENGUIN)
-      {
-       kill_x = test_x;
-       kill_y = test_y;
-       break;
-      }
+      kill_x = test_x;
+      kill_y = test_y;
+    }
+    else if (test_element == EL_PENGUIN)
+    {
+      kill_x = test_x;
+      kill_y = test_y;
     }
   }
 
@@ -12567,6 +14914,11 @@ void KillPlayer(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+#if 0
+  printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
   /* the following code was introduced to prevent an infinite loop when calling
      -> Bang()
      -> CheckTriggeredElementChangeExt()
@@ -12591,8 +14943,31 @@ void KillPlayer(struct PlayerInfo *player)
   player->shield_normal_time_left = 0;
   player->shield_deadly_time_left = 0;
 
+#if 0
+  printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
   Bang(jx, jy);
+
+#if 0
+  printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
+#if USE_PLAYER_REANIMATION
+#if 1
+  if (player->reanimated)      /* killed player may have been reanimated */
+    player->killed = player->reanimated = FALSE;
+  else
+    BuryPlayer(player);
+#else
+  if (player->killed)          /* player may have been reanimated */
+    BuryPlayer(player);
+#endif
+#else
   BuryPlayer(player);
+#endif
 }
 
 static void KillPlayerUnlessEnemyProtected(int x, int y)
@@ -12633,7 +15008,7 @@ void RemovePlayer(struct PlayerInfo *player)
     StorePlayer[jx][jy] = 0;
 
   if (player->is_moving)
-    DrawLevelField(player->last_jx, player->last_jy);
+    TEST_DrawLevelField(player->last_jx, player->last_jy);
 
   for (i = 0; i < MAX_PLAYERS; i++)
     if (stored_player[i].active)
@@ -12704,9 +15079,9 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player,
   =============================================================================
 */
 
-int DigField(struct PlayerInfo *player,
-            int oldx, int oldy, int x, int y,
-            int real_dx, int real_dy, int mode)
+static int DigField(struct PlayerInfo *player,
+                   int oldx, int oldy, int x, int y,
+                   int real_dx, int real_dy, int mode)
 {
   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
   boolean player_was_pushing = player->is_pushing;
@@ -12861,8 +15236,14 @@ int DigField(struct PlayerInfo *player,
     }
     else if (element == EL_EXIT_OPEN ||
             element == EL_EM_EXIT_OPEN ||
+#if 1
+            element == EL_EM_EXIT_OPENING ||
+#endif
             element == EL_STEEL_EXIT_OPEN ||
             element == EL_EM_STEEL_EXIT_OPEN ||
+#if 1
+            element == EL_EM_STEEL_EXIT_OPENING ||
+#endif
             element == EL_SP_EXIT_OPEN ||
             element == EL_SP_EXIT_OPENING)
     {
@@ -13017,7 +15398,14 @@ int DigField(struct PlayerInfo *player,
     else if (element == EL_EXTRA_TIME && level.time > 0)
     {
       TimeLeft += level.extra_time;
+
+#if 1
+      game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Time(TimeLeft);
+#endif
     }
     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
     {
@@ -13096,7 +15484,13 @@ int DigField(struct PlayerInfo *player,
       if (local_player->gems_still_needed < 0)
        local_player->gems_still_needed = 0;
 
+#if 1
+      game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Emeralds(local_player->gems_still_needed);
+#endif
     }
 
     RaiseScoreElement(element);
@@ -13172,8 +15566,10 @@ int DigField(struct PlayerInfo *player,
 
     if (!(IN_LEV_FIELD(nextx, nexty) &&
          (IS_FREE(nextx, nexty) ||
-          (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
-           IS_SB_ELEMENT(element)))))
+          (IS_SB_ELEMENT(element) &&
+           Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
+          (IS_CUSTOM_ELEMENT(element) &&
+           CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
       return MP_NO_ACTION;
 
     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
@@ -13193,6 +15589,13 @@ int DigField(struct PlayerInfo *player,
       return MP_NO_ACTION;
     }
 
+    if (IS_CUSTOM_ELEMENT(element) &&
+       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
+    {
+      if (!DigFieldByCE(nextx, nexty, element))
+       return MP_NO_ACTION;
+    }
+
     if (IS_SB_ELEMENT(element))
     {
       if (element == EL_SOKOBAN_FIELD_FULL)
@@ -13218,8 +15621,13 @@ int DigField(struct PlayerInfo *player,
        PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
                                    ACTION_FILLING);
 
+#if 1
+      if (local_player->sokobanfields_still_needed == 0 &&
+         (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
+#else
       if (local_player->sokobanfields_still_needed == 0 &&
          game.emulation == EMU_SOKOBAN)
+#endif
       {
        PlayerWins(player);
 
@@ -13276,7 +15684,9 @@ int DigField(struct PlayerInfo *player,
       ZX = x;
       ZY = y;
 
-      DrawLevelField(x, y);
+      game.robot_wheel_active = TRUE;
+
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_SP_TERMINAL)
     {
@@ -13331,7 +15741,7 @@ int DigField(struct PlayerInfo *player,
       local_player->lights_still_needed--;
 
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_TIME_ORB_FULL)
     {
@@ -13340,11 +15750,19 @@ int DigField(struct PlayerInfo *player,
       if (level.time > 0 || level.use_time_orb_bug)
       {
        TimeLeft += level.time_orb_time;
+       game.no_time_limit = FALSE;
+
+#if 1
+       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
       }
 
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
             element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
@@ -13426,7 +15844,66 @@ int DigField(struct PlayerInfo *player,
   return MP_MOVING;
 }
 
-boolean SnapField(struct PlayerInfo *player, int dx, int dy)
+static boolean DigFieldByCE(int x, int y, int digging_element)
+{
+  int element = Feld[x][y];
+
+  if (!IS_FREE(x, y))
+  {
+    int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
+                 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
+                 ACTION_BREAKING);
+
+    /* no element can dig solid indestructible elements */
+    if (IS_INDESTRUCTIBLE(element) &&
+       !IS_DIGGABLE(element) &&
+       !IS_COLLECTIBLE(element))
+      return FALSE;
+
+    if (AmoebaNr[x][y] &&
+       (element == EL_AMOEBA_FULL ||
+        element == EL_BD_AMOEBA ||
+        element == EL_AMOEBA_GROWING))
+    {
+      AmoebaCnt[AmoebaNr[x][y]]--;
+      AmoebaCnt2[AmoebaNr[x][y]]--;
+    }
+
+    if (IS_MOVING(x, y))
+      RemoveMovingField(x, y);
+    else
+    {
+      RemoveField(x, y);
+      TEST_DrawLevelField(x, y);
+    }
+
+    /* if digged element was about to explode, prevent the explosion */
+    ExplodeField[x][y] = EX_TYPE_NONE;
+
+    PlayLevelSoundAction(x, y, action);
+  }
+
+  Store[x][y] = EL_EMPTY;
+
+#if 1
+  /* this makes it possible to leave the removed element again */
+  if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
+    Store[x][y] = element;
+#else
+  if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
+  {
+    int move_leave_element = element_info[digging_element].move_leave_element;
+
+    /* this makes it possible to leave the removed element again */
+    Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
+                  element : move_leave_element);
+  }
+#endif
+
+  return TRUE;
+}
+
+static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 {
   int jx = player->jx, jy = player->jy;
   int x = jx + dx, y = jy + dy;
@@ -13499,19 +15976,22 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   }
 
   if (player->MovPos != 0)     /* prevent graphic bugs in versions < 2.2.0 */
-    DrawLevelField(player->last_jx, player->last_jy);
+    TEST_DrawLevelField(player->last_jx, player->last_jy);
 
-  DrawLevelField(x, y);
+  TEST_DrawLevelField(x, y);
 
   return TRUE;
 }
 
-boolean DropElement(struct PlayerInfo *player)
+static boolean DropElement(struct PlayerInfo *player)
 {
   int old_element, new_element;
   int dropx = player->jx, dropy = player->jy;
   int drop_direction = player->MovDir;
   int drop_side = drop_direction;
+#if 1
+  int drop_element = get_next_dropped_element(player);
+#else
   int drop_element = (player->inventory_size > 0 ?
                      player->inventory_element[player->inventory_size - 1] :
                      player->inventory_infinite_element != EL_UNDEFINED ?
@@ -13519,6 +15999,7 @@ boolean DropElement(struct PlayerInfo *player)
                      player->dynabombs_left > 0 ?
                      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
                      EL_UNDEFINED);
+#endif
 
   player->is_dropping_pressed = TRUE;
 
@@ -13624,14 +16105,19 @@ boolean DropElement(struct PlayerInfo *player)
   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
   {
-    int move_direction, nextx, nexty;
+#if 0
+    int move_direction;
+    int nextx, nexty;
+#endif
 
     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
       MovDir[dropx][dropy] = drop_direction;
 
+#if 0
     move_direction = MovDir[dropx][dropy];
     nextx = dropx + GET_DX_FROM_DIR(move_direction);
     nexty = dropy + GET_DY_FROM_DIR(move_direction);
+#endif
 
     ChangeCount[dropx][dropy] = 0;     /* allow at least one more change */
 
@@ -13933,36 +16419,32 @@ void PlayLevelSound_EM(int xx, int yy, 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)
+void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
 {
-  /* EMC game engine and RND game engine have separate score counters */
-  int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
-               &level.native_em_level->lev->score : &local_player->score);
+  int element = map_element_SP_to_RND(element_sp);
+  int action = map_action_SP_to_RND(action_sp);
+  int offset = (setup.sp_show_border_elements ? 0 : 1);
+  int x = xx - offset;
+  int y = yy - offset;
 
-  *score += value;
+#if 0
+  printf("::: %d -> %d\n", element_sp, action_sp);
+#endif
 
-  DrawGameValue_Score(*score);
+  PlayLevelSoundElementAction(x, y, element, action);
 }
-#endif
 
 void RaiseScore(int value)
 {
   local_player->score += value;
 
+#if 1
+  game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+
+  DisplayGameControlValues();
+#else
   DrawGameValue_Score(local_player->score);
+#endif
 }
 
 void RaiseScoreElement(int element)
@@ -14049,6 +16531,12 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
 {
   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
   {
+#if 1
+    /* closing door required in case of envelope style request dialogs */
+    if (!skip_request)
+      CloseDoor(DOOR_CLOSE_1);
+#endif
+
 #if defined(NETWORK_AVALIABLE)
     if (options.network)
       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
@@ -14057,13 +16545,31 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
     {
       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;
 
@@ -14089,7 +16595,7 @@ void RequestQuitGame(boolean ask_if_really_quit)
   boolean skip_request = AllPlayersGone || quick_quit;
 
   RequestQuitGameExt(skip_request, quick_quit,
-                    "Do you really want to quit the game ?");
+                    "Do you really want to quit the game?");
 }
 
 
@@ -14097,7 +16603,7 @@ void RequestQuitGame(boolean ask_if_really_quit)
 /* random generator functions                                                */
 /* ------------------------------------------------------------------------- */
 
-unsigned int InitEngineRandom_RND(long seed)
+unsigned int InitEngineRandom_RND(int seed)
 {
   game.num_random_calls = 0;
 
@@ -14132,8 +16638,6 @@ unsigned int RND(int max)
 /* game engine snapshot handling functions                                   */
 /* ------------------------------------------------------------------------- */
 
-#define ARGS_ADDRESS_AND_SIZEOF(x)             (&(x)), (sizeof(x))
-
 struct EngineSnapshotInfo
 {
   /* runtime values for custom element collect score */
@@ -14143,32 +16647,14 @@ struct EngineSnapshotInfo
   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;
+  int belt_graphic[4][NUM_BELT_PARTS];
+  int belt_anim_mode[4][NUM_BELT_PARTS];
 };
 
 static struct EngineSnapshotInfo engine_snapshot_rnd;
-static ListNode *engine_snapshot_list = NULL;
 static char *snapshot_level_identifier = NULL;
 static int snapshot_level_nr = -1;
 
-void FreeEngineSnapshot()
-{
-  while (engine_snapshot_list != NULL)
-    deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
-                      checked_free);
-
-  setString(&snapshot_level_identifier, NULL);
-  snapshot_level_nr = -1;
-}
-
 static void SaveEngineSnapshotValues_RND()
 {
   static int belt_base_active_element[4] =
@@ -14202,15 +16688,15 @@ static void SaveEngineSnapshotValues_RND()
       int graphic = el2img(element);
       int anim_mode = graphic_info[graphic].anim_mode;
 
-      engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
-      engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
+      engine_snapshot_rnd.belt_graphic[i][j] = graphic;
+      engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
     }
   }
 }
 
 static void LoadEngineSnapshotValues_RND()
 {
-  unsigned long num_random_calls = game.num_random_calls;
+  unsigned int num_random_calls = game.num_random_calls;
   int i, j;
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -14231,8 +16717,8 @@ static void LoadEngineSnapshotValues_RND()
   {
     for (j = 0; j < NUM_BELT_PARTS; j++)
     {
-      int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
-      int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
+      int graphic = engine_snapshot_rnd.belt_graphic[i][j];
+      int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
 
       graphic_info[graphic].anim_mode = anim_mode;
     }
@@ -14254,36 +16740,54 @@ static void LoadEngineSnapshotValues_RND()
   }
 }
 
-static void SaveEngineSnapshotBuffer(void *buffer, int size)
+void FreeEngineSnapshot()
 {
-  struct EngineSnapshotNodeInfo *bi =
-    checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
+  FreeEngineSnapshotBuffers();
 
-  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);
+  setString(&snapshot_level_identifier, NULL);
+  snapshot_level_nr = -1;
 }
 
 void SaveEngineSnapshot()
 {
-  FreeEngineSnapshot();                /* free previous snapshot, if needed */
-
-  if (level_editor_test_game)  /* do not save snapshots from editor */
+  /* do not save snapshots from editor */
+  if (level_editor_test_game)
     return;
 
+  /* free previous snapshot buffers, if needed */
+  FreeEngineSnapshotBuffers();
+
+#if 1
+  /* copy some special values to a structure better suited for the snapshot */
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    SaveEngineSnapshotValues_RND();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    SaveEngineSnapshotValues_EM();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    SaveEngineSnapshotValues_SP();
+
+  /* save values stored in special snapshot structure */
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+#else
   /* copy some special values to a structure better suited for the snapshot */
 
   SaveEngineSnapshotValues_RND();
   SaveEngineSnapshotValues_EM();
+  SaveEngineSnapshotValues_SP();
 
   /* save values stored in special snapshot structure */
 
   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+#endif
 
   /* save further RND engine values */
 
@@ -14357,7 +16861,7 @@ void SaveEngineSnapshot()
   snapshot_level_nr = level_nr;
 
 #if 0
-  ListNode *node = engine_snapshot_list;
+  ListNode *node = engine_snapshot_list_rnd;
   int num_bytes = 0;
 
   while (node != NULL)
@@ -14371,29 +16875,48 @@ void SaveEngineSnapshot()
 #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;
+  /* restore generically stored snapshot buffers */
 
-  while (node != NULL)
-  {
-    LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
-
-    node = node->next;
-  }
+  LoadEngineSnapshotBuffers();
 
   /* restore special values from snapshot structure */
 
+#if 1
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    LoadEngineSnapshotValues_RND();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    LoadEngineSnapshotValues_EM();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    LoadEngineSnapshotValues_SP();
+#else
   LoadEngineSnapshotValues_RND();
   LoadEngineSnapshotValues_EM();
+  LoadEngineSnapshotValues_SP();
+#endif
+
+#if 0
+  printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
+#endif
+
+#if 0
+  // needed if tile size was different when saving and loading engine snapshot
+  if (local_player->present)
+  {
+    scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
+               local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+               local_player->jx - MIDPOSX);
+
+    scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+               local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+               local_player->jy - MIDPOSY);
+  }
+#endif
+
+#if 0
+  printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
+#endif
 }
 
 boolean CheckEngineSnapshot()
@@ -14405,57 +16928,45 @@ boolean CheckEngineSnapshot()
 
 /* ---------- new game button stuff ---------------------------------------- */
 
-/* graphic position values for game buttons */
-#define GAME_BUTTON_XSIZE      30
-#define GAME_BUTTON_YSIZE      30
-#define GAME_BUTTON_XPOS       5
-#define GAME_BUTTON_YPOS       215
-#define SOUND_BUTTON_XPOS      5
-#define SOUND_BUTTON_YPOS      (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
-
-#define GAME_BUTTON_STOP_XPOS  (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PLAY_XPOS  (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_MUSIC_XPOS        (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_LOOPS_XPOS        (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
-
 static struct
 {
-  int x, y;
+  int graphic;
+  struct Rect *pos;
   int gadget_id;
   char *infotext;
 } gamebutton_info[NUM_GAME_BUTTONS] =
 {
   {
-    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_STOP,
-    "stop game"
+    IMG_GAME_BUTTON_GFX_STOP,          &game.button.stop,
+    GAME_CTRL_ID_STOP,                 "stop game"
+  },
+  {
+    IMG_GAME_BUTTON_GFX_PAUSE,         &game.button.pause,
+    GAME_CTRL_ID_PAUSE,                        "pause game"
   },
   {
-    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PAUSE,
-    "pause game"
+    IMG_GAME_BUTTON_GFX_PLAY,          &game.button.play,
+    GAME_CTRL_ID_PLAY,                 "play game"
   },
   {
-    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PLAY,
-    "play game"
+    IMG_GAME_BUTTON_GFX_SOUND_MUSIC,   &game.button.sound_music,
+    SOUND_CTRL_ID_MUSIC,               "background music on/off"
   },
   {
-    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_MUSIC,
-    "background music on/off"
+    IMG_GAME_BUTTON_GFX_SOUND_LOOPS,   &game.button.sound_loops,
+    SOUND_CTRL_ID_LOOPS,               "sound loops on/off"
   },
   {
-    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_LOOPS,
-    "sound loops on/off"
+    IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,  &game.button.sound_simple,
+    SOUND_CTRL_ID_SIMPLE,              "normal sounds on/off"
   },
   {
-    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_SIMPLE,
-    "normal sounds on/off"
+    IMG_GAME_BUTTON_GFX_SAVE,          &game.button.save,
+    GAME_CTRL_ID_SAVE,                 "save game"
+  },
+  {
+    IMG_GAME_BUTTON_GFX_LOAD,          &game.button.load,
+    GAME_CTRL_ID_LOAD,                 "load game"
   }
 };
 
@@ -14465,29 +16976,40 @@ void CreateGameButtons()
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
   {
-    Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+    struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
+    struct Rect *pos = gamebutton_info[i].pos;
     struct GadgetInfo *gi;
     int button_type;
     boolean checked;
-    unsigned long event_mask;
-    int gd_xoffset, gd_yoffset;
-    int gd_x1, gd_x2, gd_y1, gd_y2;
+    unsigned int event_mask;
+    int base_x = (tape.show_game_buttons ? VX : DX);
+    int base_y = (tape.show_game_buttons ? VY : DY);
+    int gd_x   = gfx->src_x;
+    int gd_y   = gfx->src_y;
+    int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
+    int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
+    int gd_xa  = gfx->src_x + gfx->active_xoffset;
+    int gd_ya  = gfx->src_y + gfx->active_yoffset;
+    int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
+    int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
     int id = i;
 
-    gd_xoffset = gamebutton_info[i].x;
-    gd_yoffset = gamebutton_info[i].y;
-    gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
-    gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+    if (gfx->bitmap == NULL)
+    {
+      game_gadget[id] = NULL;
+
+      continue;
+    }
 
     if (id == GAME_CTRL_ID_STOP ||
        id == GAME_CTRL_ID_PAUSE ||
-       id == GAME_CTRL_ID_PLAY)
+       id == GAME_CTRL_ID_PLAY ||
+       id == GAME_CTRL_ID_SAVE ||
+       id == GAME_CTRL_ID_LOAD)
     {
       button_type = GD_TYPE_NORMAL_BUTTON;
       checked = FALSE;
       event_mask = GD_EVENT_RELEASED;
-      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
-      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
     }
     else
     {
@@ -14497,23 +17019,22 @@ void CreateGameButtons()
         (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
         (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
       event_mask = GD_EVENT_PRESSED;
-      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
-      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
     }
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_INFO_TEXT, gamebutton_info[i].infotext,
-                     GDI_X, DX + gd_xoffset,
-                     GDI_Y, DY + gd_yoffset,
-                     GDI_WIDTH, GAME_BUTTON_XSIZE,
-                     GDI_HEIGHT, GAME_BUTTON_YSIZE,
+                     GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
+                     GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
+                     GDI_WIDTH, gfx->width,
+                     GDI_HEIGHT, gfx->height,
                      GDI_TYPE, button_type,
                      GDI_STATE, GD_BUTTON_UNPRESSED,
                      GDI_CHECKED, checked,
-                     GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
-                     GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
-                     GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
-                     GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+                     GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
+                     GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
+                     GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
+                     GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
+                     GDI_DIRECT_DRAW, FALSE,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleGameButtons,
                      GDI_END);
@@ -14533,7 +17054,7 @@ void FreeGameButtons()
     FreeGadget(game_gadget[i]);
 }
 
-static void MapGameButtons()
+void MapGameButtons()
 {
   int i;
 
@@ -14549,24 +17070,41 @@ void UnmapGameButtons()
     UnmapGadget(game_gadget[i]);
 }
 
-static void HandleGameButtons(struct GadgetInfo *gi)
+void RedrawGameButtons()
+{
+  int i;
+
+  for (i = 0; i < NUM_GAME_BUTTONS; i++)
+    RedrawGadget(game_gadget[i]);
+
+  // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
+  redraw_mask &= ~REDRAW_ALL;
+}
+
+static void HandleGameButtonsExt(int id)
 {
-  int id = gi->custom_id;
+  boolean handle_game_buttons =
+    (game_status == GAME_MODE_PLAYING ||
+     (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
 
-  if (game_status != GAME_MODE_PLAYING)
+  if (!handle_game_buttons)
     return;
 
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
+      if (game_status == GAME_MODE_MAIN)
+       break;
+
       if (tape.playing)
        TapeStop();
       else
        RequestQuitGame(TRUE);
+
       break;
 
     case GAME_CTRL_ID_PAUSE:
-      if (options.network)
+      if (options.network && game_status == GAME_MODE_PLAYING)
       {
 #if defined(NETWORK_AVALIABLE)
        if (tape.pausing)
@@ -14580,7 +17118,11 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       break;
 
     case GAME_CTRL_ID_PLAY:
-      if (tape.pausing)
+      if (game_status == GAME_MODE_MAIN)
+      {
+        StartGameActions(options.network, setup.autorecord, level.random_seed);
+      }
+      else if (tape.pausing)
       {
 #if defined(NETWORK_AVALIABLE)
        if (options.network)
@@ -14598,6 +17140,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       if (setup.sound_music)
       { 
        setup.sound_music = FALSE;
+
        FadeMusic();
       }
       else if (audio.music_available)
@@ -14616,6 +17159,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       else if (audio.loops_available)
       {
        setup.sound = setup.sound_loops = TRUE;
+
        SetAudioMode(setup.sound);
       }
       break;
@@ -14626,11 +17170,44 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       else if (audio.sound_available)
       {
        setup.sound = setup.sound_simple = TRUE;
+
        SetAudioMode(setup.sound);
       }
       break;
 
+    case GAME_CTRL_ID_SAVE:
+      TapeQuickSave();
+      break;
+
+    case GAME_CTRL_ID_LOAD:
+      TapeQuickLoad();
+      break;
+
     default:
       break;
   }
 }
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+  HandleGameButtonsExt(gi->custom_id);
+}
+
+void HandleSoundButtonKeys(Key key)
+{
+#if 1
+  if (key == setup.shortcut.sound_simple)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
+  else if (key == setup.shortcut.sound_loops)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
+  else if (key == setup.shortcut.sound_music)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
+#else
+  if (key == setup.shortcut.sound_simple)
+    HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
+  else if (key == setup.shortcut.sound_loops)
+    HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
+  else if (key == setup.shortcut.sound_music)
+    HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
+#endif
+}