rnd-20080120-1-src
[rocksndiamonds.git] / src / game.c
index 3f6ec77b97628b366a671484bc2c9f203ebc4886..441793cef025f261b769d8f6dab212a4b23825dc 100644 (file)
 
 #define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
 
+#define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 1)
+
+#if USE_DELAYED_GFX_REDRAW
+#define TEST_DrawLevelField(x, y)                              \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE
+#define TEST_DrawLevelFieldCrumbledSand(x, y)                  \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
+#define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)                \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
+#define TEST_DrawTwinkleOnField(x, y)                          \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
+#else
+#define TEST_DrawLevelField(x, y)                              \
+            DrawLevelField(x, y)
+#define TEST_DrawLevelFieldCrumbledSand(x, y)                  \
+            DrawLevelFieldCrumbledSand(x, y)
+#define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)                \
+            DrawLevelFieldCrumbledSandNeighbours(x, y)
+#define TEST_DrawTwinkleOnField(x, y)                          \
+            DrawTwinkleOnField(x, y)
+#endif
+
 
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define GAME_PANEL_KEY_WHITE                   27
 #define GAME_PANEL_KEY_WHITE_COUNT             28
 #define GAME_PANEL_SCORE                       29
-#define GAME_PANEL_TIME                                30
-#define GAME_PANEL_TIME_HH                     31
-#define GAME_PANEL_TIME_MM                     32
-#define GAME_PANEL_TIME_SS                     33
-#define GAME_PANEL_SHIELD_NORMAL               34
-#define GAME_PANEL_SHIELD_NORMAL_TIME          35
-#define GAME_PANEL_SHIELD_DEADLY               36
-#define GAME_PANEL_SHIELD_DEADLY_TIME          37
-#define GAME_PANEL_EXIT                                38
-#define GAME_PANEL_EMC_MAGIC_BALL              39
-#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       40
-#define GAME_PANEL_LIGHT_SWITCH                        41
-#define GAME_PANEL_LIGHT_SWITCH_TIME           42
-#define GAME_PANEL_TIMEGATE_SWITCH             43
-#define GAME_PANEL_TIMEGATE_SWITCH_TIME                44
-#define GAME_PANEL_SWITCHGATE_SWITCH           45
-#define GAME_PANEL_EMC_LENSES                  46
-#define GAME_PANEL_EMC_LENSES_TIME             47
-#define GAME_PANEL_EMC_MAGNIFIER               48
-#define GAME_PANEL_EMC_MAGNIFIER_TIME          49
-#define GAME_PANEL_BALLOON_SWITCH              50
-#define GAME_PANEL_DYNABOMB_NUMBER             51
-#define GAME_PANEL_DYNABOMB_SIZE               52
-#define GAME_PANEL_DYNABOMB_POWER              53
-#define GAME_PANEL_PENGUINS                    54
-#define GAME_PANEL_SOKOBAN_OBJECTS             55
-#define GAME_PANEL_SOKOBAN_FIELDS              56
-#define GAME_PANEL_ROBOT_WHEEL                 57
-#define GAME_PANEL_CONVEYOR_BELT_1             58
-#define GAME_PANEL_CONVEYOR_BELT_2             59
-#define GAME_PANEL_CONVEYOR_BELT_3             60
-#define GAME_PANEL_CONVEYOR_BELT_4             61
-#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      62
-#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      63
-#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      64
-#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      65
-#define GAME_PANEL_MAGIC_WALL                  66
-#define GAME_PANEL_MAGIC_WALL_TIME             67
-#define GAME_PANEL_GRAVITY_STATE               68
-#define GAME_PANEL_ELEMENT_1                   69
-#define GAME_PANEL_ELEMENT_2                   70
-#define GAME_PANEL_ELEMENT_3                   71
-#define GAME_PANEL_ELEMENT_4                   72
-#define GAME_PANEL_ELEMENT_5                   73
-#define GAME_PANEL_ELEMENT_6                   74
-#define GAME_PANEL_ELEMENT_7                   75
-#define GAME_PANEL_ELEMENT_8                   76
-#define GAME_PANEL_CE_SCORE_1                  77
-#define GAME_PANEL_CE_SCORE_2                  78
-#define GAME_PANEL_CE_SCORE_3                  79
-#define GAME_PANEL_CE_SCORE_4                  80
-#define GAME_PANEL_CE_SCORE_5                  81
-#define GAME_PANEL_CE_SCORE_6                  82
-#define GAME_PANEL_CE_SCORE_7                  83
-#define GAME_PANEL_CE_SCORE_8                  84
-#define GAME_PANEL_CE_SCORE_1_ELEMENT          85
-#define GAME_PANEL_CE_SCORE_2_ELEMENT          86
-#define GAME_PANEL_CE_SCORE_3_ELEMENT          87
-#define GAME_PANEL_CE_SCORE_4_ELEMENT          88
-#define GAME_PANEL_CE_SCORE_5_ELEMENT          89
-#define GAME_PANEL_CE_SCORE_6_ELEMENT          90
-#define GAME_PANEL_CE_SCORE_7_ELEMENT          91
-#define GAME_PANEL_CE_SCORE_8_ELEMENT          92
-#define GAME_PANEL_PLAYER_NAME                 93
-#define GAME_PANEL_LEVEL_NAME                  94
-#define GAME_PANEL_LEVEL_AUTHOR                        95
-
-#define NUM_GAME_PANEL_CONTROLS                        96
+#define GAME_PANEL_HIGHSCORE                   30
+#define GAME_PANEL_TIME                                31
+#define GAME_PANEL_TIME_HH                     32
+#define GAME_PANEL_TIME_MM                     33
+#define GAME_PANEL_TIME_SS                     34
+#define GAME_PANEL_SHIELD_NORMAL               35
+#define GAME_PANEL_SHIELD_NORMAL_TIME          36
+#define GAME_PANEL_SHIELD_DEADLY               37
+#define GAME_PANEL_SHIELD_DEADLY_TIME          38
+#define GAME_PANEL_EXIT                                39
+#define GAME_PANEL_EMC_MAGIC_BALL              40
+#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       41
+#define GAME_PANEL_LIGHT_SWITCH                        42
+#define GAME_PANEL_LIGHT_SWITCH_TIME           43
+#define GAME_PANEL_TIMEGATE_SWITCH             44
+#define GAME_PANEL_TIMEGATE_SWITCH_TIME                45
+#define GAME_PANEL_SWITCHGATE_SWITCH           46
+#define GAME_PANEL_EMC_LENSES                  47
+#define GAME_PANEL_EMC_LENSES_TIME             48
+#define GAME_PANEL_EMC_MAGNIFIER               49
+#define GAME_PANEL_EMC_MAGNIFIER_TIME          50
+#define GAME_PANEL_BALLOON_SWITCH              51
+#define GAME_PANEL_DYNABOMB_NUMBER             52
+#define GAME_PANEL_DYNABOMB_SIZE               53
+#define GAME_PANEL_DYNABOMB_POWER              54
+#define GAME_PANEL_PENGUINS                    55
+#define GAME_PANEL_SOKOBAN_OBJECTS             56
+#define GAME_PANEL_SOKOBAN_FIELDS              57
+#define GAME_PANEL_ROBOT_WHEEL                 58
+#define GAME_PANEL_CONVEYOR_BELT_1             59
+#define GAME_PANEL_CONVEYOR_BELT_2             60
+#define GAME_PANEL_CONVEYOR_BELT_3             61
+#define GAME_PANEL_CONVEYOR_BELT_4             62
+#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      63
+#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      64
+#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      65
+#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      66
+#define GAME_PANEL_MAGIC_WALL                  67
+#define GAME_PANEL_MAGIC_WALL_TIME             68
+#define GAME_PANEL_GRAVITY_STATE               69
+#define GAME_PANEL_GRAPHIC_1                   70
+#define GAME_PANEL_GRAPHIC_2                   71
+#define GAME_PANEL_GRAPHIC_3                   72
+#define GAME_PANEL_GRAPHIC_4                   73
+#define GAME_PANEL_GRAPHIC_5                   74
+#define GAME_PANEL_GRAPHIC_6                   75
+#define GAME_PANEL_GRAPHIC_7                   76
+#define GAME_PANEL_GRAPHIC_8                   77
+#define GAME_PANEL_ELEMENT_1                   78
+#define GAME_PANEL_ELEMENT_2                   79
+#define GAME_PANEL_ELEMENT_3                   80
+#define GAME_PANEL_ELEMENT_4                   81
+#define GAME_PANEL_ELEMENT_5                   82
+#define GAME_PANEL_ELEMENT_6                   83
+#define GAME_PANEL_ELEMENT_7                   84
+#define GAME_PANEL_ELEMENT_8                   85
+#define GAME_PANEL_ELEMENT_COUNT_1             86
+#define GAME_PANEL_ELEMENT_COUNT_2             87
+#define GAME_PANEL_ELEMENT_COUNT_3             88
+#define GAME_PANEL_ELEMENT_COUNT_4             89
+#define GAME_PANEL_ELEMENT_COUNT_5             90
+#define GAME_PANEL_ELEMENT_COUNT_6             91
+#define GAME_PANEL_ELEMENT_COUNT_7             92
+#define GAME_PANEL_ELEMENT_COUNT_8             93
+#define GAME_PANEL_CE_SCORE_1                  94
+#define GAME_PANEL_CE_SCORE_2                  95
+#define GAME_PANEL_CE_SCORE_3                  96
+#define GAME_PANEL_CE_SCORE_4                  97
+#define GAME_PANEL_CE_SCORE_5                  98
+#define GAME_PANEL_CE_SCORE_6                  99
+#define GAME_PANEL_CE_SCORE_7                  100
+#define GAME_PANEL_CE_SCORE_8                  101
+#define GAME_PANEL_CE_SCORE_1_ELEMENT          102
+#define GAME_PANEL_CE_SCORE_2_ELEMENT          103
+#define GAME_PANEL_CE_SCORE_3_ELEMENT          104
+#define GAME_PANEL_CE_SCORE_4_ELEMENT          105
+#define GAME_PANEL_CE_SCORE_5_ELEMENT          106
+#define GAME_PANEL_CE_SCORE_6_ELEMENT          107
+#define GAME_PANEL_CE_SCORE_7_ELEMENT          108
+#define GAME_PANEL_CE_SCORE_8_ELEMENT          109
+#define GAME_PANEL_PLAYER_NAME                 110
+#define GAME_PANEL_LEVEL_NAME                  111
+#define GAME_PANEL_LEVEL_AUTHOR                        112
+
+#define NUM_GAME_PANEL_CONTROLS                        113
 
 struct GamePanelOrderInfo
 {
@@ -403,6 +442,11 @@ static struct GamePanelControlInfo game_panel_controls[] =
     &game.panel.score,
     TYPE_INTEGER,
   },
