rnd-20070331-1-src
[rocksndiamonds.git] / src / game.c
index c5e016f9f39e71801481f1d2ee3661e9327f6382..d580212fd8e725218ce9cb3cdc68ceff033ecbbc 100644 (file)
@@ -59,6 +59,8 @@
 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF          * 1)
 #define USE_FIX_IMPACT_COLLISION       (USE_NEW_STUFF          * 1)
 
+#define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
+
 
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define EX_TYPE_DYNA           (1 << 4)
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
-#define        PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
+#define PANEL_OFF()            (local_player->LevelSolved_PanelOff)
+#define        PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
+#define PANEL_XPOS(p)          (DX + ALIGNED_TEXT_XPOS(p))
+#define PANEL_YPOS(p)          (DY + ALIGNED_TEXT_YPOS(p))
 
 /* special positions in the game control window (relative to control window) */
-#define XX_LEVEL1              (game.panel.level.x)
-#define XX_LEVEL2              (game.panel.level.x - 1)
-#define YY_LEVEL               (game.panel.level.y)
-#define XX_EMERALDS            (game.panel.gems.x)
-#define YY_EMERALDS            (game.panel.gems.y)
-#define XX_DYNAMITE            (game.panel.inventory.x)
-#define YY_DYNAMITE            (game.panel.inventory.y)
-#define XX_KEYS                        (game.panel.keys.x)
-#define YY_KEYS                        (game.panel.keys.y)
-#define XX_SCORE               (game.panel.score.x)
-#define YY_SCORE               (game.panel.score.y)
-#define XX_TIME1               (game.panel.time.x)
-#define XX_TIME2               (game.panel.time.x + 1)
-#define YY_TIME                        (game.panel.time.y)
+#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 DY_SCORE               (DY + YY_SCORE)
 #define DX_TIME1               (DX + XX_TIME1)
 #define DX_TIME2               (DX + XX_TIME2)
+#define DX_TIME                        (DX + XX_TIME)
 #define DY_TIME                        (DY + YY_TIME)
 
+#if 1
+/* game panel display and control definitions */
+
+#define GAME_CONTROL_LEVEL_NUMBER              0
+#define GAME_CONTROL_GEMS                      1
+#define GAME_CONTROL_INVENTORY                 2
+#define GAME_CONTROL_KEY_1                     3
+#define GAME_CONTROL_KEY_2                     4
+#define GAME_CONTROL_KEY_3                     5
+#define GAME_CONTROL_KEY_4                     6
+#define GAME_CONTROL_KEY_5                     7
+#define GAME_CONTROL_KEY_6                     8
+#define GAME_CONTROL_KEY_7                     9
+#define GAME_CONTROL_KEY_8                     10
+#define GAME_CONTROL_KEY_WHITE                 11
+#define GAME_CONTROL_KEY_WHITE_COUNT           12
+#define GAME_CONTROL_SCORE                     13
+#define GAME_CONTROL_TIME                      14
+#define GAME_CONTROL_TIME_HH                   15
+#define GAME_CONTROL_TIME_MM                   16
+#define GAME_CONTROL_TIME_SS                   17
+#define GAME_CONTROL_DROP_NEXT_1               18
+#define GAME_CONTROL_DROP_NEXT_2               19
+#define GAME_CONTROL_DROP_NEXT_3               20
+#define GAME_CONTROL_DROP_NEXT_4               21
+#define GAME_CONTROL_DROP_NEXT_5               22
+#define GAME_CONTROL_DROP_NEXT_6               23
+#define GAME_CONTROL_DROP_NEXT_7               24
+#define GAME_CONTROL_DROP_NEXT_8               25
+#define GAME_CONTROL_SHIELD_NORMAL             26
+#define GAME_CONTROL_SHIELD_NORMAL_TIME                27
+#define GAME_CONTROL_SHIELD_DEADLY             28
+#define GAME_CONTROL_SHIELD_DEADLY_TIME                29
+#define GAME_CONTROL_EXIT                      30
+#define GAME_CONTROL_EM_EXIT                   31
+#define GAME_CONTROL_SP_EXIT                   32
+#define GAME_CONTROL_STEEL_EXIT                        33
+#define GAME_CONTROL_EM_STEEL_EXIT             34
+#define GAME_CONTROL_EMC_MAGIC_BALL            35
+#define GAME_CONTROL_EMC_MAGIC_BALL_SWITCH     36
+#define GAME_CONTROL_LIGHT_SWITCH              37
+#define GAME_CONTROL_LIGHT_SWITCH_TIME         38
+#define GAME_CONTROL_TIMEGATE_SWITCH           39
+#define GAME_CONTROL_TIMEGATE_SWITCH_TIME      40
+#define GAME_CONTROL_SWITCHGATE_SWITCH         41
+#define GAME_CONTROL_EMC_LENSES                        42
+#define GAME_CONTROL_EMC_LENSES_TIME           43
+#define GAME_CONTROL_EMC_MAGNIFIER             44
+#define GAME_CONTROL_EMC_MAGNIFIER_TIME                45
+#define GAME_CONTROL_BALLOON_SWITCH            46
+#define GAME_CONTROL_DYNABOMB_NUMBER           47
+#define GAME_CONTROL_DYNABOMB_SIZE             48
+#define GAME_CONTROL_DYNABOMB_POWER            49
+#define GAME_CONTROL_PENGUINS                  50
+#define GAME_CONTROL_SOKOBAN_OBJECTS           51
+#define GAME_CONTROL_SOKOBAN_FIELDS            52
+#define GAME_CONTROL_ROBOT_WHEEL               53
+#define GAME_CONTROL_CONVEYOR_BELT_1           54
+#define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH    55
+#define GAME_CONTROL_CONVEYOR_BELT_2           56
+#define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH    57
+#define GAME_CONTROL_CONVEYOR_BELT_3           58
+#define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH    59
+#define GAME_CONTROL_CONVEYOR_BELT_4           60
+#define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH    61
+#define GAME_CONTROL_MAGIC_WALL                        62
+#define GAME_CONTROL_MAGIC_WALL_TIME           63
+#define GAME_CONTROL_BD_MAGIC_WALL             64
+#define GAME_CONTROL_DC_MAGIC_WALL             65
+#define GAME_CONTROL_PLAYER_NAME               66
+#define GAME_CONTROL_LEVEL_NAME                        67
+#define GAME_CONTROL_LEVEL_AUTHOR              68
+
+#define NUM_GAME_CONTROLS                      69
+
+int game_control_value[NUM_GAME_CONTROLS];
+int last_game_control_value[NUM_GAME_CONTROLS];
+
+struct GameControlInfo
+{
+  int nr;
+
+  struct TextPosInfo *pos;
+  int type;
+};
+
+static struct GameControlInfo game_controls[] =
+{
+  {
+    GAME_CONTROL_LEVEL_NUMBER,
+    &game.panel.level_number,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_GEMS,
+    &game.panel.gems,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_INVENTORY,
+    &game.panel.inventory,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_KEY_1,
+    &game.panel.key[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_2,
+    &game.panel.key[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_3,
+    &game.panel.key[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_4,
+    &game.panel.key[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_5,
+    &game.panel.key[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_6,
+    &game.panel.key[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_7,
+    &game.panel.key[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_8,
+    &game.panel.key[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_WHITE,
+    &game.panel.key_white,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_KEY_WHITE_COUNT,
+    &game.panel.key_white_count,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SCORE,
+    &game.panel.score,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIME,
+    &game.panel.time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIME_HH,
+    &game.panel.time_hh,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIME_MM,
+    &game.panel.time_mm,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIME_SS,
+    &game.panel.time_ss,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_1,
+    &game.panel.drop_next_1,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_2,
+    &game.panel.drop_next_2,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_3,
+    &game.panel.drop_next_3,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_4,
+    &game.panel.drop_next_4,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_5,
+    &game.panel.drop_next_5,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_6,
+    &game.panel.drop_next_6,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_7,
+    &game.panel.drop_next_7,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DROP_NEXT_8,
+    &game.panel.drop_next_8,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_SHIELD_NORMAL,
+    &game.panel.shield_normal,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_SHIELD_NORMAL_TIME,
+    &game.panel.shield_normal_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SHIELD_DEADLY,
+    &game.panel.shield_deadly,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_SHIELD_DEADLY_TIME,
+    &game.panel.shield_deadly_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_EXIT,
+    &game.panel.exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EM_EXIT,
+    &game.panel.em_exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_SP_EXIT,
+    &game.panel.sp_exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_STEEL_EXIT,
+    &game.panel.steel_exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EM_STEEL_EXIT,
+    &game.panel.em_steel_exit,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_MAGIC_BALL,
+    &game.panel.emc_magic_ball,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_MAGIC_BALL_SWITCH,
+    &game.panel.emc_magic_ball_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_LIGHT_SWITCH,
+    &game.panel.light_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_LIGHT_SWITCH_TIME,
+    &game.panel.light_switch_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_TIMEGATE_SWITCH,
+    &game.panel.timegate_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_TIMEGATE_SWITCH_TIME,
+    &game.panel.timegate_switch_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SWITCHGATE_SWITCH,
+    &game.panel.switchgate_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_LENSES,
+    &game.panel.emc_lenses,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_LENSES_TIME,
+    &game.panel.emc_lenses_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_EMC_MAGNIFIER,
+    &game.panel.emc_magnifier,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_EMC_MAGNIFIER_TIME,
+    &game.panel.emc_magnifier_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_BALLOON_SWITCH,
+    &game.panel.balloon_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DYNABOMB_NUMBER,
+    &game.panel.dynabomb_number,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_DYNABOMB_SIZE,
+    &game.panel.dynabomb_size,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_DYNABOMB_POWER,
+    &game.panel.dynabomb_power,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_PENGUINS,
+    &game.panel.penguins,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SOKOBAN_OBJECTS,
+    &game.panel.sokoban_objects,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_SOKOBAN_FIELDS,
+    &game.panel.sokoban_fields,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_ROBOT_WHEEL,
+    &game.panel.robot_wheel,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_1,
+    &game.panel.conveyor_belt_1,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
+    &game.panel.conveyor_belt_1_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_2,
+    &game.panel.conveyor_belt_2,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
+    &game.panel.conveyor_belt_2_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_3,
+    &game.panel.conveyor_belt_3,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
+    &game.panel.conveyor_belt_3_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_4,
+    &game.panel.conveyor_belt_4,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
+    &game.panel.conveyor_belt_4_switch,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_MAGIC_WALL,
+    &game.panel.magic_wall,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_MAGIC_WALL_TIME,
+    &game.panel.magic_wall_time,
+    TYPE_INTEGER,
+  },
+  {
+    GAME_CONTROL_BD_MAGIC_WALL,
+    &game.panel.bd_magic_wall,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_DC_MAGIC_WALL,
+    &game.panel.dc_magic_wall,
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_CONTROL_PLAYER_NAME,
+    &game.panel.player_name,
+    TYPE_STRING,
+  },
+  {
+    GAME_CONTROL_LEVEL_NAME,
+    &game.panel.level_name,
+    TYPE_STRING,
+  },
+  {
+    GAME_CONTROL_LEVEL_AUTHOR,
+    &game.panel.level_author,
+    TYPE_STRING,
+  },
+
+  {
+    -1,
+    NULL,
+    -1,
+  }
+};
+#endif
+
+
 /* values for delayed check of falling and moving elements and for collision */
 #define CHECK_DELAY_MOVING     3
 #define CHECK_DELAY_FALLING    CHECK_DELAY_MOVING
 
 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                               \
        ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
+                                                Feld[x][y] == EL_EM_EXIT_OPEN || \
                                                 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
+                                                Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
                                                 IS_FOOD_PENGUIN(Feld[x][y])))
 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                        \
        ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
@@ -381,8 +835,8 @@ static int getInvisibleFromInvisibleActiveElement(int);
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
 /* for detection of endless loops, caused by custom element programming */
-/* (using "MAX_PLAYFIELD_WIDTH" here is just a rough approximation...) */
-#define MAX_ELEMENT_CHANGE_RECURSION_DEPTH     (MAX_PLAYFIELD_WIDTH)
+/* (using maximal playfield width x 10 is just a rough approximation) */
+#define MAX_ELEMENT_CHANGE_RECURSION_DEPTH     (MAX_PLAYFIELD_WIDTH * 10)
 
 #define RECURSION_LOOP_DETECTION_START(e, rc)                          \
 {                                                                      \
@@ -491,6 +945,46 @@ static struct ChangingElementInfo change_delay_list[] =
     NULL,
     NULL
   },
+  {
+    EL_EM_EXIT_OPENING,
+    EL_EM_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EM_EXIT_CLOSING,
+#if 1
+    EL_EMPTY,
+#else
+    EL_EM_EXIT_CLOSED,
+#endif
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EM_STEEL_EXIT_OPENING,
+    EL_EM_STEEL_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EM_STEEL_EXIT_CLOSING,
+#if 1
+    EL_STEELWALL,
+#else
+    EL_EM_STEEL_EXIT_CLOSED,
+#endif
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
   {
     EL_SP_EXIT_OPENING,
     EL_SP_EXIT_OPEN,
@@ -612,6 +1106,14 @@ static struct ChangingElementInfo change_delay_list[] =
     RunTimegateWheel,
     NULL
   },
+  {
+    EL_DC_TIMEGATE_SWITCH_ACTIVE,
+    EL_DC_TIMEGATE_SWITCH,
+    0,
+    InitTimegateWheel,
+    RunTimegateWheel,
+    NULL
+  },
   {
     EL_EMC_MAGIC_BALL_ACTIVE,
     EL_EMC_MAGIC_BALL_ACTIVE,
@@ -684,10 +1186,14 @@ move_stepsize_list[] =
   { EL_AMOEBA_DROPPING,                2 },
   { EL_QUICKSAND_FILLING,      1 },
   { EL_QUICKSAND_EMPTYING,     1 },
+  { EL_QUICKSAND_FAST_FILLING, 2 },
+  { EL_QUICKSAND_FAST_EMPTYING,        2 },
   { EL_MAGIC_WALL_FILLING,     2 },
-  { EL_BD_MAGIC_WALL_FILLING,  2 },
   { EL_MAGIC_WALL_EMPTYING,    2 },
+  { EL_BD_MAGIC_WALL_FILLING,  2 },
   { EL_BD_MAGIC_WALL_EMPTYING, 2 },
+  { EL_DC_MAGIC_WALL_FILLING,  2 },
+  { EL_DC_MAGIC_WALL_EMPTYING, 2 },
 
   { EL_UNDEFINED,              0 },
 };
@@ -774,7 +1280,7 @@ static int playfield_scan_delta_y = 1;
                                     (y) += playfield_scan_delta_y)     \
                                for ((x) = playfield_scan_start_x;      \
                                     (x) >= 0 && (x) <= lev_fieldx - 1; \
-                                    (x) += playfield_scan_delta_x)     \
+                                    (x) += playfield_scan_delta_x)
 
 #ifdef DEBUG
 void DEBUG_SetMaximumDynamite()
@@ -853,6 +1359,8 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
 
 void GetPlayerConfig()
 {
+  GameFrameDelay = setup.game_frame_delay;
+
   if (!audio.sound_available)
     setup.sound_simple = FALSE;
 
@@ -871,58 +1379,7 @@ void GetPlayerConfig()
   InitJoysticks();
 }
 
-static int getBeltNrFromBeltElement(int element)
-{
-  return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
-         element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
-         element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
-}
-
-static int getBeltNrFromBeltActiveElement(int element)
-{
-  return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
-         element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
-         element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
-}
-
-static int getBeltNrFromBeltSwitchElement(int element)
-{
-  return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
-         element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
-         element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
-}
-
-static int getBeltDirNrFromBeltSwitchElement(int element)
-{
-  static int belt_base_element[4] =
-  {
-    EL_CONVEYOR_BELT_1_SWITCH_LEFT,
-    EL_CONVEYOR_BELT_2_SWITCH_LEFT,
-    EL_CONVEYOR_BELT_3_SWITCH_LEFT,
-    EL_CONVEYOR_BELT_4_SWITCH_LEFT
-  };
-
-  int belt_nr = getBeltNrFromBeltSwitchElement(element);
-  int belt_dir_nr = element - belt_base_element[belt_nr];
-
-  return (belt_dir_nr % 3);
-}
-
-static int getBeltDirFromBeltSwitchElement(int element)
-{
-  static int belt_move_dir[3] =
-  {
-    MV_LEFT,
-    MV_NONE,
-    MV_RIGHT
-  };
-
-  int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
-
-  return belt_move_dir[belt_dir_nr];
-}
-
-static int get_element_from_group_element(int element)
+int GetElementFromGroupElement(int element)
 {
   if (IS_GROUP_ELEMENT(element))
   {
@@ -1177,6 +1634,11 @@ static void InitField(int x, int y, boolean init_game)
       if (init_game)
        Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
       break;
+
+    case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
+      if (init_game)
+       Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
+      break;
 #endif
 
     case EL_LIGHT_SWITCH_ACTIVE:
@@ -1215,108 +1677,646 @@ static void InitField(int x, int y, boolean init_game)
       }
       else if (IS_GROUP_ELEMENT(element))
       {
-       Feld[x][y] = get_element_from_group_element(element);
+       Feld[x][y] = GetElementFromGroupElement(element);
+
+       InitField(x, y, init_game);
+      }
+
+      break;
+  }
+
+  if (!init_game)
+    CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
+}
+
+static inline void InitField_WithBug1(int x, int y, boolean init_game)
+{
+  InitField(x, y, init_game);
+
+  /* not needed to call InitMovDir() -- already done by InitField()! */
+  if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
+      CAN_MOVE(Feld[x][y]))
+    InitMovDir(x, y);
+}
+
+static inline void InitField_WithBug2(int x, int y, boolean init_game)
+{
+  int old_element = Feld[x][y];
+
+  InitField(x, y, init_game);
+
+  /* not needed to call InitMovDir() -- already done by InitField()! */
+  if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
+      CAN_MOVE(old_element) &&
+      (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
+    InitMovDir(x, y);
+
+  /* this case is in fact a combination of not less than three bugs:
+     first, it calls InitMovDir() for elements that can move, although this is
+     already done by InitField(); then, it checks the element that was at this
+     field _before_ the call to InitField() (which can change it); lastly, it
+     was not called for "mole with direction" elements, which were treated as
+     "cannot move" due to (fixed) wrong element initialization in "src/init.c"
+  */
+}
+
+#if 1
+
+void InitGameControlValues()
+{
+  int i;
+
+  for (i = 0; i < NUM_GAME_CONTROLS; i++)
+    game_control_value[i] = last_game_control_value[i] = -1;
+
+  for (i = 0; game_controls[i].nr != -1; i++)
+  {
+    int nr = game_controls[i].nr;
+    int type = game_controls[i].type;
+    struct TextPosInfo *pos = game_controls[i].pos;
+
+    game_control_value[nr] = last_game_control_value[nr] = -1;
+
+    /* determine panel value width for later calculation of alignment */
+    if (type == TYPE_INTEGER || type == TYPE_STRING)
+      pos->width = pos->chars * getFontWidth(pos->font);
+    else if (type == TYPE_ELEMENT)
+      pos->width = MINI_TILESIZE;
+  }
+}
+
+void UpdateGameControlValues()
+{
+  int i, j;
+
+  game_control_value[GAME_CONTROL_LEVEL_NUMBER] = level_nr;
+  game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
+
+  game_control_value[GAME_CONTROL_INVENTORY] = 0;
+  for (i = 0; i < MAX_NUM_KEYS; i++)
+    game_control_value[GAME_CONTROL_KEY_1 + i] = 0;
+  game_control_value[GAME_CONTROL_KEY_WHITE] = 0;
+  game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] = 0;
+
+  if (game.centered_player_nr == -1)
+  {
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      for (j = 0; j < MAX_NUM_KEYS; j++)
+       if (stored_player[i].key[j])
+         game_control_value[GAME_CONTROL_KEY_1 + j] = 1;
+
+      game_control_value[GAME_CONTROL_INVENTORY] +=
+       stored_player[i].inventory_size;
+
+      if (stored_player[i].num_white_keys > 0)
+       game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
+
+      game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
+       stored_player[i].num_white_keys;
+    }
+  }
+  else
+  {
+    int player_nr = game.centered_player_nr;
+
+    for (i = 0; i < MAX_NUM_KEYS; i++)
+      if (stored_player[player_nr].key[i])
+       game_control_value[GAME_CONTROL_KEY_1 + i] = 1;
+
+    game_control_value[GAME_CONTROL_INVENTORY] +=
+      stored_player[player_nr].inventory_size;
+
+    if (stored_player[player_nr].num_white_keys > 0)
+      game_control_value[GAME_CONTROL_KEY_WHITE] = 1;
+
+    game_control_value[GAME_CONTROL_KEY_WHITE_COUNT] +=
+      stored_player[player_nr].num_white_keys;
+  }
+
+  game_control_value[GAME_CONTROL_SCORE] = (local_player->LevelSolved ?
+                                           local_player->score_final :
+                                           local_player->score);
+
+  game_control_value[GAME_CONTROL_TIME] = (level.time == 0 ?
+                                          TimePlayed :
+                                          TimeLeft);
+
+  game_control_value[GAME_CONTROL_TIME_HH] = TapeTime / 3600;
+  game_control_value[GAME_CONTROL_TIME_MM] = (TapeTime / 60) % 60;
+  game_control_value[GAME_CONTROL_TIME_SS] = TapeTime % 60;
+
+  for (i = 0; i < 8; i++)
+    game_control_value[GAME_CONTROL_DROP_NEXT_1 + i] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_SHIELD_NORMAL] =
+    (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
+     EL_EMPTY);
+  game_control_value[GAME_CONTROL_SHIELD_NORMAL_TIME] =
+    local_player->shield_normal_time_left;
+  game_control_value[GAME_CONTROL_SHIELD_DEADLY] =
+    (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
+     EL_EMPTY);
+  game_control_value[GAME_CONTROL_SHIELD_DEADLY_TIME] =
+    local_player->shield_deadly_time_left;
+
+  if (local_player->gems_still_needed > 0 ||
+      local_player->sokobanfields_still_needed > 0 ||
+      local_player->lights_still_needed > 0)
+  {
+    game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_CLOSED;
+    game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_CLOSED;
+    game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_CLOSED;
+    game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_CLOSED;
+    game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_CLOSED;
+  }
+  else
+  {
+    game_control_value[GAME_CONTROL_EXIT]          = EL_EXIT_OPEN;
+    game_control_value[GAME_CONTROL_EM_EXIT]       = EL_EM_EXIT_OPEN;
+    game_control_value[GAME_CONTROL_SP_EXIT]       = EL_SP_EXIT_OPEN;
+    game_control_value[GAME_CONTROL_STEEL_EXIT]    = EL_STEEL_EXIT_OPEN;
+    game_control_value[GAME_CONTROL_EM_STEEL_EXIT] = EL_EM_STEEL_EXIT_OPEN;
+  }
+
+  game_control_value[GAME_CONTROL_EMC_MAGIC_BALL] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_EMC_MAGIC_BALL_SWITCH] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_LIGHT_SWITCH] =
+    (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
+  game_control_value[GAME_CONTROL_LIGHT_SWITCH_TIME] = game.light_time_left;
+
+  game_control_value[GAME_CONTROL_TIMEGATE_SWITCH] =
+    (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
+  game_control_value[GAME_CONTROL_TIMEGATE_SWITCH_TIME] =
+    game.timegate_time_left;
+
+  game_control_value[GAME_CONTROL_SWITCHGATE_SWITCH] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_EMC_LENSES] =
+    (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
+  game_control_value[GAME_CONTROL_EMC_LENSES_TIME] = game.lenses_time_left;
+
+  game_control_value[GAME_CONTROL_EMC_MAGNIFIER] =
+    (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
+  game_control_value[GAME_CONTROL_EMC_MAGNIFIER_TIME] = game.magnify_time_left;
+
+  game_control_value[GAME_CONTROL_BALLOON_SWITCH] =
+    (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
+     game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
+     game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
+     game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
+     EL_BALLOON_SWITCH_NONE);
+
+  game_control_value[GAME_CONTROL_DYNABOMB_NUMBER] =
+    local_player->dynabomb_count;
+  game_control_value[GAME_CONTROL_DYNABOMB_SIZE] =
+    local_player->dynabomb_size;
+  game_control_value[GAME_CONTROL_DYNABOMB_POWER] =
+    (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
+
+  game_control_value[GAME_CONTROL_PENGUINS] =
+    local_player->friends_still_needed;
+
+  game_control_value[GAME_CONTROL_SOKOBAN_OBJECTS] =
+    local_player->sokobanfields_still_needed;
+  game_control_value[GAME_CONTROL_SOKOBAN_FIELDS] =
+    local_player->sokobanfields_still_needed;
+
+  game_control_value[GAME_CONTROL_ROBOT_WHEEL] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_1] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_1_SWITCH] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_2] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_2_SWITCH] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_3] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_3_SWITCH] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_4] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_CONVEYOR_BELT_4_SWITCH] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_MAGIC_WALL] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_MAGIC_WALL_TIME] =
+    game.magic_wall_time_left;
+  game_control_value[GAME_CONTROL_BD_MAGIC_WALL] = EL_UNDEFINED;
+  game_control_value[GAME_CONTROL_DC_MAGIC_WALL] = EL_UNDEFINED;
+
+  game_control_value[GAME_CONTROL_PLAYER_NAME] = 0;
+  game_control_value[GAME_CONTROL_LEVEL_NAME] = 0;
+  game_control_value[GAME_CONTROL_LEVEL_AUTHOR] = 0;
+}
+
+void DisplayGameControlValues()
+{
+  int i;
+
+  for (i = 0; game_controls[i].nr != -1; i++)
+  {
+    int nr = game_controls[i].nr;
+    int type = game_controls[i].type;
+    struct TextPosInfo *pos = game_controls[i].pos;
+    int value = game_control_value[nr];
+    int last_value = last_game_control_value[nr];
+    int chars = pos->chars;
+    int font = pos->font;
+
+    if (value == last_value)
+      continue;
+
+    last_game_control_value[nr] = value;
+
+#if 0
+    printf("::: value %d changed from %d to %d\n", nr, last_value, value);
+#endif
+
+    if (PANEL_DEACTIVATED(pos))
+      continue;
+
+    if (type == TYPE_INTEGER)
+    {
+      if (nr == GAME_CONTROL_LEVEL_NUMBER || nr == GAME_CONTROL_TIME)
+      {
+       boolean use_dynamic_chars = (pos->chars == -1 ? TRUE : FALSE);
+
+       if (use_dynamic_chars)          /* use dynamic number of chars */
+       {
+         int value_change = (nr == GAME_CONTROL_LEVEL_NUMBER ? 100 : 1000);
+         int chars1 = (nr == GAME_CONTROL_LEVEL_NUMBER ? 2 : 3);
+         int chars2 = chars1 + 1;
+         int font1 = pos->font;
+         int font2 = pos->font_alt;
+
+         chars = (value < value_change ? chars1 : chars2);
+         font  = (value < value_change ? font1  : font2);
+
+         /* clear background if value just changed its size (dynamic chars) */
+         if ((last_value < value_change) != (value < value_change))
+         {
+           int width1 = chars1 * getFontWidth(font1);
+           int width2 = chars2 * getFontWidth(font2);
+           int max_width = MAX(width1, width2);
+           int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
+
+           pos->width = max_width;
+
+           ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                                      max_width, max_height);
+         }
+       }
+
+       pos->width = chars * getFontWidth(font);
+      }
+
+      DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font);
+    }
+    else if (type == TYPE_ELEMENT)
+    {
+      if (nr >= GAME_CONTROL_KEY_1 && nr <= GAME_CONTROL_KEY_8)
+      {
+       int key_nr = nr - GAME_CONTROL_KEY_1;
+       int src_x = DOOR_GFX_PAGEX5 + 18 + (key_nr % STD_NUM_KEYS) * MINI_TILEX;
+       int src_y = DOOR_GFX_PAGEY1 + 123;
+       int dst_x = PANEL_XPOS(pos);
+       int dst_y = PANEL_YPOS(pos);
+       int element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
+                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                      EL_EM_KEY_1 : EL_KEY_1) + key_nr;
+       int graphic = el2edimg(element);
+
+       if (value)
+         DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
+       else
+         BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+                    MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+      }
+      else if (value != EL_UNDEFINED)
+      {
+       int graphic = el2edimg(value);
+       int dst_x = PANEL_XPOS(pos);
+       int dst_y = PANEL_YPOS(pos);
+
+       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
+      }
+    }
+    else if (type == TYPE_STRING)
+    {
+      char *s = (nr == GAME_CONTROL_PLAYER_NAME  ? setup.player_name :
+                nr == GAME_CONTROL_LEVEL_NAME   ? level.name :
+                nr == GAME_CONTROL_LEVEL_AUTHOR ? level.author : NULL);
+
+      if (s != NULL)
+      {
+       char *s_cut = getStringCopyN(s, pos->chars);
+
+       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, pos->font);
+
+       free(s_cut);
+      }
+    }
+
+    redraw_mask |= REDRAW_DOOR_1;
+  }
+}
+
+void DrawGameValue_Emeralds(int value)
+{
+  struct TextPosInfo *pos = &game.panel.gems;
+#if 1
+  int font_nr = pos->font;
+#else
+  int font_nr = FONT_TEXT_2;
+#endif
+  int font_width = getFontWidth(font_nr);
+  int chars = pos->chars;
+
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-       InitField(x, y, init_game);
-      }
+  if (PANEL_DEACTIVATED(pos))
+    return;
 