+  {
+    GAME_PANEL_HIGHSCORE,
+    &game.panel.highscore,
+    TYPE_INTEGER,
+  },
   {
     GAME_PANEL_TIME,
     &game.panel.time,
@@ -598,6 +642,46 @@ static struct GamePanelControlInfo game_panel_controls[] =
     &game.panel.gravity_state,
     TYPE_STRING,
   },
+  {
+    GAME_PANEL_GRAPHIC_1,
+    &game.panel.graphic[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_2,
+    &game.panel.graphic[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_3,
+    &game.panel.graphic[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_4,
+    &game.panel.graphic[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_5,
+    &game.panel.graphic[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_6,
+    &game.panel.graphic[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_7,
+    &game.panel.graphic[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_8,
+    &game.panel.graphic[7],
+    TYPE_ELEMENT,
+  },
   {
     GAME_PANEL_ELEMENT_1,
     &game.panel.element[0],
@@ -638,6 +722,46 @@ static struct GamePanelControlInfo game_panel_controls[] =
     &game.panel.element[7],
     TYPE_ELEMENT,
   },
+  {
+    GAME_PANEL_ELEMENT_COUNT_1,
+    &game.panel.element_count[0],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_2,
+    &game.panel.element_count[1],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_3,
+    &game.panel.element_count[2],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_4,
+    &game.panel.element_count[3],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_5,
+    &game.panel.element_count[4],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_6,
+    &game.panel.element_count[5],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_7,
+    &game.panel.element_count[6],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_8,
+    &game.panel.element_count[7],
+    TYPE_INTEGER,
+  },
   {
     GAME_PANEL_CE_SCORE_1,
     &game.panel.ce_score[0],
@@ -800,6 +924,9 @@ static struct GamePanelControlInfo game_panel_controls[] =
         (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
         (be) + (e) - EL_SELF)
 
+#define GET_PLAYER_FROM_BITS(p)                                                \
+       (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
+
 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                          \
        ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
         (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
@@ -928,7 +1055,10 @@ static boolean MovePlayer(struct PlayerInfo *, int, int);
 static void ScrollPlayer(struct PlayerInfo *, int);
 static void ScrollScreen(struct PlayerInfo *, int);
 
-int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static boolean DigFieldByCE(int, int, int);
+static boolean SnapField(struct PlayerInfo *, int, int);
+static boolean DropElement(struct PlayerInfo *);
 
 static void InitBeltMovement(void);
 static void CloseAllOpenTimegates(void);
@@ -1000,9 +1130,6 @@ void KillPlayer(struct PlayerInfo *);
 void BuryPlayer(struct PlayerInfo *);
 void RemovePlayer(struct PlayerInfo *);
 
-boolean SnapField(struct PlayerInfo *, int, int);
-boolean DropElement(struct PlayerInfo *);
-
 static int getInvisibleActiveFromInvisibleElement(int);
 static int getInvisibleFromInvisibleActiveElement(int);
 
@@ -1838,6 +1965,34 @@ static void InitField(int x, int y, boolean init_game)
        Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
       break;
 
+    case EL_TRIGGER_PLAYER:
+    case EL_TRIGGER_ELEMENT:
+    case EL_TRIGGER_CE_VALUE:
+    case EL_TRIGGER_CE_SCORE:
+    case EL_SELF:
+    case EL_ANY_ELEMENT:
+    case EL_CURRENT_CE_VALUE:
+    case EL_CURRENT_CE_SCORE:
+    case EL_PREV_CE_1:
+    case EL_PREV_CE_2:
+    case EL_PREV_CE_3:
+    case EL_PREV_CE_4:
+    case EL_PREV_CE_5:
+    case EL_PREV_CE_6:
+    case EL_PREV_CE_7:
+    case EL_PREV_CE_8:
+    case EL_NEXT_CE_1:
+    case EL_NEXT_CE_2:
+    case EL_NEXT_CE_3:
+    case EL_NEXT_CE_4:
+    case EL_NEXT_CE_5:
+    case EL_NEXT_CE_6:
+    case EL_NEXT_CE_7:
+    case EL_NEXT_CE_8:
+      /* reference elements should not be used on the playfield */
+      Feld[x][y] = EL_EMPTY;
+      break;
+
     default:
       if (IS_CUSTOM_ELEMENT(element))
       {
@@ -1979,7 +2134,7 @@ void InitGameControlValues()
 
     if (nr != i)
     {
-      Error(ERR_INFO, "'game_panel_controls' structure corrupted");
+      Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
       Error(ERR_EXIT, "this should not happen -- please debug");
     }
 
@@ -2010,19 +2165,62 @@ void InitGameControlValues()
        sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
 }
 
+void UpdatePlayfieldElementCount()
+{
+  boolean use_element_count = FALSE;
+  int i, j, x, y;
+
+  /* first check if it is needed at all to calculate playfield element count */
+  for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
+    if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
+      use_element_count = TRUE;
+
+  if (!use_element_count)
+    return;
+
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    element_info[i].element_count = 0;
+
+  SCAN_PLAYFIELD(x, y)
+  {
+    element_info[Feld[x][y]].element_count++;
+  }
+
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+    for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+      if (IS_IN_GROUP(j, i))
+       element_info[EL_GROUP_START + i].element_count +=
+         element_info[j].element_count;
+}
+
 void UpdateGameControlValues()
 {
   int i, k;
-  int time = (level.time == 0 ? TimePlayed : TimeLeft);
-  int score = (local_player->LevelSolved ? local_player->score_final :
+  int time = (local_player->LevelSolved ?
+             local_player->LevelSolved_CountingTime :
+             level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+             level.native_em_level->lev->time :
+             level.time == 0 ? TimePlayed : TimeLeft);
+  int score = (local_player->LevelSolved ?
+              local_player->LevelSolved_CountingScore :
+              level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+              level.native_em_level->lev->score :
               local_player->score);
-  int exit_closed = (local_player->gems_still_needed > 0 ||
+  int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+             level.native_em_level->lev->required :
+             local_player->gems_still_needed);
+  int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                    level.native_em_level->lev->required > 0 :
+                    local_player->gems_still_needed > 0 ||
                     local_player->sokobanfields_still_needed > 0 ||
                     local_player->lights_still_needed > 0);
 
+  UpdatePlayfieldElementCount();
+
+  /* update game panel control values */
+
   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
-  game_panel_controls[GAME_PANEL_GEMS].value =
-    local_player->gems_still_needed;
+  game_panel_controls[GAME_PANEL_GEMS].value = gems;
 
   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
   for (i = 0; i < MAX_NUM_KEYS; i++)
@@ -2035,12 +2233,24 @@ void UpdateGameControlValues()
     for (i = 0; i < MAX_PLAYERS; i++)
     {
       for (k = 0; k < MAX_NUM_KEYS; k++)
-       if (stored_player[i].key[k])
+      {
+       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+       {
+         if (level.native_em_level->ply[i]->keys & (1 << k))
+           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+             get_key_element_from_nr(k);
+       }
+       else if (stored_player[i].key[k])
          game_panel_controls[GAME_PANEL_KEY_1 + k].value =
            get_key_element_from_nr(k);
+      }
 
-      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-       stored_player[i].inventory_size;
+      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         level.native_em_level->ply[i]->dynamite;
+      else
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         stored_player[i].inventory_size;
 
       if (stored_player[i].num_white_keys > 0)
        game_panel_controls[GAME_PANEL_KEY_WHITE].value =
@@ -2055,12 +2265,24 @@ void UpdateGameControlValues()
     int player_nr = game.centered_player_nr;
 
     for (k = 0; k < MAX_NUM_KEYS; k++)
-      if (stored_player[player_nr].key[k])
+    {
+      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      {
+       if (level.native_em_level->ply[player_nr]->keys & (1 << k))
+         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+           get_key_element_from_nr(k);
+      }
+      else if (stored_player[player_nr].key[k])
        game_panel_controls[GAME_PANEL_KEY_1 + k].value =
          get_key_element_from_nr(k);
+    }
 
-    game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-      stored_player[player_nr].inventory_size;
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       level.native_em_level->ply[player_nr]->dynamite;
+    else
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       stored_player[player_nr].inventory_size;
 
     if (stored_player[player_nr].num_white_keys > 0)
       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
@@ -2078,6 +2300,7 @@ void UpdateGameControlValues()
   }
 
   game_panel_controls[GAME_PANEL_SCORE].value = score;
+  game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
 
   game_panel_controls[GAME_PANEL_TIME].value = time;
 
@@ -2174,11 +2397,19 @@ void UpdateGameControlValues()
   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
 #endif
 
+  for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
+    game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
+
   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
        game.panel.element[i].id : EL_UNDEFINED);
 
+  for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+    game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
+      (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
+       element_info[game.panel.element_count[i].id].element_count : 0);
+
   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
@@ -2194,41 +2425,46 @@ void UpdateGameControlValues()
   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
 
+  /* update game panel control frames */
+
   for (i = 0; game_panel_controls[i].nr != -1; i++)
   {
     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
 
     if (gpc->type == TYPE_ELEMENT)
     {
-      int last_anim_random_frame = gfx.anim_random_frame;
-      int element = gpc->value;
-      int graphic = el2panelimg(element);
-
-      if (gpc->value != gpc->last_value)
-      {
-       gpc->gfx_frame = 0;
-       gpc->gfx_random = INIT_GFX_RANDOM();
-      }
-      else
+      if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
       {
-       gpc->gfx_frame++;
+       int last_anim_random_frame = gfx.anim_random_frame;
+       int element = gpc->value;
+       int graphic = el2panelimg(element);
 
-       if (ANIM_MODE(graphic) == ANIM_RANDOM &&
-           IS_NEXT_FRAME(gpc->gfx_frame, graphic))
+       if (gpc->value != gpc->last_value)
+       {
+         gpc->gfx_frame = 0;
          gpc->gfx_random = INIT_GFX_RANDOM();
-      }
+       }
+       else
+       {
+         gpc->gfx_frame++;
 
-      if (ANIM_MODE(graphic) == ANIM_RANDOM)
-       gfx.anim_random_frame = gpc->gfx_random;
+         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
+           gpc->gfx_random = INIT_GFX_RANDOM();
+       }
+
+       if (ANIM_MODE(graphic) == ANIM_RANDOM)
+         gfx.anim_random_frame = gpc->gfx_random;
 
-      if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
-       gpc->gfx_frame = element_info[element].collect_score;
+       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+         gpc->gfx_frame = element_info[element].collect_score;
 
-      gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
-                                           gpc->gfx_frame);
+       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
+                                             gpc->gfx_frame);
 
-      if (ANIM_MODE(graphic) == ANIM_RANDOM)
-       gfx.anim_random_frame = last_anim_random_frame;
+       if (ANIM_MODE(graphic) == ANIM_RANDOM)
+         gfx.anim_random_frame = last_anim_random_frame;
+      }
     }
   }
 }
@@ -2373,6 +2609,13 @@ void DisplayGameControlValues()
        element = value;
        graphic = el2panelimg(value);
 
+       // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
+
+#if 1
+       if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
+         size = TILESIZE;
+#endif
+
        getSizedGraphicSource(graphic, frame, size, &src_bitmap,
                              &src_x, &src_y);
 
@@ -2479,6 +2722,15 @@ void DisplayGameControlValues()
   game_status = GAME_MODE_PLAYING;
 }
 
+void UpdateAndDisplayGameControlValues()
+{
+  if (tape.warp_forward)
+    return;
+
+  UpdateGameControlValues();
+  DisplayGameControlValues();
+}
+
 void DrawGameValue_Emeralds(int value)
 {
   struct TextPosInfo *pos = &game.panel.gems;
@@ -3114,7 +3366,7 @@ static void InitGameEngine()
     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
   }
 
-  /* ---------- initialize internal run-time variables ------------- */
+  /* ---------- initialize internal run-time variables --------------------- */
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
@@ -3152,6 +3404,27 @@ static void InitGameEngine()
     }
   }
 
+#if 1
+  /* ---------- initialize reference elements in change conditions --------- */
+
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+    struct ElementInfo *ei = &element_info[element];
+
+    for (j = 0; j < ei->num_change_pages; j++)
+    {
+      int trigger_element = ei->change_page[j].initial_trigger_element;
+
+      if (trigger_element >= EL_PREV_CE_8 &&
+         trigger_element <= EL_NEXT_CE_8)
+       trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
+
+      ei->change_page[j].trigger_element = trigger_element;
+    }
+  }
+#endif
+
   /* ---------- initialize run-time trigger player and element ------------- */
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -3162,6 +3435,7 @@ static void InitGameEngine()
     {
       ei->change_page[j].actual_trigger_element = EL_EMPTY;
       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
+      ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
       ei->change_page[j].actual_trigger_ce_value = 0;
       ei->change_page[j].actual_trigger_ce_score = 0;
@@ -3526,6 +3800,8 @@ void InitGame()
     player->LevelSolved_PanelOff = FALSE;
     player->LevelSolved_SaveTape = FALSE;
     player->LevelSolved_SaveScore = FALSE;
+    player->LevelSolved_CountingTime = 0;
+    player->LevelSolved_CountingScore = 0;
   }
 
   network_player_action_received = FALSE;
@@ -3629,6 +3905,7 @@ void InitGame()
     GfxElement[x][y] = EL_UNDEFINED;
     GfxAction[x][y] = ACTION_DEFAULT;
     GfxDir[x][y] = MV_NONE;
+    GfxRedraw[x][y] = GFX_REDRAW_NONE;
   }
 
   SCAN_PLAYFIELD(x, y)
@@ -3950,8 +4227,10 @@ void InitGame()
                local_player->jy - MIDPOSY);
   }
 
+#if 0
   /* do not use PLAYING mask for fading out from main screen */
   game_status = GAME_MODE_MAIN;
+#endif
 
   StopAnimation();
 
@@ -3977,7 +4256,9 @@ void InitGame()
     FadeOut(REDRAW_FIELD);
 #endif
 
+#if 0
   game_status = GAME_MODE_PLAYING;
+#endif
 
   /* !!! FIX THIS (START) !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
@@ -4023,8 +4304,12 @@ void InitGame()
   SetPanelBackground();
   SetDrawBackgroundMask(REDRAW_DOOR_1);
 
+#if 1
+  UpdateAndDisplayGameControlValues();
+#else
   UpdateGameDoorValues();
   DrawGameDoorValues();
+#endif
 
   if (!game.restart_level)
   {
@@ -4281,6 +4566,9 @@ static void PlayerWins(struct PlayerInfo *player)
 
   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
                         level.native_em_level->lev->score : player->score);
+
+  player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
+  player->LevelSolved_CountingScore = player->score_final;
 }
 
 void GameWon()
@@ -4336,6 +4624,9 @@ void GameWon()
       score = score_final;
 
 #if 1
+      local_player->LevelSolved_CountingTime = time;
+      local_player->LevelSolved_CountingScore = score;
+
       game_panel_controls[GAME_PANEL_TIME].value = time;
       game_panel_controls[GAME_PANEL_SCORE].value = score;
 
@@ -4418,6 +4709,9 @@ void GameWon()
     score += time_count_steps * level.score[SC_TIME_BONUS];
 
 #if 1
+    local_player->LevelSolved_CountingTime = time;
+    local_player->LevelSolved_CountingScore = score;
+
     game_panel_controls[GAME_PANEL_TIME].value = time;
     game_panel_controls[GAME_PANEL_SCORE].value = score;
 
@@ -4842,6 +5136,7 @@ static void RemoveField(int x, int y)
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MV_NONE;
+  GfxRedraw[x][y] = GFX_REDRAW_NONE;
 }
 
 void RemoveMovingField(int x, int y)
@@ -4867,7 +5162,7 @@ void RemoveMovingField(int x, int y)
 
       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
 
-      DrawLevelField(oldx, oldy);
+      TEST_DrawLevelField(oldx, oldy);
 
       return;
     }
@@ -4896,8 +5191,8 @@ void RemoveMovingField(int x, int y)
   if (next_element != EL_UNDEFINED)
     Feld[oldx][oldy] = next_element;
 
-  DrawLevelField(oldx, oldy);
-  DrawLevelField(newx, newy);
+  TEST_DrawLevelField(oldx, oldy);
+  TEST_DrawLevelField(newx, newy);
 }
 
 void DrawDynamite(int x, int y)
@@ -5560,12 +5855,12 @@ void Explode(int ex, int ey, int phase, int mode)
 
     InitField_WithBug2(x, y, FALSE);
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     TestIfElementTouchesCustomElement(x, y);
 
     if (GFX_CRUMBLED(element))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
 
     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
       StorePlayer[x][y] = 0;
@@ -5579,7 +5874,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
 
     if (phase == delay)
-      DrawLevelFieldCrumbledSand(x, y);
+      TEST_DrawLevelFieldCrumbledSand(x, y);
 
     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
@@ -5882,7 +6177,7 @@ static void ToggleBeltSwitch(int x, int y)
       if (e_belt_nr == belt_nr)
       {
        Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
     else if (IS_BELT(element) && belt_dir != MV_NONE)
@@ -5894,7 +6189,7 @@ static void ToggleBeltSwitch(int x, int y)
        int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
 
        Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
@@ -5906,7 +6201,7 @@ static void ToggleBeltSwitch(int x, int y)
        int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
 
        Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
   }
@@ -5927,34 +6222,34 @@ static void ToggleSwitchgateSwitch(int x, int y)
        element == EL_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
             element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
 #else
     if (element == EL_SWITCHGATE_SWITCH_UP)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
 #endif
     else if (element == EL_SWITCHGATE_OPEN ||
@@ -6002,25 +6297,25 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
        game.light_time_left > 0)
     {
       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
             game.light_time_left == 0)
     {
       Feld[x][y] = EL_LIGHT_SWITCH;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER &&
             game.light_time_left > 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.light_time_left == 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
             element == EL_INVISIBLE_WALL ||
@@ -6029,11 +6324,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
       if (game.light_time_left > 0)
        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -6042,11 +6337,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
       if (game.light_time_left == 0)
        Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
     }
   }
 }
@@ -6063,13 +6358,13 @@ static void RedrawAllInvisibleElementsForLenses()
        game.lenses_time_left > 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.lenses_time_left == 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
             element == EL_INVISIBLE_WALL ||
@@ -6078,11 +6373,11 @@ static void RedrawAllInvisibleElementsForLenses()
       if (game.lenses_time_left > 0)
        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -6091,11 +6386,11 @@ static void RedrawAllInvisibleElementsForLenses()
       if (game.lenses_time_left == 0)
        Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
     }
   }
 }
@@ -6112,13 +6407,13 @@ static void RedrawAllInvisibleElementsForMagnifier()
        game.magnify_time_left > 0)
     {
       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
             game.magnify_time_left == 0)
     {
       Feld[x][y] = EL_EMC_FAKE_GRASS;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY(element) &&
             game.magnify_time_left > 0)
@@ -6130,7 +6425,7 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    IS_EMC_GATE_GRAY(element) ?
                    element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
                    element);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY_ACTIVE(element) &&
             game.magnify_time_left == 0)
@@ -6142,7 +6437,7 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    IS_EMC_GATE_GRAY_ACTIVE(element) ?
                    element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
                    element);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -6179,7 +6474,7 @@ static void ActivateTimegateSwitch(int x, int y)
     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
     {
       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     */
 