-      break;
-  }
+  pos->width = chars * font_width;
 
-  if (!init_game)
-    CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
-static inline void InitField_WithBug1(int x, int y, boolean init_game)
+void DrawGameValue_Dynamite(int value)
 {
-  InitField(x, y, init_game);
+  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;
 
-  /* not needed to call InitMovDir() -- already done by InitField()! */
-  if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
-      CAN_MOVE(Feld[x][y]))
-    InitMovDir(x, y);
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
+    return;
+
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
-static inline void InitField_WithBug2(int x, int y, boolean init_game)
+void DrawGameValue_Score(int value)
 {
-  int old_element = Feld[x][y];
+  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;
 
-  InitField(x, y, init_game);
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
 
-  /* not needed to call InitMovDir() -- already done by InitField()! */
-  if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
-      CAN_MOVE(old_element) &&
-      (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
-    InitMovDir(x, y);
+  if (PANEL_DEACTIVATED(pos))
+    return;
 
-  /* this case is in fact a combination of not less than three bugs:
-     first, it calls InitMovDir() for elements that can move, although this is
-     already done by InitField(); then, it checks the element that was at this
-     field _before_ the call to InitField() (which can change it); lastly, it
-     was not called for "mole with direction" elements, which were treated as
-     "cannot move" due to (fixed) wrong element initialization in "src/init.c"
-  */
+  pos->width = chars * font_width;
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
-inline void DrawGameValue_Emeralds(int value)
+void DrawGameValue_Time(int value)
 {
-  int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+  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(game.panel.gems))
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
+  if (use_dynamic_chars)               /* use dynamic number of chars */
+  {
+    chars   = (value < 1000 ? chars1   : chars2);
+    font_nr = (value < 1000 ? font1_nr : font2_nr);
+  }
+
+  /* clear background if value just changed its size (dynamic chars only) */
+  if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
+  {
+    int width1 = chars1 * getFontWidth(font1_nr);
+    int width2 = chars2 * getFontWidth(font2_nr);
+    int max_width = MAX(width1, width2);
+    int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
+
+    pos->width = max_width;
+
+    ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                              max_width, max_height);
+  }
+
+  pos->width = chars * getFontWidth(font_nr);
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+
+  last_value = value;
 }
 