@@ -6221,7 +6516,7 @@ void Impact(int x, int y)
       RemoveMovingField(x, y + 1);
       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
       Feld[x][y + 2] = EL_ROCK;
-      DrawLevelField(x, y + 2);
+      TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
     }
@@ -6231,7 +6526,7 @@ void Impact(int x, int y)
       RemoveMovingField(x, y + 1);
       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
       Feld[x][y + 2] = EL_ROCK;
-      DrawLevelField(x, y + 2);
+      TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
     }
@@ -6255,7 +6550,7 @@ void Impact(int x, int y)
       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
   {
     ResetGfxAnimation(x, y);
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
   }
 
   if (impact && CAN_EXPLODE_IMPACT(element))
@@ -6729,7 +7024,7 @@ inline static void TurnRoundExt(int x, int y)
       {
        Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
        ResetGfxAnimation(move_x, move_y);
-       DrawLevelField(move_x, move_y);
+       TEST_DrawLevelField(move_x, move_y);
 
        MovDir[x][y] = back_dir;
       }
@@ -7346,10 +7641,18 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
       {
        if (!MovDelay[x][y])
+       {
          MovDelay[x][y] = TILEY + 1;
 
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
        if (MovDelay[x][y])
        {
+         DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
          MovDelay[x][y]--;
          if (MovDelay[x][y])
            return;
@@ -7360,6 +7663,33 @@ void StartMoving(int x, int y)
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 
+       PlayLevelSoundAction(x, y, ACTION_FILLING);
+      }
+      else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+      {
+       if (!MovDelay[x][y])
+       {
+         MovDelay[x][y] = TILEY + 1;
+
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
+       if (MovDelay[x][y])
+       {
+         DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_QUICKSAND_EMPTY;
+       Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+       Store[x][y + 1] = Store[x][y];
+       Store[x][y] = 0;
+
        PlayLevelSoundAction(x, y, ACTION_FILLING);
       }
     }
@@ -7383,10 +7713,18 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
       {
        if (!MovDelay[x][y])
+       {
          MovDelay[x][y] = TILEY + 1;
 
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
        if (MovDelay[x][y])
        {
+         DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
          MovDelay[x][y]--;
          if (MovDelay[x][y])
            return;
@@ -7397,6 +7735,33 @@ void StartMoving(int x, int y)
        Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 
+       PlayLevelSoundAction(x, y, ACTION_FILLING);
+      }
+      else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+      {
+       if (!MovDelay[x][y])
+       {
+         MovDelay[x][y] = TILEY + 1;
+
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
+       if (MovDelay[x][y])
+       {
+         DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
+         MovDelay[x][y]--;
+         if (MovDelay[x][y])
+           return;
+       }
+
+       Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
+       Feld[x][y + 1] = EL_QUICKSAND_FULL;
+       Store[x][y + 1] = Store[x][y];
+       Store[x][y] = 0;
+
        PlayLevelSoundAction(x, y, ACTION_FILLING);
       }
     }
@@ -7764,7 +8129,7 @@ void StartMoving(int x, int y)
                               element == EL_SP_SNIKSNAK ||
                               element == EL_SP_ELECTRON ||
                               element == EL_MOLE))
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
       }
     }
 
@@ -7798,7 +8163,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
 
@@ -7838,7 +8203,7 @@ void StartMoving(int x, int y)
 
            if (IN_SCR_FIELD(sx, sy))
            {
-             DrawLevelFieldCrumbledSand(xx, yy);
+             TEST_DrawLevelFieldCrumbledSand(xx, yy);
              DrawGraphic(sx, sy, flame_graphic, frame);
            }
          }