-inline void DrawGameValue_Dynamite(int value)
+void DrawGameValue_Level(int value)
 {
-  int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+  struct TextPosInfo *pos = &game.panel.level_number;
+  int chars1 = 2;
+  int chars2 = 3;
+  int chars = pos->chars;
+#if 1
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+#else
+  int font1_nr = FONT_TEXT_2;
+  int font2_nr = FONT_TEXT_1;
+#endif
+  int font_nr = font1_nr;
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
 
-  if (PANEL_DEACTIVATED(game.panel.inventory))
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
+  if (use_dynamic_chars)               /* use dynamic number of chars */
+  {
+    chars   = (level_nr < 100 ? chars1   : chars2);
+    font_nr = (level_nr < 100 ? font1_nr : font2_nr);
+  }
+
+  pos->width = chars * getFontWidth(font_nr);
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
-inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
 {
+#if 0
+  struct TextPosInfo *pos = &game.panel.keys;
+#endif
+#if 0
   int base_key_graphic = EL_KEY_1;
+#endif
   int i;
 
-  if (PANEL_DEACTIVATED(game.panel.keys))
+#if 1
+  return;      /* !!! USE NEW STUFF !!! */
+#endif
+
+#if 0
+  if (PANEL_DEACTIVATED(pos))
     return;
+#endif
 
+#if 0
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
     base_key_graphic = EL_EM_KEY_1;
+#endif
 
+#if 0
+  pos->width = 4 * MINI_TILEX;
+#endif
+
+#if 1
+  for (i = 0; i < MAX_NUM_KEYS; i++)
+#else
   /* currently only 4 of 8 possible keys are displayed */
   for (i = 0; i < STD_NUM_KEYS; i++)
+#endif
   {
-    int x = XX_KEYS + i * MINI_TILEX;
-    int y = YY_KEYS;
+#if 1
+    struct TextPosInfo *pos = &game.panel.key[i];
+#endif
+    int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
+    int src_y = DOOR_GFX_PAGEY1 + 123;
+#if 1
+    int dst_x = PANEL_XPOS(pos);
+    int dst_y = PANEL_YPOS(pos);
+#else
+    int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
+    int dst_y = PANEL_YPOS(pos);
+#endif
+
+#if 1
+    int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
+                  level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
+                  EL_KEY_1) + i;
+    int graphic = el2edimg(element);
+#endif
+
+#if 1
+    if (PANEL_DEACTIVATED(pos))
+      continue;
+#endif
+
+#if 0
+    /* masked blit with tiles from half-size scaled bitmap does not work yet
+       (no mask bitmap created for these sizes after loading and scaling) --
+       solution: load without creating mask, scale, then create final mask */
+
+    BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+              MINI_TILEX, MINI_TILEY, dst_x, dst_y);
 
     if (key[i])
-      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
+    {
+#if 0
+      int graphic = el2edimg(base_key_graphic + i);
+#endif
+      Bitmap *src_bitmap;
+      int src_x, src_y;
+
+      getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
+
+      SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+                   dst_x - src_x, dst_y - src_y);
+      BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
+                      dst_x, dst_y);
+    }
+#else
+#if 1
+    if (key[i])
+      DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
     else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-                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);
+#else
+    if (key[i])
+      DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
+    else
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+#endif
+#endif
   }
 }
 
-inline void DrawGameValue_Score(int value)
+#else
+
+void DrawGameValue_Emeralds(int value)
+{
+  int font_nr = FONT_TEXT_2;
+  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
+
+  if (PANEL_DEACTIVATED(game.panel.gems))
+    return;
+
+  DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
+}
+
+void DrawGameValue_Dynamite(int value)
+{
+  int font_nr = FONT_TEXT_2;
+  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
+
+  if (PANEL_DEACTIVATED(game.panel.inventory))
+    return;
+
+  DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
+}
+
+void DrawGameValue_Score(int value)
 {
-  int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
+  int font_nr = FONT_TEXT_2;
+  int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
 
   if (PANEL_DEACTIVATED(game.panel.score))
     return;
 
-  DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
+  DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
 }
 
-inline void DrawGameValue_Time(int value)
+void DrawGameValue_Time(int value)
 {
-  int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
-  int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
+  int font1_nr = FONT_TEXT_2;
+#if 1
+  int font2_nr = FONT_TEXT_1;
+#else
+  int font2_nr = FONT_LEVEL_NUMBER;
+#endif
+  int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
+  int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
 
   if (PANEL_DEACTIVATED(game.panel.time))
     return;
@@ -1326,22 +2326,56 @@ inline void DrawGameValue_Time(int value)
     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
 
   if (value < 1000)
-    DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
+    DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
   else
-    DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
+    DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
 }
 
-inline void DrawGameValue_Level(int value)
+void DrawGameValue_Level(int value)
 {
+  int font1_nr = FONT_TEXT_2;
+#if 1
+  int font2_nr = FONT_TEXT_1;
+#else
+  int font2_nr = FONT_LEVEL_NUMBER;
+#endif
+
   if (PANEL_DEACTIVATED(game.panel.level))
     return;
 
   if (level_nr < 100)
-    DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
+    DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
   else
-    DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
+    DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
+}
+
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+{
+  int base_key_graphic = EL_KEY_1;
+  int i;
+
+  if (PANEL_DEACTIVATED(game.panel.keys))
+    return;
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    base_key_graphic = EL_EM_KEY_1;
+
+  /* currently only 4 of 8 possible keys are displayed */
+  for (i = 0; i < STD_NUM_KEYS; i++)
+  {
+    int x = XX_KEYS + i * MINI_TILEX;
+    int y = YY_KEYS;
+
+    if (key[i])
+      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
+    else
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
+  }
 }
 
+#endif
+
 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
                       int key_bits)
 {
@@ -1367,6 +2401,12 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
 }
 
 void DrawGameDoorValues()
+{
+  UpdateGameControlValues();
+  DisplayGameControlValues();
+}
+
+void DrawGameDoorValues_OLD()
 {
   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
   int dynamite_value = 0;
@@ -1835,6 +2875,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)
@@ -1874,12 +2921,15 @@ void InitGame()
   boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
+#if 0
   boolean do_fading = (game_status == GAME_MODE_MAIN);
+#endif
   int i, j, x, y;
 
   game_status = GAME_MODE_PLAYING;
 
   InitGameEngine();
+  InitGameControlValues();
 
   /* don't play tapes over network */
   network_playing = (options.network && !tape.playing);
@@ -1911,6 +2961,8 @@ void InitGame()
     for (j = 0; j < MAX_NUM_KEYS; j++)
       player->key[j] = FALSE;
 
+    player->num_white_keys = 0;
+
     player->dynabomb_count = 0;
     player->dynabomb_size = 1;
     player->dynabombs_left = 0;
@@ -2003,7 +3055,9 @@ void InitGame()
     player->LevelSolved = FALSE;
     player->GameOver = FALSE;
 
+    player->LevelSolved_GameWon = FALSE;
     player->LevelSolved_GameEnd = FALSE;
+    player->LevelSolved_PanelOff = FALSE;
     player->LevelSolved_SaveTape = FALSE;
     player->LevelSolved_SaveScore = FALSE;
   }
@@ -2356,7 +3410,8 @@ void InitGame()
          content = element_info[element].change_page[i].target_element;
          is_player = ELEM_IS_PLAYER(content);
 