@@ -7846,7 +8211,7 @@ void StartMoving(int x, int y)
          {
            if (Feld[xx][yy] == EL_FLAMES)
              Feld[xx][yy] = EL_EMPTY;
-           DrawLevelField(xx, yy);
+           TEST_DrawLevelField(xx, yy);
          }
        }
       }
@@ -7889,7 +8254,7 @@ void StartMoving(int x, int y)
          Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
       {
        RemoveField(x, y);
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
        if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
@@ -7905,7 +8270,7 @@ void StartMoving(int x, int y)
       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
       {
        if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        else
          GfxDir[x][y] = MovDir[x][y] = MV_NONE;
       }
@@ -7916,7 +8281,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -7930,7 +8295,7 @@ void StartMoving(int x, int y)
        else
        {
          Feld[newx][newy] = EL_EMPTY;
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        }
 
        PlayLevelSound(x, y, SND_PIG_DIGGING);
@@ -7940,7 +8305,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -8019,7 +8384,7 @@ void StartMoving(int x, int y)
        else
        {
          Feld[newx][newy] = EL_EMPTY;
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
 
          PlayLevelSoundAction(x, y, ACTION_DIGGING);
        }
@@ -8030,7 +8395,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 #endif
 
        return;
@@ -8039,6 +8404,10 @@ void StartMoving(int x, int y)
     else if (IS_CUSTOM_ELEMENT(element) &&
             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
     {
+#if 1
+      if (!DigFieldByCE(newx, newy, element))
+       return;
+#else
       int new_element = Feld[newx][newy];
 
       if (!IS_FREE(newx, newy))
@@ -8067,7 +8436,7 @@ void StartMoving(int x, int y)
        else
        {
          RemoveField(newx, newy);
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        }
 
        /* if digged element was about to explode, prevent the explosion */
@@ -8077,6 +8446,7 @@ void StartMoving(int x, int y)
       }
 
       Store[newx][newy] = EL_EMPTY;
+
 #if 1
       /* this makes it possible to leave the removed element again */
       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
@@ -8090,6 +8460,8 @@ void StartMoving(int x, int y)
        Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
                             new_element : move_leave_element);
       }