-         if (is_player && (found_rating < 3 || element < found_element))
+         if (is_player && (found_rating < 3 ||
+                           (found_rating == 3 && element < found_element)))
          {
            start_x = x;
            start_y = y;
@@ -2373,7 +3428,8 @@ void InitGame()
        content = element_info[element].content.e[xx][yy];
        is_player = ELEM_IS_PLAYER(content);
 
-       if (is_player && (found_rating < 2 || element < found_element))
+       if (is_player && (found_rating < 2 ||
+                         (found_rating == 2 && element < found_element)))
        {
          start_x = x + xx - 1;
          start_y = y + yy - 1;
@@ -2393,7 +3449,8 @@ void InitGame()
 
          is_player = ELEM_IS_PLAYER(content);
 
-         if (is_player && (found_rating < 1 || element < found_element))
+         if (is_player && (found_rating < 1 ||
+                           (found_rating == 1 && element < found_element)))
          {
            start_x = x + xx - 1;
            start_y = y + yy - 1;
@@ -2429,8 +3486,24 @@ void InitGame()
   if (!game.restart_level)
     CloseDoor(DOOR_CLOSE_1);
 
+#if 1
+  if (level_editor_test_game)
+    FadeSkipNextFadeIn();
+  else
+    FadeSetStartItem();
+#else
+  if (level_editor_test_game)
+    fading = fading_none;
+  else
+    fading = menu.destination;
+#endif
+
+#if 1
+  FadeOut(REDRAW_FIELD);
+#else
   if (do_fading)
     FadeOut(REDRAW_FIELD);
+#endif
 
   /* !!! FIX THIS (START) !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
@@ -2457,10 +3530,14 @@ void InitGame()
   }
   /* !!! FIX THIS (END) !!! */
 
+#if 1
+  FadeIn(REDRAW_FIELD);
+#else
   if (do_fading)
     FadeIn(REDRAW_FIELD);
 
   BackToFront();
+#endif
 
   if (!game.restart_level)
   {
@@ -2735,10 +3812,12 @@ void GameWon()
 {
   static int time, time_final;
   static int score, score_final;
-  static int game_over_delay = 0;
-  int game_over_delay_value = 50;
+  static int game_over_delay_1 = 0;
+  static int game_over_delay_2 = 0;
+  int game_over_delay_value_1 = 50;
+  int game_over_delay_value_2 = 50;
 
-  if (!local_player->LevelSolved_GameEnd)
+  if (!local_player->LevelSolved_GameWon)
   {
     int i;
 
@@ -2746,7 +3825,7 @@ void GameWon()
     if (local_player->MovPos)
       return;
 
-    local_player->LevelSolved_GameEnd = TRUE;
+    local_player->LevelSolved_GameWon = TRUE;
     local_player->LevelSolved_SaveTape = tape.recording;
     local_player->LevelSolved_SaveScore = !tape.playing;
 
@@ -2757,7 +3836,8 @@ void GameWon()
     TapeStop();
 #endif
 
-    game_over_delay = game_over_delay_value;
+    game_over_delay_1 = game_over_delay_value_1;
+    game_over_delay_2 = game_over_delay_value_2;
 
     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
     score = score_final = local_player->score_final;
@@ -2780,8 +3860,15 @@ void GameWon()
       time = time_final;
       score = score_final;
 
+#if 1
+      game_control_value[GAME_CONTROL_TIME] = time;
+      game_control_value[GAME_CONTROL_SCORE] = score;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Time(time);
       DrawGameValue_Score(score);
+#endif
     }
 
     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
@@ -2789,18 +3876,33 @@ void GameWon()
       if (ExitX >= 0 && ExitY >= 0)    /* local player has left the level */
       {
        /* close exit door after last player */
-       if (AllPlayersGone &&
-           (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
-            Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
-            Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN))
+       if ((AllPlayersGone &&
+            (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+             Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
+             Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
+           Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
+           Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
        {
          int element = Feld[ExitX][ExitY];
 
-         Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
-                               element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
-                               EL_STEEL_EXIT_CLOSING);
-
-         PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+#if 0
+         if (element == EL_EM_EXIT_OPEN ||
+             element == EL_EM_STEEL_EXIT_OPEN)
+         {
+           Bang(ExitX, ExitY);
+         }
+         else
+#endif
+         {
+           Feld[ExitX][ExitY] =
+             (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
+              element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
+              element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
+              element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
+              EL_EM_STEEL_EXIT_CLOSING);
+
+           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+         }
        }
 
        /* player disappears */
@@ -2824,9 +3926,9 @@ void GameWon()
     PlaySound(SND_GAME_WINNING);
   }
 
-  if (game_over_delay > 0)
+  if (game_over_delay_1 > 0)
   {
-    game_over_delay--;
+    game_over_delay_1--;
 
     return;
   }
@@ -2840,8 +3942,15 @@ void GameWon()
     time  += time_count_steps * time_count_dir;
     score += time_count_steps * level.score[SC_TIME_BONUS];
 
+#if 1
+    game_control_value[GAME_CONTROL_TIME] = time;
+    game_control_value[GAME_CONTROL_SCORE] = score;
+
+    DisplayGameControlValues();
+#else
     DrawGameValue_Time(time);
     DrawGameValue_Score(score);
+#endif
 
     if (time == time_final)
       StopSound(SND_GAME_LEVELTIME_BONUS);
@@ -2849,7 +3958,22 @@ void GameWon()
       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
     else
       PlaySound(SND_GAME_LEVELTIME_BONUS);
+
+    return;
+  }
+
+  local_player->LevelSolved_PanelOff = TRUE;
+
+  if (game_over_delay_2 > 0)
+  {
+    game_over_delay_2--;
+
+    return;
   }
+
+#if 1
+  GameEnd();
+#endif
 }
 
 void GameEnd()
@@ -2857,6 +3981,8 @@ void GameEnd()
   int hi_pos;
   boolean raise_level = FALSE;
 
+  local_player->LevelSolved_GameEnd = TRUE;
+
   CloseDoor(DOOR_CLOSE_1);
 
   if (local_player->LevelSolved_SaveTape)
@@ -2876,14 +4002,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;
 
@@ -2915,7 +4047,9 @@ void GameEnd()
   }
   else
   {
+#if 1
     FadeOut(REDRAW_FIELD);
+#endif
 
     game_status = GAME_MODE_MAIN;
 
@@ -3083,7 +4217,12 @@ void InitMovingField(int x, int y, int direction)
 
   /* check if element was/is moving or being moved before/after mode change */
 #if 1
+#if 1
+  is_moving_before = (WasJustMoving[x][y] != 0);
+#else
+  /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
   is_moving_before = WasJustMoving[x][y];
+#endif
 #else
   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
 #endif
@@ -3267,8 +4406,10 @@ void RemoveMovingField(int x, int y)
 
   if (element == EL_BLOCKED &&
       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
+       Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
+       Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
     next_element = get_next_element(Feld[oldx][oldy]);
 
@@ -3389,12 +4530,14 @@ 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);
+    int offset = game.scroll_delay_value;
 
     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);
@@ -3429,6 +4572,8 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
     }
     else
     {
+      /* quick relocation (without scrolling), inside visible screen area */
+
       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
          (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
        scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
@@ -3450,6 +4595,49 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
   }
   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);
@@ -3457,6 +4645,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 */
 
@@ -4030,6 +5219,14 @@ void Bang(int x, int y)
       explosion_type = EX_TYPE_DYNA;
       break;
 
+    case EL_DC_LANDMINE:
+#if 0
+    case EL_EM_EXIT_OPEN:
+    case EL_EM_STEEL_EXIT_OPEN:
+#endif
+      explosion_type = EX_TYPE_CENTER;
+      break;
+
     case EL_PENGUIN:
     case EL_LAMP:
     case EL_LAMP_ACTIVE:
@@ -4243,6 +5440,12 @@ static void ToggleSwitchgateSwitch(int x, int y)
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
       DrawLevelField(xx, yy);
     }
+    else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
+            element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+    {
+      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
+      DrawLevelField(xx, yy);
+    }
 #else
     if (element == EL_SWITCHGATE_SWITCH_UP)
     {
@@ -4254,6 +5457,16 @@ static void ToggleSwitchgateSwitch(int x, int y)
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
       DrawLevelField(xx, yy);
     }
+    else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
+    {
+      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
+      DrawLevelField(xx, yy);
+    }
+    else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+    {
+      Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
+      DrawLevelField(xx, yy);
+    }
 #endif
     else if (element == EL_SWITCHGATE_OPEN ||
             element == EL_SWITCHGATE_OPENING)
@@ -4470,7 +5683,7 @@ static void ActivateTimegateSwitch(int x, int y)
        element == EL_TIMEGATE_CLOSING)
     {
       Feld[xx][yy] = EL_TIMEGATE_OPENING;
-      PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
+      PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
     }
 
     /*
@@ -4483,7 +5696,12 @@ static void ActivateTimegateSwitch(int x, int y)
 
   }
 
+#if 1
+  Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
+               EL_DC_TIMEGATE_SWITCH_ACTIVE);
+#else
   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
+#endif
 }
 
 void Impact(int x, int y)
@@ -4518,6 +5736,16 @@ void Impact(int x, int y)
 
       object_hit = TRUE;
     }
+
+    if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
+    {
+      RemoveMovingField(x, y + 1);
+      Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
+      Feld[x][y + 2] = EL_ROCK;
+      DrawLevelField(x, y + 2);
+
+      object_hit = TRUE;
+    }
 #endif
 
     if (object_hit)
@@ -4546,7 +5774,8 @@ void Impact(int x, int y)
     Bang(x, y);
     return;
   }
-  else if (impact && element == EL_PEARL)
+  else if (impact && element == EL_PEARL &&
+          smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
   {
     ResetGfxAnimation(x, y);
 
@@ -4579,26 +5808,33 @@ void Impact(int x, int y)
 
   if (object_hit)              /* check which object was hit */
   {
-    if (CAN_PASS_MAGIC_WALL(element) && 
-       (smashed == EL_MAGIC_WALL ||
-        smashed == EL_BD_MAGIC_WALL))
+    if ((CAN_PASS_MAGIC_WALL(element) && 
+        (smashed == EL_MAGIC_WALL ||
+         smashed == EL_BD_MAGIC_WALL)) ||
+       (CAN_PASS_DC_MAGIC_WALL(element) &&
+        smashed == EL_DC_MAGIC_WALL))
     {
       int xx, yy;
       int activated_magic_wall =
        (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
-        EL_BD_MAGIC_WALL_ACTIVE);
+        smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
+        EL_DC_MAGIC_WALL_ACTIVE);
 
       /* activate magic wall / mill */
       SCAN_PLAYFIELD(xx, yy)
+      {
        if (Feld[xx][yy] == smashed)
          Feld[xx][yy] = activated_magic_wall;
+      }
 
       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
       game.magic_wall_active = TRUE;
 
       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
                            SND_MAGIC_WALL_ACTIVATING :
-                           SND_BD_MAGIC_WALL_ACTIVATING));
+                           smashed == EL_BD_MAGIC_WALL ?
+                           SND_BD_MAGIC_WALL_ACTIVATING :
+                           SND_DC_MAGIC_WALL_ACTIVATING));
     }
 
     if (IS_PLAYER(x, y + 1))
@@ -4678,7 +5914,9 @@ void Impact(int x, int y)
          ToggleBeltSwitch(x, y + 1);
        }
        else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
-                smashed == EL_SWITCHGATE_SWITCH_DOWN)
+                smashed == EL_SWITCHGATE_SWITCH_DOWN ||
+                smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
+                smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
        {
          ToggleSwitchgateSwitch(x, y + 1);
        }
@@ -4711,12 +5949,15 @@ void Impact(int x, int y)
   /* play sound of magic wall / mill */
   if (!last_line &&
       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
-       Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
+       Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
+       Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
   {
     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
+    else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+      PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
 
     return;
   }
@@ -5074,7 +6315,9 @@ inline static void TurnRoundExt(int x, int y)
        int ey = y + xy[i][1];
 
        if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
-                                    Feld[ex][ey] == EL_STEEL_EXIT_OPEN))
+                                    Feld[ex][ey] == EL_EM_EXIT_OPEN ||
+                                    Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
+                                    Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
        {
          attr_x = ex;
          attr_y = ey;
@@ -5631,6 +6874,43 @@ void StartMoving(int x, int y)
        PlayLevelSoundAction(x, y, ACTION_FILLING);
       }
     }