+#endif
+
 #endif
 
       if (move_pattern & MV_MAZE_RUNNER_STYLE)
@@ -8105,7 +8477,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -8132,7 +8504,7 @@ void StartMoving(int x, int y)
          if (IS_PLAYER(x, y))
            DrawPlayerField(x, y);
          else
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
 
          PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
 
@@ -8170,7 +8542,7 @@ void StartMoving(int x, int y)
       else
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
@@ -8201,7 +8573,7 @@ void StartMoving(int x, int y)
       else
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
@@ -8224,7 +8596,7 @@ void StartMoving(int x, int y)
 
        ResetGfxAnimation(x, y);
        GfxAction[x][y] = ACTION_DIGGING;
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
 
@@ -8233,7 +8605,7 @@ void StartMoving(int x, int y)
       else     /* element == EL_PACMAN */
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
        PlayLevelSound(x, y, SND_PACMAN_DIGGING);
       }
     }
@@ -8315,7 +8687,7 @@ void ContinueMoving(int x, int y)
           GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
 #endif
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     return;    /* element is still moving */
   }
@@ -8334,7 +8706,7 @@ void ContinueMoving(int x, int y)
   {
     Feld[x][y] = EL_SAND;
 
-    DrawLevelFieldCrumbledSandNeighbours(x, y);
+    TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
   }
   else if (element == EL_QUICKSAND_FILLING)
   {
@@ -8443,7 +8815,7 @@ void ContinueMoving(int x, int y)
   }
 
 #if USE_NEW_CUSTOM_VALUE
-    CustomValue[newx][newy] = CustomValue[x][y];
+  CustomValue[newx][newy] = CustomValue[x][y];
 #endif
 
   ChangeDelay[x][y] = 0;
@@ -8501,7 +8873,7 @@ void ContinueMoving(int x, int y)
     InitField(x, y, FALSE);
 
     if (GFX_CRUMBLED(Feld[x][y]))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
 
     if (ELEM_IS_PLAYER(move_leave_element))
       RelocatePlayer(x, y, move_leave_element);
@@ -8517,8 +8889,8 @@ void ContinueMoving(int x, int y)
        element_info[element].move_pattern == MV_WHEN_DROPPED)))
     GfxDir[x][y] = MovDir[newx][newy] = 0;
 
-  DrawLevelField(x, y);
-  DrawLevelField(newx, newy);
+  TEST_DrawLevelField(x, y);
+  TEST_DrawLevelField(newx, newy);
 
   Stop[newx][newy] = TRUE;     /* ignore this element until the next frame */
 
@@ -8780,7 +9152,7 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
       AmoebaNr[x][y] = 0;
       Feld[x][y] = new_element;
       InitField(x, y, FALSE);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
       done = TRUE;
     }
   }