+    else if (element == EL_QUICKSAND_FAST_FULL)
+    {
+      if (IS_FREE(x, y + 1))
+      {
+       InitMovingField(x, y, MV_DOWN);
+       started_moving = TRUE;
+
+       Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
+#if USE_QUICKSAND_BD_ROCK_BUGFIX
+       if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
+         Store[x][y] = EL_ROCK;
+#else
+       Store[x][y] = EL_ROCK;
+#endif
+
+       PlayLevelSoundAction(x, y, ACTION_EMPTYING);
+      }
+      else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+      {
+       if (!MovDelay[x][y])
+         MovDelay[x][y] = TILEY + 1;
+
+       if (MovDelay[x][y])
+       {
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
+       Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+       Store[x][y + 1] = Store[x][y];
+       Store[x][y] = 0;
+
+       PlayLevelSoundAction(x, y, ACTION_FILLING);
+      }
+    }
     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
             Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
     {
@@ -5642,6 +6922,17 @@ void StartMoving(int x, int y)
 
       PlayLevelSoundAction(x, y, ACTION_FILLING);
     }
+    else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
+            Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+    {
+      InitMovingField(x, y, MV_DOWN);
+      started_moving = TRUE;
+
+      Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
+      Store[x][y] = element;
+
+      PlayLevelSoundAction(x, y, ACTION_FILLING);
+    }
     else if (element == EL_MAGIC_WALL_FULL)
     {
       if (IS_FREE(x, y + 1))
@@ -5678,7 +6969,7 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
 
        Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
-       Store[x][y] = EL_CHANGED2(Store[x][y]);
+       Store[x][y] = EL_CHANGED_BD(Store[x][y]);
       }
       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       {
@@ -5694,20 +6985,52 @@ void StartMoving(int x, int y)
 
        Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
        Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
-       Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
+       Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
+       Store[x][y] = 0;
+      }
+    }
+    else if (element == EL_DC_MAGIC_WALL_FULL)
+    {
+      if (IS_FREE(x, y + 1))
+      {
+       InitMovingField(x, y, MV_DOWN);
+       started_moving = TRUE;
+
+       Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
+       Store[x][y] = EL_CHANGED_DC(Store[x][y]);
+      }
+      else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+      {
+       if (!MovDelay[x][y])
+         MovDelay[x][y] = TILEY/4 + 1;
+
+       if (MovDelay[x][y])
+       {
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
+       Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
+       Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
        Store[x][y] = 0;
       }
     }
-    else if (CAN_PASS_MAGIC_WALL(element) &&
-            (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
-             Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
+    else if ((CAN_PASS_MAGIC_WALL(element) &&
+             (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+              Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
+            (CAN_PASS_DC_MAGIC_WALL(element) &&
+             (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
+
     {
       InitMovingField(x, y, MV_DOWN);
       started_moving = TRUE;
 
       Feld[x][y] =
        (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
-        EL_BD_MAGIC_WALL_FILLING);
+        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
+        EL_DC_MAGIC_WALL_FILLING);
       Store[x][y] = element;
     }
     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
@@ -6072,7 +7395,9 @@ void StartMoving(int x, int y)
     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
     {
       if (Feld[newx][newy] == EL_EXIT_OPEN ||
-         Feld[newx][newy] == EL_STEEL_EXIT_OPEN)
+         Feld[newx][newy] == EL_EM_EXIT_OPEN ||
+         Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
+         Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
       {
        RemoveField(x, y);
        DrawLevelField(x, y);
@@ -6490,6 +7815,17 @@ void ContinueMoving(int x, int y)
 
   if (ABS(MovPos[x][y]) < TILEX)
   {
+#if 0
+    int ee = Feld[x][y];
+    int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+    int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
+
+    printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
+          x, y, ABS(MovPos[x][y]),
+          ee, gg, ff,
+          GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
+#endif
+
     DrawLevelField(x, y);
 
     return;    /* element is still moving */
@@ -6521,6 +7857,16 @@ void ContinueMoving(int x, int y)
     Feld[x][y] = get_next_element(element);
     element = Feld[newx][newy] = Store[x][y];
   }
+  else if (element == EL_QUICKSAND_FAST_FILLING)
+  {
+    element = Feld[newx][newy] = get_next_element(element);
+    Store[newx][newy] = Store[x][y];
+  }
+  else if (element == EL_QUICKSAND_FAST_EMPTYING)
+  {
+    Feld[x][y] = get_next_element(element);
+    element = Feld[newx][newy] = Store[x][y];
+  }
   else if (element == EL_MAGIC_WALL_FILLING)
   {
     element = Feld[newx][newy] = get_next_element(element);
@@ -6553,6 +7899,24 @@ void ContinueMoving(int x, int y)
       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
 
+#if USE_NEW_CUSTOM_VALUE
+    InitField(newx, newy, FALSE);
+#endif
+  }
+  else if (element == EL_DC_MAGIC_WALL_FILLING)
+  {
+    element = Feld[newx][newy] = get_next_element(element);
+    if (!game.magic_wall_active)
+      element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
+    Store[newx][newy] = Store[x][y];
+  }
+  else if (element == EL_DC_MAGIC_WALL_EMPTYING)
+  {
+    Feld[x][y] = get_next_element(element);
+    if (!game.magic_wall_active)
+      Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
+    element = Feld[newx][newy] = Store[x][y];
+
 #if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
 #endif
@@ -7056,7 +8420,8 @@ void AmoebeAbleger(int ax, int ay)
 
     if (IS_FREE(x, y) ||
        CAN_GROW_INTO(Feld[x][y]) ||
-       Feld[x][y] == EL_QUICKSAND_EMPTY)
+       Feld[x][y] == EL_QUICKSAND_EMPTY ||
+       Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
     {
       newax = x;
       neway = y;
@@ -7081,7 +8446,8 @@ void AmoebeAbleger(int ax, int ay)
 
       if (IS_FREE(x, y) ||
          CAN_GROW_INTO(Feld[x][y]) ||
-         Feld[x][y] == EL_QUICKSAND_EMPTY)
+         Feld[x][y] == EL_QUICKSAND_EMPTY ||
+         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
       {
        newax = x;
        neway = y;
@@ -7266,7 +8632,7 @@ static void InitTimegateWheel(int x, int y)
 
 static void RunTimegateWheel(int x, int y)
 {
-  PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
+  PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
 }
 
 static void InitMagicBallDelay(int x, int y)
@@ -7333,6 +8699,29 @@ void CheckExit(int x, int y)
   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
 }
 
+void CheckExitEM(int x, int y)
+{
+  if (local_player->gems_still_needed > 0 ||
+      local_player->sokobanfields_still_needed > 0 ||
+      local_player->lights_still_needed > 0)
+  {
+    int element = Feld[x][y];
+    int graphic = el2img(element);
+
+    if (IS_ANIMATED(graphic))
+      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+    return;
+  }
+
+  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+    return;
+
+  Feld[x][y] = EL_EM_EXIT_OPENING;
+
+  PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
+}
+
 void CheckExitSteel(int x, int y)
 {
   if (local_player->gems_still_needed > 0 ||
@@ -7356,6 +8745,29 @@ void CheckExitSteel(int x, int y)
   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
 }
 
+void CheckExitSteelEM(int x, int y)
+{
+  if (local_player->gems_still_needed > 0 ||
+      local_player->sokobanfields_still_needed > 0 ||
+      local_player->lights_still_needed > 0)
+  {
+    int element = Feld[x][y];
+    int graphic = el2img(element);
+
+    if (IS_ANIMATED(graphic))
+      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+    return;
+  }
+
+  if (AllPlayersGone)  /* do not re-open exit door closed after last player */
+    return;
+
+  Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
+
+  PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
+}
+
 void CheckExitSP(int x, int y)
 {
   if (local_player->gems_still_needed > 0)
@@ -7594,6 +9006,108 @@ void MauerAbleger(int ax, int ay)
     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
 }
 
+void MauerAblegerStahl(int ax, int ay)
+{
+  int element = Feld[ax][ay];
+  int graphic = el2img(element);
+  boolean oben_frei = FALSE, unten_frei = FALSE;
+  boolean links_frei = FALSE, rechts_frei = FALSE;
+  boolean oben_massiv = FALSE, unten_massiv = FALSE;
+  boolean links_massiv = FALSE, rechts_massiv = FALSE;
+  boolean new_wall = FALSE;
+
+  if (IS_ANIMATED(graphic))
+    DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
+
+  if (!MovDelay[ax][ay])       /* start building new wall */
+    MovDelay[ax][ay] = 6;
+
+  if (MovDelay[ax][ay])                /* wait some time before building new wall */
+  {
+    MovDelay[ax][ay]--;
+    if (MovDelay[ax][ay])
+      return;
+  }
+
+  if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
+    oben_frei = TRUE;
+  if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
+    unten_frei = TRUE;
+  if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
+    links_frei = TRUE;
+  if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
+    rechts_frei = TRUE;
+
+  if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+      element == EL_EXPANDABLE_STEELWALL_ANY)
+  {
+    if (oben_frei)
+    {
+      Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
+      Store[ax][ay-1] = element;
+      GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
+      if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
+       DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
+                   IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
+      new_wall = TRUE;
+    }
+    if (unten_frei)
+    {
+      Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
+      Store[ax][ay+1] = element;
+      GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
+      if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
+       DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
+                   IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
+      new_wall = TRUE;
+    }
+  }
+
+  if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+      element == EL_EXPANDABLE_STEELWALL_ANY)
+  {
+    if (links_frei)
+    {
+      Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
+      Store[ax-1][ay] = element;
+      GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
+      if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
+       DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
+                   IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
+      new_wall = TRUE;
+    }
+
+    if (rechts_frei)
+    {
+      Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
+      Store[ax+1][ay] = element;
+      GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
+      if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
+       DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
+                   IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
+      new_wall = TRUE;
+    }
+  }
+
+  if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
+    oben_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
+    unten_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
+    links_massiv = TRUE;
+  if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
+    rechts_massiv = TRUE;
+
+  if (((oben_massiv && unten_massiv) ||
+       element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
+      ((links_massiv && rechts_massiv) ||
+       element == EL_EXPANDABLE_STEELWALL_VERTICAL))
+    Feld[ax][ay] = EL_WALL;
+
+  if (new_wall)
+    PlayLevelSoundAction(ax, ay, ACTION_GROWING);
+}
+
 void CheckForDragon(int x, int y)
 {
   int i, j;
@@ -7870,7 +9384,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       {
        TimeLeft = action_arg_number_new;
 
+#if 1
+       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
@@ -7884,7 +9404,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->score = action_arg_number_new;
 
+#if 1
+      game_control_value[GAME_CONTROL_SCORE] = local_player->score;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Score(local_player->score);
+#endif
 
       break;
     }
@@ -7893,7 +9419,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       local_player->gems_still_needed = action_arg_number_new;
 
+#if 1
+      game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Emeralds(local_player->gems_still_needed);
+#endif
 
       break;
     }
@@ -8172,7 +9704,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 static void CreateFieldExt(int x, int y, int element, boolean is_change)
 {
   int old_element = Feld[x][y];
-  int new_element = get_element_from_group_element(element);
+  int new_element = GetElementFromGroupElement(element);
   int previous_move_direction = MovDir[x][y];
 #if USE_NEW_CUSTOM_VALUE
   int last_ce_value = CustomValue[x][y];
@@ -8504,8 +10036,19 @@ static void HandleElementChange(int x, int y, int page)
 
     if (change->can_change)
     {
-      ResetGfxAnimation(x, y);
-      ResetRandomAnimationValue(x, y);
+#if 1
+      /* !!! not clear why graphic animation should be reset at all here !!! */
+      /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
+#if USE_GFX_RESET_WHEN_NOT_MOVING
+      /* when a custom element is about to change (for example by change delay),
+        do not reset graphic animation when the custom element is moving */
+      if (!IS_MOVING(x, y))
+#endif
+      {
+       ResetGfxAnimation(x, y);
+       ResetRandomAnimationValue(x, y);
+      }
+#endif
 
       if (change->pre_change_function)
        change->pre_change_function(x, y);
@@ -9157,7 +10700,13 @@ static void CheckLevelTime()
        if (TimeLeft <= 10 && setup.time_limit)
          PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
+#if 1
+       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
        {
@@ -9168,8 +10717,17 @@ static void CheckLevelTime()
              KillPlayer(&stored_player[i]);
        }
       }
+#if 1
+      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      {
+       game_control_value[GAME_CONTROL_TIME] = TimePlayed;
+
+       DisplayGameControlValues();
+      }
+#else
       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
+#endif
 
       level.native_em_level->lev->time =
        (level.time == 0 ? TimePlayed : TimeLeft);
@@ -9178,6 +10736,8 @@ static void CheckLevelTime()
     if (tape.recording || tape.playing)
       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
   }
+
+  DrawGameDoorValues();
 }
 
 void AdvanceFrameAndPlayerCounters(int player_nr)
@@ -9300,7 +10860,7 @@ void GameActions()
       AllPlayersGone = TRUE;
   }
 
-  if (local_player->LevelSolved)
+  if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
     GameWon();
 
   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
@@ -9537,13 +11097,15 @@ void GameActions_RND()
 
        /* continue moving after pushing (this is actually a bug) */
        if (!IS_MOVING(x, y))
-       {
          Stop[x][y] = FALSE;
-       }
       }
     }
   }
 
+#if 0
+  debug_print_timestamp(0, "start main loop profiling");
+#endif
+
   SCAN_PLAYFIELD(x, y)
   {
     ChangeCount[x][y] = 0;
@@ -9618,6 +11180,63 @@ void GameActions_RND()
 #endif
   }
 
+#if 0
+  debug_print_timestamp(0, "- time for pre-main loop:");
+#endif
+
+#if 0  // -------------------- !!! TEST ONLY !!! --------------------
+  SCAN_PLAYFIELD(x, y)
+  {
+    element = Feld[x][y];
+    graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+#if 1
+    {
+#if 1
+      int element2 = element;
+      int graphic2 = graphic;
+#else
+      int element2 = Feld[x][y];
+      int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
+#endif
+      int last_gfx_frame = GfxFrame[x][y];
+
+      if (graphic_info[graphic2].anim_global_sync)
+       GfxFrame[x][y] = FrameCounter;
+      else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
+       GfxFrame[x][y] = CustomValue[x][y];
+      else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
+       GfxFrame[x][y] = element_info[element2].collect_score;
+      else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
+       GfxFrame[x][y] = ChangeDelay[x][y];
+
+      if (redraw && GfxFrame[x][y] != last_gfx_frame)
+       DrawLevelGraphicAnimation(x, y, graphic2);
+    }
+#else
+    ResetGfxFrame(x, y, TRUE);
+#endif
+
+#if 1
+    if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+       IS_NEXT_FRAME(GfxFrame[x][y], graphic))
+      ResetRandomAnimationValue(x, y);
+#endif
+
+#if 1
+    SetRandomAnimationValue(x, y);
+#endif
+
+#if 1
+    PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
+#endif
+  }
+#endif // -------------------- !!! TEST ONLY !!! --------------------
+
+#if 0
+  debug_print_timestamp(0, "- time for TEST loop:     -->");
+#endif
+
   SCAN_PLAYFIELD(x, y)
   {
     element = Feld[x][y];
@@ -9661,6 +11280,143 @@ void GameActions_RND()
       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
     }
 
+#if 0  // ---------------------------------------------------------------------
+
+    if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
+    {
+      StartMoving(x, y);
+
+      element = Feld[x][y];
+      graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+      if (IS_ANIMATED(graphic) &&
+         !IS_MOVING(x, y) &&
+         !Stop[x][y])
+       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+      if (IS_GEM(element) || element == EL_SP_INFOTRON)
+       DrawTwinkleOnField(x, y);
+    }
+    else if (IS_MOVING(x, y))
+      ContinueMoving(x, y);
+    else
+    {
+      switch (element)
+      {
+        case EL_ACID:
+        case EL_EXIT_OPEN:
+        case EL_EM_EXIT_OPEN:
+        case EL_SP_EXIT_OPEN:
+        case EL_STEEL_EXIT_OPEN:
+        case EL_EM_STEEL_EXIT_OPEN:
+        case EL_SP_TERMINAL:
+        case EL_SP_TERMINAL_ACTIVE:
+        case EL_EXTRA_TIME:
+        case EL_SHIELD_NORMAL:
+        case EL_SHIELD_DEADLY:
+         if (IS_ANIMATED(graphic))
+           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+         break;
+
+        case EL_DYNAMITE_ACTIVE:
+        case EL_EM_DYNAMITE_ACTIVE:
+        case EL_DYNABOMB_PLAYER_1_ACTIVE:
+        case EL_DYNABOMB_PLAYER_2_ACTIVE:
+        case EL_DYNABOMB_PLAYER_3_ACTIVE:
+        case EL_DYNABOMB_PLAYER_4_ACTIVE:
+        case EL_SP_DISK_RED_ACTIVE:
+         CheckDynamite(x, y);
+         break;
+
+        case EL_AMOEBA_GROWING:
+         AmoebeWaechst(x, y);
+         break;
+
+        case EL_AMOEBA_SHRINKING:
+         AmoebaDisappearing(x, y);
+         break;
+
+#if !USE_NEW_AMOEBA_CODE
+        case EL_AMOEBA_WET:
+        case EL_AMOEBA_DRY:
+        case EL_AMOEBA_FULL:
+        case EL_BD_AMOEBA:
+        case EL_EMC_DRIPPER:
+         AmoebeAbleger(x, y);
+         break;
+#endif
+
+        case EL_GAME_OF_LIFE:
+        case EL_BIOMAZE:
+         Life(x, y);
+         break;
+
+        case EL_EXIT_CLOSED:
+         CheckExit(x, y);
+         break;
+
+        case EL_EM_EXIT_CLOSED:
+         CheckExitEM(x, y);
+         break;
+
+        case EL_STEEL_EXIT_CLOSED:
+         CheckExitSteel(x, y);
+         break;
+
+        case EL_EM_STEEL_EXIT_CLOSED:
+         CheckExitSteelEM(x, y);
+         break;
+
+        case EL_SP_EXIT_CLOSED:
+         CheckExitSP(x, y);
+         break;
+
+        case EL_EXPANDABLE_WALL_GROWING:
+        case EL_EXPANDABLE_STEELWALL_GROWING:
+         MauerWaechst(x, y);
+         break;
+
+        case EL_EXPANDABLE_WALL:
+        case EL_EXPANDABLE_WALL_HORIZONTAL:
+        case EL_EXPANDABLE_WALL_VERTICAL:
+        case EL_EXPANDABLE_WALL_ANY:
+        case EL_BD_EXPANDABLE_WALL:
+         MauerAbleger(x, y);
+         break;
+
+        case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
+        case EL_EXPANDABLE_STEELWALL_VERTICAL:
+        case EL_EXPANDABLE_STEELWALL_ANY:
+         MauerAblegerStahl(x, y);
+         break;
+
+        case EL_FLAMES:
+         CheckForDragon(x, y);
+         break;
+
+        case EL_EXPLOSION:
+         break;
+
+        case EL_ELEMENT_SNAPPING:
+        case EL_DIAGONAL_SHRINKING:
+        case EL_DIAGONAL_GROWING:
+       {
+         graphic =
+           el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
+
+         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+         break;
+       }
+
+        default:
+         if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
+           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+         break;
+      }
+    }
+
+#else  // ---------------------------------------------------------------------
+
     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
     {
       StartMoving(x, y);
@@ -9678,8 +11434,10 @@ void GameActions_RND()
     }
     else if ((element == EL_ACID ||
              element == EL_EXIT_OPEN ||
+             element == EL_EM_EXIT_OPEN ||
              element == EL_SP_EXIT_OPEN ||
              element == EL_STEEL_EXIT_OPEN ||
+             element == EL_EM_STEEL_EXIT_OPEN ||
              element == EL_SP_TERMINAL ||
              element == EL_SP_TERMINAL_ACTIVE ||
              element == EL_EXTRA_TIME ||
@@ -9705,11 +11463,16 @@ void GameActions_RND()
       Life(x, y);
     else if (element == EL_EXIT_CLOSED)
       CheckExit(x, y);
+    else if (element == EL_EM_EXIT_CLOSED)
+      CheckExitEM(x, y);
     else if (element == EL_STEEL_EXIT_CLOSED)
       CheckExitSteel(x, y);
+    else if (element == EL_EM_STEEL_EXIT_CLOSED)
+      CheckExitSteelEM(x, y);
     else if (element == EL_SP_EXIT_CLOSED)
       CheckExitSP(x, y);
-    else if (element == EL_EXPANDABLE_WALL_GROWING)
+    else if (element == EL_EXPANDABLE_WALL_GROWING ||
+            element == EL_EXPANDABLE_STEELWALL_GROWING)
       MauerWaechst(x, y);
     else if (element == EL_EXPANDABLE_WALL ||
             element == EL_EXPANDABLE_WALL_HORIZONTAL ||
@@ -9717,6 +11480,10 @@ void GameActions_RND()
             element == EL_EXPANDABLE_WALL_ANY ||
             element == EL_BD_EXPANDABLE_WALL)
       MauerAbleger(x, y);
+    else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+            element == EL_EXPANDABLE_STEELWALL_ANY)
+      MauerAblegerStahl(x, y);
     else if (element == EL_FLAMES)
       CheckForDragon(x, y);
     else if (element == EL_EXPLOSION)
@@ -9732,6 +11499,8 @@ void GameActions_RND()
     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+#endif // ---------------------------------------------------------------------
+
     if (IS_BELT_ACTIVE(element))
       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
 
@@ -9745,7 +11514,10 @@ void GameActions_RND()
           element == EL_MAGIC_WALL_EMPTYING ||
           element == EL_BD_MAGIC_WALL_FULL ||
           element == EL_BD_MAGIC_WALL_ACTIVE ||
-          element == EL_BD_MAGIC_WALL_EMPTYING) &&
+          element == EL_BD_MAGIC_WALL_EMPTYING ||
+          element == EL_DC_MAGIC_WALL_FULL ||
+          element == EL_DC_MAGIC_WALL_ACTIVE ||
+          element == EL_DC_MAGIC_WALL_EMPTYING) &&
          ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
       {
        magic_wall_x = x;
@@ -9754,6 +11526,10 @@ void GameActions_RND()
     }
   }
 
+#if 0
+  debug_print_timestamp(0, "- time for MAIN loop:     -->");
+#endif
+
 #if USE_NEW_AMOEBA_CODE
   /* new experimental amoeba growth stuff */
   if (!(FrameCounter % 8))
@@ -9770,6 +11546,7 @@ void GameActions_RND()
          (element == EL_EMPTY ||
           CAN_GROW_INTO(element) ||
           element == EL_QUICKSAND_EMPTY ||
+          element == EL_QUICKSAND_FAST_EMPTY ||
           element == EL_ACID_SPLASH_LEFT ||
           element == EL_ACID_SPLASH_RIGHT))
       {
@@ -9816,6 +11593,10 @@ void GameActions_RND()
          element == EL_BD_MAGIC_WALL_ACTIVE ||
          element == EL_BD_MAGIC_WALL_EMPTYING)
        PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
+      else if (element == EL_DC_MAGIC_WALL_FULL ||
+              element == EL_DC_MAGIC_WALL_ACTIVE ||
+              element == EL_DC_MAGIC_WALL_EMPTYING)
+       PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
       else
        PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
     }
@@ -9841,6 +11622,12 @@ void GameActions_RND()
            Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
            DrawLevelField(x, y);
          }
+         else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
+                  element == EL_DC_MAGIC_WALL_FULL)
+         {
+           Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
+           DrawLevelField(x, y);
+         }
        }
 
        game.magic_wall_active = FALSE;
@@ -9926,6 +11713,11 @@ void GameActions_RND()
     local_player->show_envelope = 0;
   }
 
+#if 0
+  debug_print_timestamp(0, "stop main loop profiling ");
+  printf("----------------------------------------------------------\n");
+#endif
+
   /* use random number generator in every frame to make it less predictable */
   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
     RND(1);
@@ -9972,25 +11764,85 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
+#if 1
+  static Bitmap *bitmap_db_field2 = NULL;
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
   int x, y;
+#else
+  int i, x, y;
+#endif
+
+#if 0
+  /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
+  /* only horizontal XOR vertical scroll direction allowed */
+  if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
+    return;
+#endif
+
+#if 1
+  if (bitmap_db_field2 == NULL)
+    bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
+
+  /* needed when blitting directly to same bitmap -- should not be needed with
+     recent SDL libraries, but apparently does not work in 1.2.11 directly */
+  BlitBitmap(drawto_field, bitmap_db_field2,
+            FX + TILEX * (dx == -1) - softscroll_offset,
+            FY + TILEY * (dy == -1) - softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
+  BlitBitmap(bitmap_db_field2, drawto_field,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
+
+#else
+
+#if 1
+  /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
+  int xsize = (BX2 - BX1 + 1);
+  int ysize = (BY2 - BY1 + 1);
+  int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
+  int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
+  int step  = (start < end ? +1 : -1);
+
+  for (i = start; i != end; i += step)
+  {
+    BlitBitmap(drawto_field, drawto_field,
+              FX + TILEX * (dx != 0 ? i + step : 0),
+              FY + TILEY * (dy != 0 ? i + step : 0),
+              TILEX * (dx != 0 ? 1 : xsize),
+              TILEY * (dy != 0 ? 1 : ysize),
+              FX + TILEX * (dx != 0 ? i : 0),
+              FY + TILEY * (dy != 0 ? i : 0));
+  }
+
+#else
+
+  int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
 
   BlitBitmap(drawto_field, drawto_field,
             FX + TILEX * (dx == -1) - softscroll_offset,
             FY + TILEY * (dy == -1) - softscroll_offset,
-            SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
-            SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
             FX + TILEX * (dx == 1) - softscroll_offset,
             FY + TILEY * (dy == 1) - softscroll_offset);
+#endif
+#endif
 
-  if (dx)
+  if (dx != 0)
   {
     x = (dx == 1 ? BX1 : BX2);
     for (y = BY1; y <= BY2; y++)
       DrawScreenField(x, y);
   }
 
-  if (dy)
+  if (dy != 0)
   {
     y = (dy == 1 ? BY1 : BY2);
     for (x = BX1; x <= BX2; x++)
@@ -10301,7 +12153,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 #endif
   {
     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
-    int offset = (setup.scroll_delay ? 3 : 0);
+    int offset = game.scroll_delay_value;
 
     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
     {
@@ -10529,7 +12381,9 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     player->last_jy = jy;
 
     if (Feld[jx][jy] == EL_EXIT_OPEN ||
+       Feld[jx][jy] == EL_EM_EXIT_OPEN ||
        Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
+       Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
        Feld[jx][jy] == EL_SP_EXIT_OPEN ||
        Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
     {
@@ -10599,14 +12453,29 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        if (TimeLeft <= 10 && setup.time_limit)
          PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
+#if 1
+       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
            KillPlayer(&stored_player[i]);
       }
+#if 1
+      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      {
+       game_control_value[GAME_CONTROL_TIME] = TimePlayed;
+
+       DisplayGameControlValues();
+      }
+#else
       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
+#endif
     }
 
     if (tape.single_step && tape.recording && !tape.pausing &&
@@ -11455,6 +13324,9 @@ int DigField(struct PlayerInfo *player,
     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
                                        player->index_bit, dig_side);
 
+    if (element == EL_DC_LANDMINE)
+      Bang(x, y);
+
     if (Feld[x][y] != element)         /* field changed by snapping */
       return MP_ACTION;
 
@@ -11495,7 +13367,9 @@ int DigField(struct PlayerInfo *player,
        return MP_NO_ACTION;
     }
     else if (element == EL_EXIT_OPEN ||
+            element == EL_EM_EXIT_OPEN ||
             element == EL_STEEL_EXIT_OPEN ||
+            element == EL_EM_STEEL_EXIT_OPEN ||
             element == EL_SP_EXIT_OPEN ||
             element == EL_SP_EXIT_OPENING)
     {
@@ -11551,6 +13425,15 @@ int DigField(struct PlayerInfo *player,
       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
        return MP_NO_ACTION;
     }
+    else if (element == EL_DC_GATE_WHITE ||
+            element == EL_DC_GATE_WHITE_GRAY ||
+            element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
+    {
+      if (player->num_white_keys == 0)
+       return MP_NO_ACTION;
+
+      player->num_white_keys--;
+    }
     else if (IS_SP_PORT(element))
     {
       if (element == EL_SP_GRAVITY_PORT_LEFT ||
@@ -11641,7 +13524,14 @@ int DigField(struct PlayerInfo *player,
     else if (element == EL_EXTRA_TIME && level.time > 0)
     {
       TimeLeft += level.extra_time;
+
+#if 1
+      game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Time(TimeLeft);
+#endif
     }
     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
     {
@@ -11677,6 +13567,13 @@ int DigField(struct PlayerInfo *player,
 
       DrawGameDoorValues();
     }
+    else if (element == EL_DC_KEY_WHITE)
+    {
+      player->num_white_keys++;
+
+      /* display white keys? */
+      /* DrawGameDoorValues(); */
+    }
     else if (IS_ENVELOPE(element))
     {
       player->show_envelope = element;
@@ -11713,7 +13610,13 @@ int DigField(struct PlayerInfo *player,
       if (local_player->gems_still_needed < 0)
        local_player->gems_still_needed = 0;
 
+#if 1
+      game_control_value[GAME_CONTROL_GEMS] = local_player->gems_still_needed;
+
+      DisplayGameControlValues();
+#else
       DrawGameValue_Emeralds(local_player->gems_still_needed);
+#endif
     }
 
     RaiseScoreElement(element);
@@ -11912,7 +13815,9 @@ int DigField(struct PlayerInfo *player,
       ToggleBeltSwitch(x, y);
     }
     else if (element == EL_SWITCHGATE_SWITCH_UP ||
-            element == EL_SWITCHGATE_SWITCH_DOWN)
+            element == EL_SWITCHGATE_SWITCH_DOWN ||
+            element == EL_DC_SWITCHGATE_SWITCH_UP ||
+            element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
       ToggleSwitchgateSwitch(x, y);
     }
@@ -11921,7 +13826,8 @@ int DigField(struct PlayerInfo *player,
     {
       ToggleLightSwitch(x, y);
     }
-    else if (element == EL_TIMEGATE_SWITCH)
+    else if (element == EL_TIMEGATE_SWITCH ||
+            element == EL_DC_TIMEGATE_SWITCH)
     {
       ActivateTimegateSwitch(x, y);
     }
@@ -11954,7 +13860,14 @@ int DigField(struct PlayerInfo *player,
       if (level.time > 0 || level.use_time_orb_bug)
       {
        TimeLeft += level.time_orb_time;
+
+#if 1
+       game_control_value[GAME_CONTROL_TIME] = TimeLeft;
+
+       DisplayGameControlValues();
+#else
        DrawGameValue_Time(TimeLeft);
+#endif
       }
 
       ResetGfxAnimation(x, y);
@@ -12576,7 +14489,13 @@ void RaiseScore(int value)
 {
   local_player->score += value;
 
+#if 1
+  game_control_value[GAME_CONTROL_SCORE] = local_player->score;
+
+  DisplayGameControlValues();
+#else
   DrawGameValue_Score(local_player->score);
+#endif
 }
 
 void RaiseScoreElement(int element)
@@ -12650,6 +14569,7 @@ void RaiseScoreElement(int element)
     case EL_EMC_KEY_6:
     case EL_EMC_KEY_7:
     case EL_EMC_KEY_8:
+    case EL_DC_KEY_WHITE:
       RaiseScore(level.score[SC_KEY]);
       break;
     default:
@@ -12670,13 +14590,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;
 
@@ -12860,9 +14798,9 @@ static void LoadEngineSnapshotValues_RND()
 
   if (game.num_random_calls != num_random_calls)
   {
-    Error(ERR_RETURN, "number of random calls out of sync");
-    Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
-    Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
+    Error(ERR_INFO, "number of random calls out of sync");
+    Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
+    Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
     Error(ERR_EXIT, "this should not happen -- please debug");
   }
 }
@@ -13035,11 +14973,50 @@ boolean CheckEngineSnapshot()
 
 static struct
 {
-  int x, y;
+  int *x, *y;
+  int gd_x, gd_y;
   int gadget_id;
   char *infotext;
 } gamebutton_info[NUM_GAME_BUTTONS] =
 {
+#if 1
+  {
+    &game.button.stop.x,       &game.button.stop.y,
+    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_STOP,
+    "stop game"
+  },
+  {
+    &game.button.pause.x,      &game.button.pause.y,
+    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PAUSE,
+    "pause game"
+  },
+  {
+    &game.button.play.x,       &game.button.play.y,
+    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PLAY,
+    "play game"
+  },
+  {
+    &game.button.sound_music.x,        &game.button.sound_music.y,
+    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_MUSIC,
+    "background music on/off"
+  },
+  {
+    &game.button.sound_loops.x,        &game.button.sound_loops.y,
+    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_LOOPS,
+    "sound loops on/off"
+  },
+  {
+    &game.button.sound_simple.x,&game.button.sound_simple.y,
+    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_SIMPLE,
+    "normal sounds on/off"
+  }
+#else
   {
     GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
     GAME_CTRL_ID_STOP,
@@ -13070,6 +15047,7 @@ static struct
     SOUND_CTRL_ID_SIMPLE,
     "normal sounds on/off"
   }
+#endif
 };
 
 void CreateGameButtons()
@@ -13083,12 +15061,15 @@ void CreateGameButtons()
     int button_type;
     boolean checked;
     unsigned long event_mask;
+    int x, y;
     int gd_xoffset, gd_yoffset;
     int gd_x1, gd_x2, gd_y1, gd_y2;
     int id = i;
 
-    gd_xoffset = gamebutton_info[i].x;
-    gd_yoffset = gamebutton_info[i].y;
+    x = DX + *gamebutton_info[i].x;
+    y = DY + *gamebutton_info[i].y;
+    gd_xoffset = gamebutton_info[i].gd_x;
+    gd_yoffset = gamebutton_info[i].gd_y;
     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
 
@@ -13116,8 +15097,13 @@ void CreateGameButtons()
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_INFO_TEXT, gamebutton_info[i].infotext,
+#if 1
+                     GDI_X, x,
+                     GDI_Y, y,
+#else
                      GDI_X, DX + gd_xoffset,
                      GDI_Y, DY + gd_yoffset,
+#endif
                      GDI_WIDTH, GAME_BUTTON_XSIZE,
                      GDI_HEIGHT, GAME_BUTTON_YSIZE,
                      GDI_TYPE, button_type,