@@ -8822,7 +9194,7 @@ void AmoebeWaechst(int x, int y)
     {
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -8854,7 +9226,7 @@ void AmoebaDisappearing(int x, int y)
     if (!MovDelay[x][y])
     {
       Feld[x][y] = EL_EMPTY;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* don't let mole enter this field in this cycle;
         (give priority to objects falling to this field from above) */
@@ -8881,7 +9253,7 @@ void AmoebeAbleger(int ax, int ay)
   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
   {
     Feld[ax][ay] = EL_AMOEBA_DEAD;
-    DrawLevelField(ax, ay);
+    TEST_DrawLevelField(ax, ay);
     return;
   }
 
@@ -8951,7 +9323,7 @@ void AmoebeAbleger(int ax, int ay)
       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
       {
        Feld[ax][ay] = EL_AMOEBA_DEAD;
-       DrawLevelField(ax, ay);
+       TEST_DrawLevelField(ax, ay);
        AmoebaCnt[AmoebaNr[ax][ay]]--;
 
        if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
@@ -9015,7 +9387,7 @@ void AmoebeAbleger(int ax, int ay)
     return;
   }
 
-  DrawLevelField(newax, neway);
+  TEST_DrawLevelField(newax, neway);
 }
 
 void Life(int ax, int ay)
@@ -9073,7 +9445,7 @@ void Life(int ax, int ay)
       {
        Feld[xx][yy] = EL_EMPTY;
        if (!Stop[xx][yy])
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
@@ -9086,7 +9458,7 @@ void Life(int ax, int ay)
        Feld[xx][yy] = element;
        MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
        if (!Stop[xx][yy])
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
@@ -9314,9 +9686,6 @@ void DrawTwinkleOnField(int x, int y)
   {
     MovDelay[x][y]--;
 
-    if (setup.direct_draw && MovDelay[x][y])
-      SetDrawtoField(DRAW_BUFFERED);
-
     DrawLevelElementAnimation(x, y, Feld[x][y]);
 
     if (MovDelay[x][y] != 0)
@@ -9325,18 +9694,6 @@ void DrawTwinkleOnField(int x, int y)
                                           10 - MovDelay[x][y]);
 
       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
-
-      if (setup.direct_draw)
-      {
-       int dest_x, dest_y;
-
-       dest_x = FX + SCREENX(x) * TILEX;
-       dest_y = FY + SCREENY(y) * TILEY;
-
-       BlitBitmap(drawto_field, window,
-                  dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
-       SetDrawtoField(DRAW_DIRECT);
-      }
     }
   }
 }
@@ -9365,28 +9722,28 @@ void MauerWaechst(int x, int y)
       if (MovDir[x][y] == MV_LEFT)
       {
        if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
-         DrawLevelField(x - 1, y);
+         TEST_DrawLevelField(x - 1, y);
       }
       else if (MovDir[x][y] == MV_RIGHT)
       {
        if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
-         DrawLevelField(x + 1, y);
+         TEST_DrawLevelField(x + 1, y);
       }
       else if (MovDir[x][y] == MV_UP)
       {
        if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
-         DrawLevelField(x, y - 1);
+         TEST_DrawLevelField(x, y - 1);
       }
       else
       {
        if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
-         DrawLevelField(x, y + 1);
+         TEST_DrawLevelField(x, y + 1);
       }
 
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -9477,7 +9834,7 @@ void MauerAbleger(int ax, int ay)
   }
 
   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
-    DrawLevelField(ax, ay);
+    TEST_DrawLevelField(ax, ay);
 
   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
     oben_massiv = TRUE;
@@ -9595,7 +9952,7 @@ void MauerAblegerStahl(int ax, int ay)
        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
       ((links_massiv && rechts_massiv) ||
        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
-    Feld[ax][ay] = EL_WALL;
+    Feld[ax][ay] = EL_STEELWALL;
 
   if (new_wall)
     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
@@ -9641,7 +9998,7 @@ void CheckForDragon(int x, int y)
        if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
        {
          Feld[xx][yy] = EL_EMPTY;
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        }
        else
          break;
@@ -9705,7 +10062,7 @@ static void ChangeActiveTrap(int x, int y)
 
   /* if new animation frame was drawn, correct crumbled sand border */
   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
-    DrawLevelFieldCrumbledSand(x, y);
+    TEST_DrawLevelFieldCrumbledSand(x, y);
 }
 
 static int getSpecialActionElement(int element, int number, int base_element)
@@ -9830,11 +10187,15 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                            action_mode, action_arg_number,
                            action_arg_number_min, action_arg_number_max);
 
+#if 1
+  int trigger_player_bits = change->actual_trigger_player_bits;
+#else
   int trigger_player_bits =
     (change->actual_trigger_player >= EL_PLAYER_1 &&
      change->actual_trigger_player <= EL_PLAYER_4 ?
      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
      PLAYER_BITS_ANY);
+#endif
 
   int action_arg_player_bits =
     (action_arg >= CA_ARG_PLAYER_1 &&
@@ -10256,10 +10617,10 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
     ResetRandomAnimationValue(x, y);
 #endif
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     if (GFX_CRUMBLED(new_element))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
   }
 
 #if 1
@@ -10340,6 +10701,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
     /* reset actual trigger element, trigger player and action element */
     change->actual_trigger_element = EL_EMPTY;
     change->actual_trigger_player = EL_PLAYER_1;
+    change->actual_trigger_player_bits = CH_PLAYER_1;
     change->actual_trigger_side = CH_SIDE_NONE;
     change->actual_trigger_ce_value = 0;
     change->actual_trigger_ce_score = 0;
@@ -10720,7 +11082,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
       {
        change->actual_trigger_element = trigger_element;
-       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+       change->actual_trigger_player_bits = trigger_player;
        change->actual_trigger_side = trigger_side;
        change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
        change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
@@ -10839,7 +11202,8 @@ static boolean CheckElementChangeExt(int x, int y,
         IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
     {
       change->actual_trigger_element = trigger_element;
-      change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+      change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+      change->actual_trigger_player_bits = trigger_player;
       change->actual_trigger_side = trigger_side;
       change->actual_trigger_ce_value = CustomValue[x][y];
       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
@@ -11230,8 +11594,12 @@ static void CheckLevelTime()
       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
   }
 
+#if 1
+  UpdateAndDisplayGameControlValues();
+#else
   UpdateGameDoorValues();
   DrawGameDoorValues();
+#endif
 }
 
 void AdvanceFrameAndPlayerCounters(int player_nr)
@@ -11620,7 +11988,7 @@ void GameActions_RND()
       if (MovDelay[x][y] <= 0)
       {
        RemoveField(x, y);
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        TestIfElementTouchesCustomElement(x, y);        /* for empty space */
       }
@@ -11654,7 +12022,7 @@ void GameActions_RND()
     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
     {
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
 
 #if DEBUG
@@ -11789,7 +12157,7 @@ void GameActions_RND()
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       DrawTwinkleOnField(x, y);
+       TEST_DrawTwinkleOnField(x, y);
     }
     else if (IS_MOVING(x, y))
       ContinueMoving(x, y);
@@ -11924,7 +12292,7 @@ void GameActions_RND()
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       DrawTwinkleOnField(x, y);
+       TEST_DrawTwinkleOnField(x, y);
     }
     else if ((element == EL_ACID ||
              element == EL_EXIT_OPEN ||
@@ -12109,19 +12477,19 @@ void GameActions_RND()
              element == EL_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
          else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
                   element == EL_BD_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
          else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
                   element == EL_DC_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
        }
 
@@ -12175,6 +12543,33 @@ void GameActions_RND()
     }
   }
 
+#if USE_DELAYED_GFX_REDRAW
+  SCAN_PLAYFIELD(x, y)
+  {
+#if 1
+    if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
+#else
+    if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
+       GfxRedraw[x][y] != GFX_REDRAW_NONE)
+#endif
+    {
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
+       DrawLevelField(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
+       DrawLevelFieldCrumbledSand(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
+       DrawLevelFieldCrumbledSandNeighbours(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
+       DrawTwinkleOnField(x, y);
+    }
+
+    GfxRedraw[x][y] = GFX_REDRAW_NONE;
+  }
+#endif
+
   CheckLevelTime();
 
   DrawAllPlayers();
@@ -12297,7 +12692,7 @@ void ScrollLevel(int dx, int dy)
 
 #else
 
-#if 1
+#if 0
   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
   int xsize = (BX2 - BX1 + 1);
   int ysize = (BY2 - BY1 + 1);
@@ -12732,7 +13127,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     else if (old_jx == jx && old_jy != jy)
       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
 
-    DrawLevelField(jx, jy);    /* for "crumbled sand" */
+    TEST_DrawLevelField(jx, jy);       /* for "crumbled sand" */
 
     player->last_move_dir = player->MovDir;
     player->is_moving = TRUE;
@@ -13635,7 +14030,7 @@ void RemovePlayer(struct PlayerInfo *player)
     StorePlayer[jx][jy] = 0;
 
   if (player->is_moving)
-    DrawLevelField(player->last_jx, player->last_jy);
+    TEST_DrawLevelField(player->last_jx, player->last_jy);
 
   for (i = 0; i < MAX_PLAYERS; i++)
     if (stored_player[i].active)
@@ -13706,9 +14101,9 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player,
   =============================================================================
 */
 
-int DigField(struct PlayerInfo *player,
-            int oldx, int oldy, int x, int y,
-            int real_dx, int real_dy, int mode)
+static int DigField(struct PlayerInfo *player,
+                   int oldx, int oldy, int x, int y,
+                   int real_dx, int real_dy, int mode)
 {
   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
   boolean player_was_pushing = player->is_pushing;
@@ -14187,8 +14582,10 @@ int DigField(struct PlayerInfo *player,
 
     if (!(IN_LEV_FIELD(nextx, nexty) &&
          (IS_FREE(nextx, nexty) ||
-          (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
-           IS_SB_ELEMENT(element)))))
+          (IS_SB_ELEMENT(element) &&
+           Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
+          (IS_CUSTOM_ELEMENT(element) &&
+           CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
       return MP_NO_ACTION;
 
     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
@@ -14208,6 +14605,13 @@ int DigField(struct PlayerInfo *player,
       return MP_NO_ACTION;
     }
 
+    if (IS_CUSTOM_ELEMENT(element) &&
+       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
+    {
+      if (!DigFieldByCE(nextx, nexty, element))
+       return MP_NO_ACTION;
+    }
+
     if (IS_SB_ELEMENT(element))
     {
       if (element == EL_SOKOBAN_FIELD_FULL)
@@ -14293,7 +14697,7 @@ int DigField(struct PlayerInfo *player,
 
       game.robot_wheel_active = TRUE;
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_SP_TERMINAL)
     {
@@ -14348,7 +14752,7 @@ int DigField(struct PlayerInfo *player,
       local_player->lights_still_needed--;
 
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_TIME_ORB_FULL)
     {
@@ -14368,7 +14772,7 @@ int DigField(struct PlayerInfo *player,
       }
 
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
             element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
@@ -14450,7 +14854,66 @@ int DigField(struct PlayerInfo *player,
   return MP_MOVING;
 }
 
-boolean SnapField(struct PlayerInfo *player, int dx, int dy)
+static boolean DigFieldByCE(int x, int y, int digging_element)
+{
+  int element = Feld[x][y];
+
+  if (!IS_FREE(x, y))
+  {
+    int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
+                 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
+                 ACTION_BREAKING);
+
+    /* no element can dig solid indestructible elements */
+    if (IS_INDESTRUCTIBLE(element) &&
+       !IS_DIGGABLE(element) &&
+       !IS_COLLECTIBLE(element))
+      return FALSE;
+
+    if (AmoebaNr[x][y] &&
+       (element == EL_AMOEBA_FULL ||
+        element == EL_BD_AMOEBA ||
+        element == EL_AMOEBA_GROWING))
+    {
+      AmoebaCnt[AmoebaNr[x][y]]--;
+      AmoebaCnt2[AmoebaNr[x][y]]--;
+    }
+
+    if (IS_MOVING(x, y))
+      RemoveMovingField(x, y);
+    else
+    {
+      RemoveField(x, y);
+      TEST_DrawLevelField(x, y);
+    }
+
+    /* if digged element was about to explode, prevent the explosion */
+    ExplodeField[x][y] = EX_TYPE_NONE;
+
+    PlayLevelSoundAction(x, y, action);
+  }
+
+  Store[x][y] = EL_EMPTY;
+
+#if 1
+  /* this makes it possible to leave the removed element again */
+  if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
+    Store[x][y] = element;
+#else
+  if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
+  {
+    int move_leave_element = element_info[digging_element].move_leave_element;
+
+    /* this makes it possible to leave the removed element again */
+    Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
+                  element : move_leave_element);
+  }
+#endif
+
+  return TRUE;
+}
+
+static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 {
   int jx = player->jx, jy = player->jy;
   int x = jx + dx, y = jy + dy;
@@ -14523,14 +14986,14 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   }
 
   if (player->MovPos != 0)     /* prevent graphic bugs in versions < 2.2.0 */
-    DrawLevelField(player->last_jx, player->last_jy);
+    TEST_DrawLevelField(player->last_jx, player->last_jy);
 
-  DrawLevelField(x, y);
+  TEST_DrawLevelField(x, y);
 
   return TRUE;
 }
 
-boolean DropElement(struct PlayerInfo *player)
+static boolean DropElement(struct PlayerInfo *player)
 {
   int old_element, new_element;
   int dropx = player->jx, dropy = player->jy;