rnd-20050525-1-src
[rocksndiamonds.git] / src / game.c
index 2731d0ef9cd301832ead0a7081d430670ad2ce63..bac83725cb81b334bd9511d71ef56a865744b23d 100644 (file)
 /* EXPERIMENTAL STUFF */
 #define USE_NEW_AMOEBA_CODE    FALSE
 
+/* EXPERIMENTAL STUFF */
+#define USE_NEW_STUFF          (TRUE                           * 1)
+
+#define USE_NEW_MOVE_STYLE     (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_MOVE_DELAY     (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_PUSH_DELAY     (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_BLOCK_STYLE    (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_SP_SLIPPERY    (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_RANDOMIZE      (TRUE   * USE_NEW_STUFF         * 1)
+
+#define USE_PUSH_BUGFIX                (TRUE   * USE_NEW_STUFF         * 1)
+
+#define USE_CAN_MOVE_NOT_MOVING        (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_PREVIOUS_MOVE_DIR  (TRUE   * USE_NEW_STUFF         * 1)
+
+
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
 
 /* for Explode() */
 #define EX_PHASE_START         0
-#define EX_NO_EXPLOSION                0
-#define EX_NORMAL              1
-#define EX_CENTER              2
-#define EX_BORDER              3
+#define EX_TYPE_NONE           0
+#define EX_TYPE_NORMAL         (1 << 0)
+#define EX_TYPE_CENTER         (1 << 1)
+#define EX_TYPE_BORDER         (1 << 2)
+#define EX_TYPE_CROSS          (1 << 3)
+#define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
 /* special positions in the game control window (relative to control window) */
 #define XX_LEVEL               37
 /* values for other actions */
 #define MOVE_STEPSIZE_NORMAL   (TILEX / MOVE_DELAY_NORMAL_SPEED)
 
+#define GET_DX_FROM_DIR(d)     ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
+#define GET_DY_FROM_DIR(d)     ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
+
 #define        INIT_GFX_RANDOM()       (SimpleRND(1000000))
 
 #define GET_NEW_PUSH_DELAY(e)  (   (element_info[e].push_delay_fixed) + \
                                 RND(element_info[e].push_delay_random))
+#define GET_NEW_DROP_DELAY(e)  (   (element_info[e].drop_delay_fixed) + \
+                                RND(element_info[e].drop_delay_random))
 #define GET_NEW_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
                                 RND(element_info[e].move_delay_random))
 #define GET_MAX_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
        ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
         (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
 
+#define GET_VALID_PLAYER_ELEMENT(e)                                    \
+       ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
+
+#define CAN_GROW_INTO(e)                                               \
+       ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
+
 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                        \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
                                        (condition)))
 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
 
+#define ACCESS_FROM(e, d)              (element_info[e].access_direction &(d))
+#define IS_WALKABLE_FROM(e, d)         (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
+#define IS_PASSABLE_FROM(e, d)         (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
+#define IS_ACCESSIBLE_FROM(e, d)       (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
+
 /* game button identifiers */
 #define GAME_CTRL_ID_STOP              0
 #define GAME_CTRL_ID_PAUSE             1
 
 /* forward declaration for internal use */
 
+static void AdvanceFrameAndPlayerCounters(int);
+
 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
 static boolean MovePlayer(struct PlayerInfo *, int, int);
 static void ScrollPlayer(struct PlayerInfo *, int);
@@ -301,22 +337,22 @@ static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
 #define CheckTriggeredElementChange(x, y, e, ev)                       \
        CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
                                       CH_SIDE_ANY, -1)
-#define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s)           \
+#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)         \
        CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
-#define CheckTriggeredElementChangeSide(x, y, e, ev, s)                        \
+#define CheckTriggeredElementChangeBySide(x, y, e, ev, s)              \
        CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
-#define CheckTriggeredElementChangePage(x, y, e, ev, p)                        \
+#define CheckTriggeredElementChangeByPage(x, y, e, ev, p)              \
        CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
                                       CH_SIDE_ANY, p)
 
 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
 #define CheckElementChange(x, y, e, te, ev)                            \
        CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
-#define CheckElementChangePlayer(x, y, e, ev, p, s)                    \
+#define CheckElementChangeByPlayer(x, y, e, ev, p, s)                  \
        CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
-#define CheckElementChangeSide(x, y, e, te, ev, s)                     \
+#define CheckElementChangeBySide(x, y, e, te, ev, s)                   \
        CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
-#define CheckElementChangePage(x, y, e, te, ev, p)                     \
+#define CheckElementChangeByPage(x, y, e, te, ev, p)                   \
        CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
 
 static void PlayLevelSound(int, int, int);
@@ -592,21 +628,41 @@ struct
   int element;
   int direction;
 }
-tube_access[] =
-{
-  { EL_TUBE_ANY,               MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
-  { EL_TUBE_VERTICAL,                               MV_UP | MV_DOWN },
-  { EL_TUBE_HORIZONTAL,                MV_LEFT | MV_RIGHT                   },
-  { EL_TUBE_VERTICAL_LEFT,     MV_LEFT |            MV_UP | MV_DOWN },
-  { EL_TUBE_VERTICAL_RIGHT,              MV_RIGHT | MV_UP | MV_DOWN },
-  { EL_TUBE_HORIZONTAL_UP,     MV_LEFT | MV_RIGHT | MV_UP           },
-  { EL_TUBE_HORIZONTAL_DOWN,   MV_LEFT | MV_RIGHT |         MV_DOWN },
-  { EL_TUBE_LEFT_UP,           MV_LEFT |            MV_UP           },
-  { EL_TUBE_LEFT_DOWN,         MV_LEFT |                    MV_DOWN },
-  { EL_TUBE_RIGHT_UP,                    MV_RIGHT | MV_UP           },
-  { EL_TUBE_RIGHT_DOWN,                          MV_RIGHT |         MV_DOWN },
-
-  { EL_UNDEFINED,              0                                    }
+access_direction_list[] =
+{
+  { EL_TUBE_ANY,                       MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_TUBE_VERTICAL,                                       MV_UP | MV_DOWN },
+  { EL_TUBE_HORIZONTAL,                        MV_LEFT | MV_RIGHT                   },
+  { EL_TUBE_VERTICAL_LEFT,             MV_LEFT |            MV_UP | MV_DOWN },
+  { EL_TUBE_VERTICAL_RIGHT,                      MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_TUBE_HORIZONTAL_UP,             MV_LEFT | MV_RIGHT | MV_UP           },
+  { EL_TUBE_HORIZONTAL_DOWN,           MV_LEFT | MV_RIGHT |         MV_DOWN },
+  { EL_TUBE_LEFT_UP,                   MV_LEFT |            MV_UP           },
+  { EL_TUBE_LEFT_DOWN,                 MV_LEFT |                    MV_DOWN },
+  { EL_TUBE_RIGHT_UP,                            MV_RIGHT | MV_UP           },
+  { EL_TUBE_RIGHT_DOWN,                                  MV_RIGHT |         MV_DOWN },
+
+  { EL_SP_PORT_LEFT,                             MV_RIGHT                   },
+  { EL_SP_PORT_RIGHT,                  MV_LEFT                              },
+  { EL_SP_PORT_UP,                                                  MV_DOWN },
+  { EL_SP_PORT_DOWN,                                        MV_UP           },
+  { EL_SP_PORT_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
+  { EL_SP_PORT_VERTICAL,                                    MV_UP | MV_DOWN },
+  { EL_SP_PORT_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+  { EL_SP_GRAVITY_PORT_LEFT,                     MV_RIGHT                   },
+  { EL_SP_GRAVITY_PORT_RIGHT,          MV_LEFT                              },
+  { EL_SP_GRAVITY_PORT_UP,                                          MV_DOWN },
+  { EL_SP_GRAVITY_PORT_DOWN,                                MV_UP           },
+  { EL_SP_GRAVITY_ON_PORT_LEFT,                          MV_RIGHT                   },
+  { EL_SP_GRAVITY_ON_PORT_RIGHT,       MV_LEFT                              },
+  { EL_SP_GRAVITY_ON_PORT_UP,                                       MV_DOWN },
+  { EL_SP_GRAVITY_ON_PORT_DOWN,                                     MV_UP           },
+  { EL_SP_GRAVITY_OFF_PORT_LEFT,                 MV_RIGHT                   },
+  { EL_SP_GRAVITY_OFF_PORT_RIGHT,      MV_LEFT                              },
+  { EL_SP_GRAVITY_OFF_PORT_UP,                                      MV_DOWN },
+  { EL_SP_GRAVITY_OFF_PORT_DOWN,                            MV_UP           },
+
+  { EL_UNDEFINED,                      MV_NO_MOVING                         }
 };
 
 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
@@ -723,6 +779,24 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
                                level.sp_block_last_field :
                                level.block_last_field);
 
+#if USE_NEW_BLOCK_STYLE
+#if 1
+    player->block_delay = (player->block_last_field ?
+                          (element == EL_SP_MURPHY ?
+                           level.sp_block_delay :
+                           level.block_delay) : 0);
+#else
+    player->block_delay = (element == EL_SP_MURPHY ?
+                          (player->block_last_field ? 7 : 1) :
+                          (player->block_last_field ? 7 : 1));
+#endif
+
+#if 0
+    printf("::: block_last_field == %d, block_delay = %d\n",
+          player->block_last_field, player->block_delay);
+#endif
+#endif
+
     if (!options.network || player->connected)
     {
       player->active = TRUE;
@@ -743,6 +817,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
     }
 
     Feld[x][y] = EL_EMPTY;
+
     player->jx = player->last_jx = x;
     player->jy = player->last_jy = y;
   }
@@ -975,9 +1050,10 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
   /* 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)
-
- */
+     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"
+  */
 }
 
 inline void DrawGameValue_Emeralds(int value)
@@ -990,12 +1066,13 @@ inline void DrawGameValue_Dynamite(int value)
   DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
 }
 
-inline void DrawGameValue_Keys(struct PlayerInfo *player)
+inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
 {
   int i;
 
-  for (i = 0; i < MAX_KEYS; i++)
-    if (player->key[i])
+  /* currently only 4 of 8 possible keys are displayed */
+  for (i = 0; i < STD_NUM_KEYS; i++)
+    if (key[i])
       DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
                         el2edimg(EL_KEY_1 + i));
 }
@@ -1037,19 +1114,45 @@ inline void DrawGameValue_Level(int value)
   }
 }
 
-void DrawGameDoorValues()
+void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
+                      int key_bits)
 {
+  int key[MAX_NUM_KEYS];
   int i;
 
+  for (i = 0; i < MAX_NUM_KEYS; i++)
+    key[i] = key_bits & (1 << i);
+
   DrawGameValue_Level(level_nr);
 
-  for (i = 0; i < MAX_PLAYERS; i++)
-    DrawGameValue_Keys(&stored_player[i]);
+  DrawGameValue_Emeralds(emeralds);
+  DrawGameValue_Dynamite(dynamite);
+  DrawGameValue_Score(score);
+  DrawGameValue_Time(time);
+
+  DrawGameValue_Keys(key);
+}
+
+void DrawGameDoorValues()
+{
+  int i;
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+  {
+    DrawGameDoorValues_EM();
+
+    return;
+  }
+
+  DrawGameValue_Level(level_nr);
 
   DrawGameValue_Emeralds(local_player->gems_still_needed);
   DrawGameValue_Dynamite(local_player->inventory_size);
   DrawGameValue_Score(local_player->score);
   DrawGameValue_Time(TimeLeft);
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    DrawGameValue_Keys(stored_player[i].key);
 }
 
 static void resolve_group_element(int group_element, int recursion_depth)
@@ -1124,6 +1227,40 @@ static void InitGameEngine()
   game.engine_version = (tape.playing ? tape.engine_version :
                         level.game_version);
 
+  /* ---------------------------------------------------------------------- */
+  /* set flags for bugs and changes according to active game engine version */
+  /* ---------------------------------------------------------------------- */
+
+  /*
+    Type of bug/change:
+    Before 3.1.0, custom elements that "change when pushing" changed directly
+    after the player started pushing them (until then handled in "DigField()").
+    Since 3.1.0, these custom elements are not changed until the "pushing"
+    move of the element is finished (now handled in "ContinueMoving()").
+
+    Affected levels/tapes:
+    The first condition is generally needed for all levels/tapes before version
+    3.1.0, which might use the old behaviour before it was changed; known tapes
+    that are affected are some tapes from the level set "Walpurgis Gardens" by
+    Jamie Cullen.
+    The second condition is an exception from the above case and is needed for
+    the special case of tapes recorded with game (not engine!) version 3.1.0 or
+    above (including some development versions of 3.1.0), but before it was
+    known that this change would break tapes like the above and was fixed in
+    3.1.1, so that the changed behaviour was active although the engine version
+    while recording maybe was before 3.1.0. There is at least one tape that is
+    affected by this exception, which is the tape for the one-level set "Bug
+    Machine" by Juergen Bonhagen.
+  */
+
+  game.use_bug_change_when_pushing =
+    (game.engine_version < VERSION_IDENT(3,1,0,0) &&
+     !(tape.playing &&
+       tape.game_version >= VERSION_IDENT(3,1,0,0) &&
+       tape.game_version <  VERSION_IDENT(3,1,1,0)));
+
+  /* ---------------------------------------------------------------------- */
+
   /* dynamically adjust element properties according to game engine version */
   InitElementPropertiesEngine(game.engine_version);
 
@@ -1146,6 +1283,15 @@ static void InitGameEngine()
 
   /* ---------- initialize player's initial move delay --------------------- */
 
+#if USE_NEW_MOVE_DELAY
+  /* dynamically adjust player properties according to level information */
+  game.initial_move_delay_value =
+    (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+
+  /* dynamically adjust player properties according to game engine version */
+  game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
+                            game.initial_move_delay_value : 0);
+#else
   /* dynamically adjust player properties according to game engine version */
   game.initial_move_delay =
     (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
@@ -1154,6 +1300,7 @@ static void InitGameEngine()
   /* dynamically adjust player properties according to level information */
   game.initial_move_delay_value =
     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+#endif
 
   /* ---------- initialize player's initial push delay --------------------- */
 
@@ -1201,6 +1348,10 @@ static void InitGameEngine()
     ei->change->post_change_function = ch_delay->post_change_function;
 
     ei->change_events |= CH_EVENT_BIT(CE_DELAY);
+
+#if 1
+    SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
+#endif
   }
 
 #if 1
@@ -1324,8 +1475,16 @@ static void InitGameEngine()
     {
       if (IS_SP_ELEMENT(i))
       {
+#if USE_NEW_MOVE_STYLE
+       /* set SP push delay to just enough to push under a falling zonk */
+       int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
+
+       element_info[i].push_delay_fixed  = delay;
+       element_info[i].push_delay_random = 0;
+#else
        element_info[i].push_delay_fixed  = 6;  /* just enough to escape ... */
        element_info[i].push_delay_random = 0;  /* ... from falling zonk     */
+#endif
       }
     }
   }
@@ -1345,6 +1504,7 @@ static void InitGameEngine()
     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
   }
 
+#if 0
   /* ---------- initialize move dig/leave ---------------------------------- */
 
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
@@ -1352,6 +1512,7 @@ static void InitGameEngine()
     element_info[i].can_leave_element = FALSE;
     element_info[i].can_leave_element_last = FALSE;
   }
+#endif
 
   /* ---------- initialize gem count --------------------------------------- */
 
@@ -1367,15 +1528,15 @@ static void InitGameEngine()
 
   /* ---------- initialize access direction -------------------------------- */
 
-  /* initialize access direction values to default */
+  /* initialize access direction values to default (access from every side) */
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     if (!IS_CUSTOM_ELEMENT(i))
       element_info[i].access_direction = MV_ALL_DIRECTIONS;
 
   /* set access direction value for certain elements from pre-defined list */
-  for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
-    element_info[tube_access[i].element].access_direction =
-      tube_access[i].direction;
+  for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
+    element_info[access_direction_list[i].element].access_direction =
+      access_direction_list[i].direction;
 }
 
 
@@ -1430,7 +1591,7 @@ void InitGame()
     player->lights_still_needed = 0;
     player->friends_still_needed = 0;
 
-    for (j = 0; j < MAX_KEYS; j++)
+    for (j = 0; j < MAX_NUM_KEYS; j++)
       player->key[j] = FALSE;
 
     player->dynabomb_count = 0;
@@ -1448,7 +1609,9 @@ void InitGame()
 
     player->use_murphy_graphic = FALSE;
 
-    player->block_last_field = FALSE;
+    player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
+    player->block_delay = -1;          /* initialized in InitPlayerField() */
+
     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
 
     player->actual_frame_counter = 0;
@@ -1459,6 +1622,7 @@ void InitGame()
 
     player->is_waiting = FALSE;
     player->is_moving = FALSE;
+    player->is_auto_moving = FALSE;
     player->is_digging = FALSE;
     player->is_snapping = FALSE;
     player->is_collecting = FALSE;
@@ -1523,8 +1687,13 @@ void InitGame()
 
     player->move_delay_reset_counter = 0;
 
-    player->push_delay = 0;
+#if USE_NEW_PUSH_DELAY
+    player->push_delay       = -1;     /* initialized when pushing starts */
+    player->push_delay_value = game.initial_push_delay_value;
+#else
+    player->push_delay       = 0;
     player->push_delay_value = game.initial_push_delay_value;
+#endif
 
     player->drop_delay = 0;
 
@@ -1546,13 +1715,14 @@ void InitGame()
 
   network_player_action_received = FALSE;
 
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
   /* initial null action */
   if (network_playing)
     SendToServer_MovePlayer(MV_NO_MOVING);
 #endif
 
   ZX = ZY = -1;
+  ExitX = ExitY = -1;
 
   FrameCounter = 0;
   TimeFrames = 0;
@@ -1601,6 +1771,7 @@ void InitGame()
       AmoebaNr[x][y] = 0;
       WasJustMoving[x][y] = 0;
       WasJustFalling[x][y] = 0;
+      CheckCollision[x][y] = 0;
       Stop[x][y] = FALSE;
       Pushed[x][y] = FALSE;
 
@@ -1609,7 +1780,7 @@ void InitGame()
 
       ExplodePhase[x][y] = 0;
       ExplodeDelay[x][y] = 0;
-      ExplodeField[x][y] = EX_NO_EXPLOSION;
+      ExplodeField[x][y] = EX_TYPE_NONE;
 
       RunnerVisit[x][y] = 0;
       PlayerVisit[x][y] = 0;
@@ -1649,7 +1820,7 @@ void InitGame()
     if (!IS_CUSTOM_ELEMENT(i))
     {
       int num_phase = 8;
-      int delay = ((IS_SP_ELEMENT(i) &&
+      int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
                    game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
                   game.emulation == EMU_SUPAPLEX ? 3 : 2);
       int last_phase = (num_phase + 1) * delay;
@@ -1706,6 +1877,11 @@ void InitGame()
          player->element_nr = some_player->element_nr;
 #endif
 
+#if USE_NEW_BLOCK_STYLE
+         player->block_last_field = some_player->block_last_field;
+         player->block_delay = some_player->block_delay;
+#endif
+
          StorePlayer[jx][jy] = player->element_nr;
          player->jx = player->last_jx = jx;
          player->jy = player->last_jy = jy;
@@ -1908,18 +2084,27 @@ void InitGame()
 
   CloseDoor(DOOR_CLOSE_1);
 
-  DrawLevel();
-  DrawAllPlayers();
+  /* !!! FIX THIS (START) !!! */
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+  {
+    InitGameEngine_EM();
+  }
+  else
+  {
+    DrawLevel();
+    DrawAllPlayers();
 
-  /* after drawing the level, correct some elements */
-  if (game.timegate_time_left == 0)
-    CloseAllOpenTimegates();
+    /* after drawing the level, correct some elements */
+    if (game.timegate_time_left == 0)
+      CloseAllOpenTimegates();
 
-  if (setup.soft_scrolling)
-    BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+    if (setup.soft_scrolling)
+      BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
 
-  redraw_mask |= REDRAW_FROM_BACKBUFFER;
-  FadeToFront();
+    redraw_mask |= REDRAW_FROM_BACKBUFFER;
+    FadeToFront();
+  }
+  /* !!! FIX THIS (END) !!! */
 
   /* copy default game door content to main double buffer */
   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
@@ -1960,6 +2145,15 @@ void InitGame()
 #endif
 }
 
+void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
+{
+  /* this is used for non-R'n'D game engines to update certain engine values */
+
+  /* needed to determine if sounds are played within the visible screen area */
+  scroll_x = actual_scroll_x;
+  scroll_y = actual_scroll_y;
+}
+
 void InitMovDir(int x, int y)
 {
   int i, element = Feld[x][y];
@@ -2070,6 +2264,12 @@ void InitMovDir(int x, int y)
        else if (move_pattern == MV_ALONG_LEFT_SIDE ||
                 move_pattern == MV_ALONG_RIGHT_SIDE)
        {
+#if 1
+         /* use random direction as default start direction */
+         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
+           MovDir[x][y] = 1 << RND(4);
+#endif
+
          for (i = 0; i < NUM_DIRECTIONS; i++)
          {
            int x1 = x + xy[i][0];
@@ -2224,8 +2424,9 @@ void GameWon()
   }
 
   /* close exit door after last player */
-  if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
-       Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
+  if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
+      (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+       Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
   {
     int element = Feld[ExitX][ExitY];
 
@@ -2236,7 +2437,9 @@ void GameWon()
   }
 
   /* Hero disappears */
-  DrawLevelField(ExitX, ExitY);
+  if (ExitX >= 0 && ExitY >= 0)
+    DrawLevelField(ExitX, ExitY);
+
   BackToFront();
 
   if (tape.playing)
@@ -2345,6 +2548,37 @@ int NewHiScore()
   return position;
 }
 
+inline static int getElementMoveStepsize(int x, int y)
+{
+  int element = Feld[x][y];
+  int direction = MovDir[x][y];
+  int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+  int horiz_move = (dx != 0);
+  int sign = (horiz_move ? dx : dy);
+  int step = sign * element_info[element].move_stepsize;
+
+  /* special values for move stepsize for spring and things on conveyor belt */
+  if (horiz_move)
+  {
+#if 0
+    if (element == EL_SPRING)
+      step = sign * MOVE_STEPSIZE_NORMAL * 2;
+    else if (CAN_FALL(element) && !CAN_MOVE(element) &&
+            y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
+      step = sign * MOVE_STEPSIZE_NORMAL / 2;
+#else
+    if (CAN_FALL(element) &&
+       y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
+      step = sign * MOVE_STEPSIZE_NORMAL / 2;
+    else if (element == EL_SPRING)
+      step = sign * MOVE_STEPSIZE_NORMAL * 2;
+#endif
+  }
+
+  return step;
+}
+
 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
 {
   if (player->GfxAction != action || player->GfxDir != dir)
@@ -2384,6 +2618,27 @@ void InitMovingField(int x, int y, int direction)
   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
     ResetGfxAnimation(x, y);
 
+#if USE_CAN_MOVE_NOT_MOVING
+
+  MovDir[x][y] = direction;
+  GfxDir[x][y] = direction;
+  GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
+                    ACTION_FALLING : ACTION_MOVING);
+
+  if (getElementMoveStepsize(x, y) != 0)       /* moving or being moved */
+  {
+    if (Feld[newx][newy] == EL_EMPTY)
+      Feld[newx][newy] = EL_BLOCKED;
+
+    MovDir[newx][newy] = MovDir[x][y];
+    GfxFrame[newx][newy] = GfxFrame[x][y];
+    GfxRandom[newx][newy] = GfxRandom[x][y];
+    GfxAction[newx][newy] = GfxAction[x][y];
+    GfxDir[newx][newy] = GfxDir[x][y];
+  }
+
+#else
+
   MovDir[newx][newy] = MovDir[x][y] = direction;
   GfxDir[x][y] = direction;
 
@@ -2399,6 +2654,7 @@ void InitMovingField(int x, int y, int direction)
   GfxRandom[newx][newy] = GfxRandom[x][y];
   GfxAction[newx][newy] = GfxAction[x][y];
   GfxDir[newx][newy] = GfxDir[x][y];
+#endif
 }
 
 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
@@ -2480,6 +2736,10 @@ static void RemoveField(int x, int y)
   ChangePage[x][y] = -1;
   Pushed[x][y] = FALSE;
 
+#if 0
+  ExplodeField[x][y] = EX_TYPE_NONE;
+#endif
+
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MV_NO_MOVING;
@@ -2605,57 +2865,19 @@ void CheckDynamite(int x, int y)
   Bang(x, y);
 }
 
-void RelocatePlayer(int x, int y, int element_raw)
+void DrawRelocatePlayer(struct PlayerInfo *player)
 {
-  int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
-  struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
-  boolean no_delay = (tape.index_search);
+  boolean no_delay = (tape.warp_forward);
   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
-  int old_jx, old_jy;
-
-  if (player->GameOver)                /* do not reanimate dead player */
-    return;
-
-  RemoveField(x, y);           /* temporarily remove newly placed player */
-  DrawLevelField(x, y);
-
-  if (player->present)
-  {
-    while (player->MovPos)
-    {
-      ScrollPlayer(player, SCROLL_GO_ON);
-      ScrollScreen(NULL, SCROLL_GO_ON);
-      FrameCounter++;
-
-      DrawPlayer(player);
-
-      BackToFront();
-      Delay(wait_delay_value);
-    }
-
-    DrawPlayer(player);                /* needed here only to cleanup last field */
-    DrawLevelField(player->jx, player->jy);    /* remove player graphic */
-
-    player->is_moving = FALSE;
-  }
-
-  old_jx = player->jx;
-  old_jy = player->jy;
-
-  Feld[x][y] = element;
-  InitPlayerField(x, y, element, TRUE);
-
-  if (player != local_player)  /* do not visually relocate other players */
-    return;
+  int jx = player->jx;
+  int jy = player->jy;
 
   if (level.instant_relocation)
   {
 #if 1
     int offset = (setup.scroll_delay ? 3 : 0);
-    int jx = local_player->jx;
-    int jy = local_player->jy;
 
     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
     {
@@ -2703,9 +2925,9 @@ void RelocatePlayer(int x, int y, int element_raw)
   else
   {
 #if 1
+#if 0
     int offset = (setup.scroll_delay ? 3 : 0);
-    int jx = local_player->jx;
-    int jy = local_player->jy;
+#endif
     int scroll_xx = -999, scroll_yy = -999;
 
     ScrollScreen(NULL, SCROLL_GO_ON);  /* scroll last frame to full tile */
@@ -2800,7 +3022,154 @@ void RelocatePlayer(int x, int y, int element_raw)
       Delay(wait_delay_value);
     }
 #endif
+
+    DrawPlayer(player);
+    BackToFront();
+    Delay(wait_delay_value);
+  }
+}
+
+void RelocatePlayer(int jx, int jy, int el_player_raw)
+{
+#if 1
+  int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
+#else
+  int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
+#endif
+  struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
+  boolean ffwd_delay = (tape.playing && tape.fast_forward);
+  boolean no_delay = (tape.warp_forward);
+  int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+  int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+  int old_jx = player->jx;
+  int old_jy = player->jy;
+  int old_element = Feld[old_jx][old_jy];
+  int element = Feld[jx][jy];
+  boolean player_relocated = (old_jx != jx || old_jy != jy);
+
+  int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
+  int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
+#if 1
+  int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
+  int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
+  int leave_side_horiz = move_dir_horiz;
+  int leave_side_vert  = move_dir_vert;
+#else
+  static int trigger_sides[4][2] =
+  {
+    /* enter side               leave side */
+    { CH_SIDE_RIGHT,           CH_SIDE_LEFT    },      /* moving left  */
+    { CH_SIDE_LEFT,            CH_SIDE_RIGHT   },      /* moving right */
+    { CH_SIDE_BOTTOM,          CH_SIDE_TOP     },      /* moving up    */
+    { CH_SIDE_TOP,             CH_SIDE_BOTTOM  }       /* moving down  */
+  };
+  int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
+  int enter_side_vert  = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
+  int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
+  int leave_side_vert  = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
+#endif
+  int enter_side = enter_side_horiz | enter_side_vert;
+  int leave_side = leave_side_horiz | leave_side_vert;
+
+  if (player->GameOver)                /* do not reanimate dead player */
+    return;
+
+  if (!player_relocated)       /* no need to relocate the player */
+    return;
+
+  if (IS_PLAYER(jx, jy))       /* player already placed at new position */
+  {
+    RemoveField(jx, jy);       /* temporarily remove newly placed player */
+    DrawLevelField(jx, jy);
+  }
+
+  if (player->present)
+  {
+    while (player->MovPos)
+    {
+      ScrollPlayer(player, SCROLL_GO_ON);
+      ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if USE_NEW_MOVE_DELAY
+      AdvanceFrameAndPlayerCounters(player->index_nr);
+#else
+      FrameCounter++;
+#endif
+
+      DrawPlayer(player);
+
+      BackToFront();
+      Delay(wait_delay_value);
+    }
+
+    DrawPlayer(player);                /* needed here only to cleanup last field */
+    DrawLevelField(player->jx, player->jy);    /* remove player graphic */
+
+    player->is_moving = FALSE;
+  }
+
+#if 1
+  if (IS_CUSTOM_ELEMENT(old_element))
+    CheckElementChangeByPlayer(old_jx, old_jy, old_element,
+                              CE_LEFT_BY_PLAYER,
+                              player->index_bit, leave_side);
+
+  CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
+                                     CE_OTHER_GETS_LEFT,
+                                     player->index_bit, leave_side);
+#endif
+
+  Feld[jx][jy] = el_player;
+  InitPlayerField(jx, jy, el_player, TRUE);
+
+  if (!ELEM_IS_PLAYER(element))        /* player may be set on walkable element */
+  {
+    Feld[jx][jy] = element;
+    InitField(jx, jy, FALSE);
   }
+
+#if 1
+  if (player == local_player)  /* only visually relocate local player */
+    DrawRelocatePlayer(player);
+#endif
+
+#if 1
+  TestIfHeroTouchesBadThing(jx, jy);
+  TestIfPlayerTouchesCustomElement(jx, jy);
+#endif
+
+#if 0
+  printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
+#endif
+
+#if 0
+#if 0
+  /* needed to allow change of walkable custom element by entering player */
+  if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
+    Changed[jx][jy] = 0;       /* allow another change (but prevent loop) */
+#else
+  /* needed to allow change of walkable custom element by entering player */
+  Changed[jx][jy] = 0;         /* allow another change */
+#endif
+#endif
+
+#if 0
+  printf("::: player entering %d, %d from %s ...\n", jx, jy,
+        enter_side == MV_LEFT  ? "left" :
+        enter_side == MV_RIGHT ? "right" :
+        enter_side == MV_UP    ? "top" :
+        enter_side == MV_DOWN  ? "bottom" : "oops! no idea!");
+#endif
+
+#if 1
+  if (IS_CUSTOM_ELEMENT(element))
+    CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
+                              player->index_bit, enter_side);
+
+  CheckTriggeredElementChangeByPlayer(jx, jy, element,
+                                     CE_OTHER_GETS_ENTERED,
+                                     player->index_bit, enter_side);
+#endif
 }
 
 void Explode(int ex, int ey, int phase, int mode)
@@ -2840,12 +3209,20 @@ void Explode(int ex, int ey, int phase, int mode)
     /* --- This is only really needed (and now handled) in "Impact()". --- */
     /* do not explode moving elements that left the explode field in time */
     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
-       center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
+       center_element == EL_EMPTY &&
+       (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
       return;
 #endif
 
-    if (mode == EX_NORMAL || mode == EX_CENTER)
+#if 1
+    if (mode == EX_TYPE_NORMAL ||
+       mode == EX_TYPE_CENTER ||
+       mode == EX_TYPE_CROSS)
+      PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
+#else
+    if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
       PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
+#endif
 
     /* remove things displayed in background while burning dynamite */
     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
@@ -2879,11 +3256,20 @@ void Explode(int ex, int ey, int phase, int mode)
       int element;
 
 #if 1
-      if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
+#if 1
+      if (!IN_LEV_FIELD(x, y) ||
+         (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
+         (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
+       continue;
+#else
+      if (!IN_LEV_FIELD(x, y) ||
+         (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
        continue;
+#endif
 #else
       if (!IN_LEV_FIELD(x, y) ||
-         ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
+         ((mode != EX_TYPE_NORMAL ||
+           center_element == EL_AMOEBA_TO_DIAMOND) &&
           (x != ex || y != ey)))
        continue;
 #endif
@@ -2905,10 +3291,17 @@ void Explode(int ex, int ey, int phase, int mode)
        continue;
 #else
       /* indestructible elements can only explode in center (but not flames) */
+#if 1
+      if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
+                                          mode == EX_TYPE_BORDER)) ||
+         element == EL_FLAMES)
+       continue;
+#else
       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
          element == EL_FLAMES)
        continue;
 #endif
+#endif
 
 #else
       if ((IS_INDESTRUCTIBLE(element) &&
@@ -2918,13 +3311,30 @@ void Explode(int ex, int ey, int phase, int mode)
        continue;
 #endif
 
+#if 1
+      if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
+         (game.engine_version < VERSION_IDENT(3,1,0,0) ||
+          (x == ex && y == ey && mode != EX_TYPE_BORDER)))
+#else
       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
+#endif
       {
        if (IS_ACTIVE_BOMB(element))
        {
          /* re-activate things under the bomb like gate or penguin */
+#if 1
+         Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
+         Back[x][y] = 0;
+#else
          Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
          Store[x][y] = 0;
+#endif
+
+#if 0
+       printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
+              element_info[Feld[x][y]].token_name,
+              Store[x][y], Store2[x][y]);
+#endif
        }
 
        continue;
@@ -2934,9 +3344,21 @@ void Explode(int ex, int ey, int phase, int mode)
 #if 0
       if (IS_INDESTRUCTIBLE(element))
        Back[x][y] = element;
+#else
+#if 1
+#if 1
+      if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
+         (x != ex || y != ey || mode == EX_TYPE_BORDER))
+       Back[x][y] = element;
+#else
+      if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
+         (x != ex || y != ey))
+       Back[x][y] = element;
+#endif
 #else
       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
        Back[x][y] = element;
+#endif
 #endif
 
       /* ignite explodable elements reached by other explosion */
@@ -3021,10 +3443,15 @@ void Explode(int ex, int ey, int phase, int mode)
       else
        Store[x][y] = EL_EMPTY;
 
-      if (x != ex || y != ey ||
-         center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
+      if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
+         center_element == EL_AMOEBA_TO_DIAMOND)
        Store2[x][y] = element;
 
+#if 0
+      printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
+            element_info[Store2[x][y]].token_name);
+#endif
+
 #if 0
       if (AmoebaNr[x][y] &&
          (element == EL_AMOEBA_FULL ||
@@ -3071,6 +3498,11 @@ void Explode(int ex, int ey, int phase, int mode)
       game.yamyam_content_nr =
        (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
 
+#if 0
+  printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
+        element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
+#endif
+
     return;
   }
 
@@ -3116,8 +3548,18 @@ void Explode(int ex, int ey, int phase, int mode)
 #if 1
 
   border_element = Store2[x][y];
+#if 1
+  if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
+    border_element = StorePlayer[x][y];
+#else
   if (IS_PLAYER(x, y))
     border_element = StorePlayer[x][y];
+#endif
+
+#if 0
+  printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
+        element_info[border_element].token_name, Store2[x][y]);
+#endif
 
 #if 0
   printf("::: phase == %d\n", phase);
@@ -3129,7 +3571,12 @@ void Explode(int ex, int ey, int phase, int mode)
     boolean border_explosion = FALSE;
 
 #if 1
+#if 1
+    if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
+       !PLAYER_EXPLOSION_PROTECTED(x, y))
+#else
     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
+#endif
 #else
     if (IS_PLAYER(x, y))
 #endif
@@ -3144,6 +3591,11 @@ void Explode(int ex, int ey, int phase, int mode)
     }
     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
     {
+#if 0
+      printf("::: %d,%d: %d %s\n", x, y, border_element,
+            element_info[border_element].token_name);
+#endif
+
       Feld[x][y] = Store2[x][y];
       Store2[x][y] = 0;
       Bang(x, y);
@@ -3212,6 +3664,10 @@ void Explode(int ex, int ey, int phase, int mode)
   {
     int element;
 
+#if 0
+  printf("::: done: phase == %d\n", phase);
+#endif
+
 #if 0
     printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
 #endif
@@ -3345,7 +3801,7 @@ void DynaExplode(int ex, int ey)
     player->dynabombs_left++;
   }
 
-  Explode(ex, ey, EX_PHASE_START, EX_CENTER);
+  Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
   {
@@ -3364,14 +3820,24 @@ void DynaExplode(int ex, int ey)
       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
        continue;
 
-      Explode(x, y, EX_PHASE_START, EX_BORDER);
+      Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
 
+#if 1
+#if 1
+      if (element != EL_EMPTY && element != EL_EXPLOSION &&
+         !IS_DIGGABLE(element) && !dynabomb_xl)
+       break;
+#else
+      if (element != EL_EMPTY && element != EL_EXPLOSION &&
+         !CAN_GROW_INTO(element) && !dynabomb_xl)
+       break;
+#endif
+#else
       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
-      if (element != EL_EMPTY &&
-         element != EL_SAND &&
-         element != EL_EXPLOSION &&
-         !dynabomb_xl)
+      if (element != EL_EMPTY && element != EL_EXPLOSION &&
+         element != EL_SAND && !dynabomb_xl)
        break;
+#endif
     }
   }
 }
@@ -3424,7 +3890,7 @@ void Bang(int x, int y)
     case EL_PACMAN:
     case EL_MOLE:
       RaiseScoreElement(element);
-      Explode(x, y, EX_PHASE_START, EX_NORMAL);
+      Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
       break;
     case EL_DYNABOMB_PLAYER_1_ACTIVE:
     case EL_DYNABOMB_PLAYER_2_ACTIVE:
@@ -3442,17 +3908,29 @@ void Bang(int x, int y)
     case EL_AMOEBA_TO_DIAMOND:
 #endif
       if (IS_PLAYER(x, y))
-       Explode(x, y, EX_PHASE_START, EX_NORMAL);
+       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
       else
-       Explode(x, y, EX_PHASE_START, EX_CENTER);
+       Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
       break;
     default:
-      if (CAN_EXPLODE_DYNA(element))
+#if 1
+      if (element_info[element].explosion_type == EXPLODES_CROSS)
+#else
+      if (CAN_EXPLODE_CROSS(element))
+#endif
+#if 1
+       Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
+#else
        DynaExplode(x, y);
+#endif
+#if 1
+      else if (element_info[element].explosion_type == EXPLODES_1X1)
+#else
       else if (CAN_EXPLODE_1X1(element))
-       Explode(x, y, EX_PHASE_START, EX_CENTER);
+#endif
+       Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
       else
-       Explode(x, y, EX_PHASE_START, EX_NORMAL);
+       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
       break;
   }
 
@@ -3750,6 +4228,10 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
          Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
        DrawLevelField(x, y);
+
+       /* uncrumble neighbour fields, if needed */
+       if (element == EL_INVISIBLE_SAND)
+         DrawLevelFieldCrumbledSandNeighbours(x, y);
       }
       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
               element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -3759,6 +4241,10 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
          Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
        DrawLevelField(x, y);
+
+       /* re-crumble neighbour fields, if needed */
+       if (element == EL_INVISIBLE_SAND)
+         DrawLevelFieldCrumbledSandNeighbours(x, y);
       }
     }
   }
@@ -3808,37 +4294,6 @@ static void ActivateTimegateSwitch(int x, int y)
   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
 }
 
-inline static int getElementMoveStepsize(int x, int y)
-{
-  int element = Feld[x][y];
-  int direction = MovDir[x][y];
-  int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
-  int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
-  int horiz_move = (dx != 0);
-  int sign = (horiz_move ? dx : dy);
-  int step = sign * element_info[element].move_stepsize;
-
-  /* special values for move stepsize for spring and things on conveyor belt */
-  if (horiz_move)
-  {
-#if 0
-    if (element == EL_SPRING)
-      step = sign * MOVE_STEPSIZE_NORMAL * 2;
-    else if (CAN_FALL(element) && !CAN_MOVE(element) &&
-            y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
-      step = sign * MOVE_STEPSIZE_NORMAL / 2;
-#else
-    if (CAN_FALL(element) &&
-       y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
-      step = sign * MOVE_STEPSIZE_NORMAL / 2;
-    else if (element == EL_SPRING)
-      step = sign * MOVE_STEPSIZE_NORMAL * 2;
-#endif
-  }
-
-  return step;
-}
-
 void Impact(int x, int y)
 {
   boolean lastline = (y == lev_fieldy-1);
@@ -3877,6 +4332,7 @@ void Impact(int x, int y)
     return;
   }
 
+  /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
   /* only reset graphic animation if graphic really changes after impact */
   if (impact &&
       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
@@ -3892,6 +4348,8 @@ void Impact(int x, int y)
   }
   else if (impact && element == EL_PEARL)
   {
+    ResetGfxAnimation(x, y);
+
     Feld[x][y] = EL_PEARL_BREAKING;
     PlayLevelSound(x, y, SND_PEARL_BREAKING);
     return;
@@ -4011,6 +4469,8 @@ void Impact(int x, int y)
        }
        else if (smashed == EL_PEARL)
        {
+         ResetGfxAnimation(x, y);
+
          Feld[x][y + 1] = EL_PEARL_BREAKING;
          PlayLevelSound(x, y, SND_PEARL_BREAKING);
          return;
@@ -4043,10 +4503,18 @@ void Impact(int x, int y)
 
          CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
 
-         CheckTriggeredElementChangeSide(x, y + 1, smashed,
-                                         CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
-         CheckElementChangeSide(x, y + 1, smashed, element,
-                                CE_SWITCHED, CH_SIDE_TOP);
+#if 1
+         /* !!! TEST ONLY !!! */
+         CheckElementChangeBySide(x, y + 1, smashed, element,
+                                  CE_SWITCHED, CH_SIDE_TOP);
+         CheckTriggeredElementChangeBySide(x, y + 1, smashed,
+                                           CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
+#else
+         CheckTriggeredElementChangeBySide(x, y + 1, smashed,
+                                           CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
+         CheckElementChangeBySide(x, y + 1, smashed, element,
+                                  CE_SWITCHED, CH_SIDE_TOP);
+#endif
        }
       }
       else
@@ -4293,8 +4761,15 @@ inline static void TurnRoundExt(int x, int y)
     xx = x + move_xy[MovDir[x][y]].x;
     yy = y + move_xy[MovDir[x][y]].y;
 
+#if 1
+    /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
+    if (!IN_LEV_FIELD(xx, yy) ||
+        (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
+      MovDir[x][y] = old_move_dir;
+#else
     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
       MovDir[x][y] = old_move_dir;
+#endif
 
     MovDelay[x][y] = 0;
   }
@@ -4426,7 +4901,13 @@ inline static void TurnRoundExt(int x, int y)
       }
     }
 
+#if 1
+    if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
+       (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
+        game.engine_version < VERSION_IDENT(3,1,0,0)))
+#else
     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
+#endif
     {
       attr_x = ZX;
       attr_y = ZY;
@@ -4551,6 +5032,11 @@ inline static void TurnRoundExt(int x, int y)
     boolean can_turn_right =
       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
 
+#if USE_CAN_MOVE_NOT_MOVING
+    if (element_info[element].move_stepsize == 0)      /* not moving */
+      return;
+#endif
+
     if (move_pattern == MV_TURNING_LEFT)
       MovDir[x][y] = left_dir;
     else if (move_pattern == MV_TURNING_RIGHT)
@@ -4661,6 +5147,16 @@ inline static void TurnRoundExt(int x, int y)
       boolean first_horiz = RND(2);
       int new_move_dir = MovDir[x][y];
 
+#if USE_CAN_MOVE_NOT_MOVING
+      if (element_info[element].move_stepsize == 0)    /* not moving */
+      {
+       first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
+       MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+
+       return;
+      }
+#endif
+
       MovDir[x][y] =
        new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
       Moving2Blocked(x, y, &newx, &newy);
@@ -4754,7 +5250,13 @@ inline static void TurnRoundExt(int x, int y)
 
     MovDir[x][y] = new_move_dir;
     if (old_move_dir != new_move_dir)
+    {
+#if 1
+      MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+#else
       MovDelay[x][y] = 9;
+#endif
+    }
   }
 }
 
@@ -4970,12 +5472,16 @@ void StartMoving(int x, int y)
 #endif
     }
 #if 1
-    else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
-             CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
-             (Feld[x][y + 1] == EL_BLOCKED)) ||
+    else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+             CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
+
             (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
              CAN_SMASH(element) && WasJustFalling[x][y] &&
-             (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
+             (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
+
+            (game.engine_version < VERSION_IDENT(2,2,0,7) &&
+             CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
+             (Feld[x][y + 1] == EL_BLOCKED)))
 
 #else
 #if 1
@@ -5004,6 +5510,13 @@ void StartMoving(int x, int y)
       WasJustFalling[x][y] = 0;
 #endif
 
+      CheckCollision[x][y] = 0;
+
+#if 0
+      if (IS_PLAYER(x, y + 1))
+       printf("::: we ARE now killing the player [%d]\n", FrameCounter);
+#endif
+
       Impact(x, y);
     }
     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
@@ -5071,13 +5584,39 @@ void StartMoving(int x, int y)
        can_fall_both = (can_fall_left && can_fall_right);
       }
 
+#if USE_NEW_SP_SLIPPERY
+      /* !!! better use the same properties as for custom elements here !!! */
+      else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
+              can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
+      {
+       can_fall_right = FALSE;         /* slip down on left side */
+       can_fall_both = FALSE;
+      }
+#endif
+
+#if 1
+      if (can_fall_both)
+      {
+       if (game.emulation == EMU_BOULDERDASH ||
+           element == EL_BD_ROCK || element == EL_BD_DIAMOND)
+         can_fall_right = FALSE;       /* slip down on left side */
+       else
+         can_fall_left = !(can_fall_right = RND(2));
+
+       can_fall_both = FALSE;
+      }
+#endif
+
       if (can_fall_any)
       {
+#if 0
        if (can_fall_both &&
            (game.emulation != EMU_BOULDERDASH &&
             element != EL_BD_ROCK && element != EL_BD_DIAMOND))
          can_fall_left = !(can_fall_right = RND(2));
+#endif
 
+       /* if not determined otherwise, prefer left side for slipping down */
        InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
        started_moving = TRUE;
       }
@@ -5118,11 +5657,26 @@ void StartMoving(int x, int y)
   }
 
   /* not "else if" because of elements that can fall and move (EL_SPRING) */
+#if 0
+  if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
+#else
   if (CAN_MOVE(element) && !started_moving)
+#endif
   {
     int move_pattern = element_info[element].move_pattern;
     int newx, newy;
 
+#if 0
+#if DEBUG
+    if (MovDir[x][y] == MV_NO_MOVING)
+    {
+      printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
+            x, y, element, element_info[element].token_name);
+      printf("StartMoving(): This should never happen!\n");
+    }
+#endif
+#endif
+
     Moving2Blocked(x, y, &newx, &newy);
 
 #if 1
@@ -5136,10 +5690,16 @@ void StartMoving(int x, int y)
       return;
 #endif
 
+#if 1
+
 #if 1
+    if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+       CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+#else
     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
        WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
        (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
+#endif
     {
 #if 0
       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
@@ -5155,6 +5715,8 @@ void StartMoving(int x, int y)
       WasJustMoving[x][y] = 0;
 #endif
 
+      CheckCollision[x][y] = 0;
+
       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
 
 #if 0
@@ -5307,11 +5869,30 @@ void StartMoving(int x, int y)
          {
            int flamed = MovingOrBlocked2Element(xx, yy);
 
+           /* !!! */
+#if 0
+           if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
+             Bang(xx, yy);
+           else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
+             RemoveMovingField(xx, yy);
+           else
+             RemoveField(xx, yy);
+#else
            if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
              Bang(xx, yy);
            else
              RemoveMovingField(xx, yy);
+#endif
+
+#if 0
+           if (ChangeDelay[xx][yy])
+             printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
+                                       Feld[xx][yy] == EL_BLOCKED));
+#endif
 
+#if 1
+           ChangeDelay[xx][yy] = 0;
+#endif
            Feld[xx][yy] = EL_FLAMES;
            if (IN_SCR_FIELD(sx, sy))
            {
@@ -5508,11 +6089,27 @@ void StartMoving(int x, int y)
          DrawLevelField(newx, newy);
        }
 
+       /* if digged element was about to explode, prevent the explosion */
+       ExplodeField[newx][newy] = EX_TYPE_NONE;
+
        PlayLevelSoundAction(x, y, action);
       }
 
+#if 1
+#if 1
+      Store[newx][newy] = EL_EMPTY;
+      if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
+       Store[newx][newy] = element_info[element].move_leave_element;
+#else
+      Store[newx][newy] = EL_EMPTY;
+      if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
+         element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
+       Store[newx][newy] = element_info[element].move_leave_element;
+#endif
+#else
       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
        element_info[element].can_leave_element = TRUE;
+#endif
 
       if (move_pattern & MV_MAZE_RUNNER_STYLE)
       {
@@ -5565,11 +6162,25 @@ void StartMoving(int x, int y)
 
          MovDelay[x][y] = 50;
 
+         /* !!! */
+#if 0
+         RemoveField(newx, newy);
+#endif
          Feld[newx][newy] = EL_FLAMES;
          if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
+         {
+#if 0
+           RemoveField(newx1, newy1);
+#endif
            Feld[newx1][newy1] = EL_FLAMES;
+         }
          if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
+         {
+#if 0
+           RemoveField(newx2, newy2);
+#endif
            Feld[newx2][newy2] = EL_FLAMES;
+         }
 
          return;
        }
@@ -5660,6 +6271,27 @@ void StartMoving(int x, int y)
 
       TurnRound(x, y);
 
+#if 0
+      if (move_pattern & MV_ANY_DIRECTION &&
+         move_pattern == MovDir[x][y])
+      {
+       int blocking_element =
+         (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
+
+#if 0
+       printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
+              element_info[element].token_name,
+              element_info[blocking_element].token_name,
+              x, y, newx, newy);
+#endif
+
+       CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
+                                MovDir[x][y]);
+
+       element = Feld[x][y];   /* element might have changed */
+      }
+#endif
+
 #if 1
       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
        DrawLevelElementAnimation(x, y, element);
@@ -5701,6 +6333,7 @@ void StartMoving(int x, int y)
 void ContinueMoving(int x, int y)
 {
   int element = Feld[x][y];
+  int stored = Store[x][y];
   struct ElementInfo *ei = &element_info[element];
   int direction = MovDir[x][y];
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
@@ -5742,7 +6375,13 @@ void ContinueMoving(int x, int y)
   Feld[newx][newy] = element;
   MovPos[x][y] = 0;    /* force "not moving" for "crumbled sand" */
 
-  if (element == EL_MOLE)
+#if 1
+  if (Store[x][y] == EL_ACID)  /* element is moving into acid pool */
+  {
+    element = Feld[newx][newy] = EL_ACID;
+  }
+#endif
+  else if (element == EL_MOLE)
   {
     Feld[x][y] = EL_SAND;
 
@@ -5801,20 +6440,42 @@ void ContinueMoving(int x, int y)
 
     Back[x][y] = Back[newx][newy] = 0;
   }
+#if 0
   else if (Store[x][y] == EL_ACID)
   {
     element = Feld[newx][newy] = EL_ACID;
   }
+#endif
+#if 0
+  else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
+          ei->move_leave_element != EL_EMPTY &&
+          (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
+           Store[x][y] != EL_EMPTY))
+  {
+    /* some elements can leave other elements behind after moving */
+
+    Feld[x][y] = ei->move_leave_element;
+    InitField(x, y, FALSE);
+
+    if (GFX_CRUMBLED(Feld[x][y]))
+      DrawLevelFieldCrumbledSandNeighbours(x, y);
+  }
+#endif
 
-  Store[x][y] = 0;
-  MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+  Store[x][y] = EL_EMPTY;
+  MovPos[x][y] = 0;
+  MovDir[x][y] = 0;
+  MovDelay[x][y] = 0;
   MovDelay[newx][newy] = 0;
 
-  /* copy element change control values to new field */
-  ChangeDelay[newx][newy] = ChangeDelay[x][y];
-  ChangePage[newx][newy] = ChangePage[x][y];
-  Changed[newx][newy] = Changed[x][y];
-  ChangeEvent[newx][newy] = ChangeEvent[x][y];
+  if (CAN_CHANGE(element))
+  {
+    /* copy element change control values to new field */
+    ChangeDelay[newx][newy] = ChangeDelay[x][y];
+    ChangePage[newx][newy]  = ChangePage[x][y];
+    Changed[newx][newy]     = Changed[x][y];
+    ChangeEvent[newx][newy] = ChangeEvent[x][y];
+  }
 
   ChangeDelay[x][y] = 0;
   ChangePage[x][y] = -1;
@@ -5829,10 +6490,44 @@ void ContinueMoving(int x, int y)
 
   Pushed[x][y] = Pushed[newx][newy] = FALSE;
 
+#if 0
+  /* do this after checking for left-behind element */
   ResetGfxAnimation(x, y);     /* reset animation values for old field */
+#endif
 
 #if 1
   /* some elements can leave other elements behind after moving */
+#if 1
+  if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
+      (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
+      (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
+#else
+  if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
+      (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
+      !IS_PLAYER(x, y))
+#endif
+  {
+    int move_leave_element = ei->move_leave_element;
+
+    Feld[x][y] = move_leave_element;
+
+#if USE_PREVIOUS_MOVE_DIR
+    if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+      MovDir[x][y] = direction;
+#endif
+
+    InitField(x, y, FALSE);
+
+    if (GFX_CRUMBLED(Feld[x][y]))
+      DrawLevelFieldCrumbledSandNeighbours(x, y);
+
+    if (ELEM_IS_PLAYER(move_leave_element))
+      RelocatePlayer(x, y, move_leave_element);
+  }
+#endif
+
+#if 0
+  /* some elements can leave other elements behind after moving */
   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
       ei->move_leave_element != EL_EMPTY &&
       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
@@ -5849,6 +6544,11 @@ void ContinueMoving(int x, int y)
   ei->can_leave_element = FALSE;
 #endif
 
+#if 1
+  /* do this after checking for left-behind element */
+  ResetGfxAnimation(x, y);     /* reset animation values for old field */
+#endif
+
 #if 0
   /* 2.1.1 (does not work correctly for spring) */
   if (!CAN_MOVE(element))
@@ -5866,9 +6566,18 @@ void ContinueMoving(int x, int y)
     MovDir[newx][newy] = 0;
   */
 
+#if 0
   if (!CAN_MOVE(element) ||
       (CAN_FALL(element) && direction == MV_DOWN))
     GfxDir[x][y] = MovDir[newx][newy] = 0;
+#else
+  if (!CAN_MOVE(element) ||
+      (CAN_FALL(element) && direction == MV_DOWN &&
+       (element == EL_SPRING ||
+       element_info[element].move_pattern == MV_WHEN_PUSHED ||
+       element_info[element].move_pattern == MV_WHEN_DROPPED)))
+    GfxDir[x][y] = MovDir[newx][newy] = 0;
+#endif
 
 #endif
 #endif
@@ -5888,15 +6597,30 @@ void ContinueMoving(int x, int y)
   /* prevent elements on conveyor belt from moving on in last direction */
   if (pushed_by_conveyor && CAN_FALL(element) &&
       direction & MV_HORIZONTAL)
+  {
+#if 0
+    if (CAN_MOVE(element))
+      InitMovDir(newx, newy);
+    else
+      MovDir[newx][newy] = 0;
+#else
     MovDir[newx][newy] = 0;
 #endif
+  }
+#endif
 
   if (!pushed_by_player)
   {
+    int nextx = newx + dx, nexty = newy + dy;
+    boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
+
     WasJustMoving[newx][newy] = 3;
 
     if (CAN_FALL(element) && direction == MV_DOWN)
       WasJustFalling[newx][newy] = 3;
+
+    if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
+      CheckCollision[newx][newy] = 2;
   }
 
   if (DONT_TOUCH(element))     /* object may be nasty to player or others */
@@ -5910,9 +6634,59 @@ void ContinueMoving(int x, int y)
   else if (element == EL_PENGUIN)
     TestIfFriendTouchesBadThing(newx, newy);
 
+#if USE_NEW_MOVE_STYLE
+#if 0
   if (CAN_FALL(element) && direction == MV_DOWN &&
-      (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
+      (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
+      IS_PLAYER(x, newy + 1))
+    printf("::: we would now kill the player [%d]\n", FrameCounter);
+#endif
+
+  /* give the player one last chance (one more frame) to move away */
+  if (CAN_FALL(element) && direction == MV_DOWN &&
+      (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
+      (!IS_PLAYER(x, newy + 1) ||
+       game.engine_version < VERSION_IDENT(3,1,1,0)))
+    Impact(x, newy);
+#else
+  if (CAN_FALL(element) && direction == MV_DOWN &&
+      (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
     Impact(x, newy);
+#endif
+
+#if 1
+
+#if USE_PUSH_BUGFIX
+#if 1
+  if (pushed_by_player && !game.use_bug_change_when_pushing)
+#else
+  if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
+#endif
+#else
+  if (pushed_by_player)
+#endif
+
+  {
+#if 1
+    int dig_side = MV_DIR_OPPOSITE(direction);
+#else
+    static int trigger_sides[4] =
+    {
+      CH_SIDE_RIGHT,   /* moving left  */
+      CH_SIDE_LEFT,    /* moving right */
+      CH_SIDE_BOTTOM,  /* moving up    */
+      CH_SIDE_TOP,     /* moving down  */
+    };
+    int dig_side = trigger_sides[MV_DIR_BIT(direction)];
+#endif
+    struct PlayerInfo *player = PLAYERINFO(x, y);
+
+    CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
+                              player->index_bit, dig_side);
+    CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
+                                       player->index_bit, dig_side);
+  }
+#endif
 
 #if 1
   TestIfElementTouchesCustomElement(x, y);     /* empty or new element */
@@ -5934,8 +6708,8 @@ void ContinueMoving(int x, int y)
     int hitting_element = Feld[newx][newy];
 
     /* !!! fix side (direction) orientation here and elsewhere !!! */
-    CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
-                          direction);
+    CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
+                            direction);
 
 #if 0
     if (IN_LEV_FIELD(nextx, nexty))
@@ -5952,8 +6726,8 @@ void ContinueMoving(int x, int y)
       {
        int i;
 
-       CheckElementChangeSide(nextx, nexty, touched_element,
-                              CE_HIT_BY_SOMETHING, opposite_direction);
+       CheckElementChangeBySide(nextx, nexty, touched_element,
+                                CE_HIT_BY_SOMETHING, opposite_direction);
 
        if (IS_CUSTOM_ELEMENT(hitting_element) &&
            HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
@@ -5968,8 +6742,8 @@ void ContinueMoving(int x, int y)
                change->trigger_side & touched_side &&
                change->trigger_element == touched_element)
            {
-             CheckElementChangePage(newx, newy, hitting_element,
-                                    touched_element, CE_OTHER_IS_HITTING, i);
+             CheckElementChangeByPage(newx, newy, hitting_element,
+                                      touched_element, CE_OTHER_IS_HITTING,i);
              break;
            }
          }
@@ -5988,8 +6762,8 @@ void ContinueMoving(int x, int y)
                change->trigger_side & hitting_side &&
                change->trigger_element == hitting_element)
            {
-             CheckElementChangePage(nextx, nexty, touched_element,
-                                    hitting_element, CE_OTHER_GETS_HIT, i);
+             CheckElementChangeByPage(nextx, nexty, touched_element,
+                                      hitting_element, CE_OTHER_GETS_HIT, i);
              break;
            }
          }
@@ -6305,6 +7079,15 @@ void AmoebeAbleger(int ax, int ay)
     if (!IN_LEV_FIELD(x, y))
       return;
 
+#if 1
+    if (IS_FREE(x, y) ||
+       CAN_GROW_INTO(Feld[x][y]) ||
+       Feld[x][y] == EL_QUICKSAND_EMPTY)
+    {
+      newax = x;
+      neway = y;
+    }
+#else
     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
     if (IS_FREE(x, y) ||
        Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
@@ -6312,6 +7095,7 @@ void AmoebeAbleger(int ax, int ay)
       newax = x;
       neway = y;
     }
+#endif
 
     if (newax == ax && neway == ay)
       return;
@@ -6330,6 +7114,16 @@ void AmoebeAbleger(int ax, int ay)
       if (!IN_LEV_FIELD(x, y))
        continue;
 
+#if 1
+      if (IS_FREE(x, y) ||
+         CAN_GROW_INTO(Feld[x][y]) ||
+         Feld[x][y] == EL_QUICKSAND_EMPTY)
+      {
+       newax = x;
+       neway = y;
+       break;
+      }
+#else
       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
       if (IS_FREE(x, y) ||
          Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
@@ -6338,13 +7132,18 @@ void AmoebeAbleger(int ax, int ay)
        neway = y;
        break;
       }
+#endif
       else if (IS_PLAYER(x, y))
        waiting_for_player = TRUE;
     }
 
     if (newax == ax && neway == ay)            /* amoeba cannot grow */
     {
+#if 1
+      if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
+#else
       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
+#endif
       {
        Feld[ax][ay] = EL_AMOEBA_DEAD;
        DrawLevelField(ax, ay);
@@ -6475,6 +7274,20 @@ void Life(int ax, int ay)
        changed = TRUE;
       }
     }
+#if 1
+    else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
+    {                                  /* free border field */
+      if (nachbarn >= life[2] && nachbarn <= life[3])
+      {
+       Feld[xx][yy] = element;
+       MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
+       if (!Stop[xx][yy])
+         DrawLevelField(xx, yy);
+       Stop[xx][yy] = TRUE;
+       changed = TRUE;
+      }
+    }
+#else
     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
     {                                  /* free border field */
@@ -6488,6 +7301,7 @@ void Life(int ax, int ay)
        changed = TRUE;
       }
     }
+#endif
   }
 
   if (changed)
@@ -6513,7 +7327,12 @@ static void StopRobotWheel(int x, int y)
 
 static void InitTimegateWheel(int x, int y)
 {
+#if 1
+  ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
+#else
+  /* another brainless, "type style" bug ... :-( */
   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
+#endif
 }
 
 static void RunTimegateWheel(int x, int y)
@@ -6900,52 +7719,95 @@ static void ChangeActiveTrap(int x, int y)
 static void ChangeElementNowExt(int x, int y, int target_element)
 {
   int previous_move_direction = MovDir[x][y];
+#if 1
+  boolean add_player = (ELEM_IS_PLAYER(target_element) &&
+                       IS_WALKABLE(Feld[x][y]));
+#else
+  boolean add_player = (ELEM_IS_PLAYER(target_element) &&
+                       IS_WALKABLE(Feld[x][y]) &&
+                       !IS_MOVING(x, y));
+#endif
 
   /* check if element under player changes from accessible to unaccessible
      (needed for special case of dropping element which then changes) */
   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
   {
+#if 0
+    printf("::: BOOOM! [%d, '%s']\n", target_element,
+          element_info[target_element].token_name);
+#endif
+
     Bang(x, y);
     return;
   }
 
-  RemoveField(x, y);
-  Feld[x][y] = target_element;
+#if 1
+  if (!add_player)
+#endif
+  {
+#if 1
+    if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
+      RemoveMovingField(x, y);
+    else
+      RemoveField(x, y);
 
-  Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
+    Feld[x][y] = target_element;
+#else
+    RemoveField(x, y);
+    Feld[x][y] = target_element;
+#endif
 
-  ResetGfxAnimation(x, y);
-  ResetRandomAnimationValue(x, y);
+    ResetGfxAnimation(x, y);
+    ResetRandomAnimationValue(x, y);
 
-  if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
-    MovDir[x][y] = previous_move_direction;
+    if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+      MovDir[x][y] = previous_move_direction;
 
 #if 1
-  InitField_WithBug1(x, y, FALSE);
+    InitField_WithBug1(x, y, FALSE);
 #else
-  InitField(x, y, FALSE);
-  if (CAN_MOVE(Feld[x][y]))
-    InitMovDir(x, y);
+    InitField(x, y, FALSE);
+    if (CAN_MOVE(Feld[x][y]))
+      InitMovDir(x, y);
 #endif
 
-  DrawLevelField(x, y);
+    DrawLevelField(x, y);
 
-  if (GFX_CRUMBLED(Feld[x][y]))
-    DrawLevelFieldCrumbledSandNeighbours(x, y);
+    if (GFX_CRUMBLED(Feld[x][y]))
+      DrawLevelFieldCrumbledSandNeighbours(x, y);
+  }
+
+#if 0
+  Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
+#endif
 
+#if 0
   TestIfBadThingTouchesHero(x, y);
   TestIfPlayerTouchesCustomElement(x, y);
   TestIfElementTouchesCustomElement(x, y);
+#endif
 
+  /* "Changed[][]" not set yet to allow "entered by player" change one time */
   if (ELEM_IS_PLAYER(target_element))
     RelocatePlayer(x, y, target_element);
+
+#if 1
+  Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
+#endif
+
+#if 1
+  TestIfBadThingTouchesHero(x, y);
+  TestIfPlayerTouchesCustomElement(x, y);
+  TestIfElementTouchesCustomElement(x, y);
+#endif
 }
 
 static boolean ChangeElementNow(int x, int y, int element, int page)
 {
   struct ElementChangeInfo *change = &element_info[element].change_page[page];
   int target_element;
+  int old_element = Feld[x][y];
 
   /* always use default change event to prevent running into a loop */
   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
@@ -6969,7 +7831,10 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
 
   Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
 
-  CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
+#if 0
+  /* !!! indirect change before direct change !!! */
+  CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
+#endif
 
   if (change->explode)
   {
@@ -6987,7 +7852,10 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
     {
       boolean is_empty;
+      boolean is_walkable;
       boolean is_diggable;
+      boolean is_collectible;
+      boolean is_removable;
       boolean is_destructible;
       int ex = x + xx - 1;
       int ey = y + yy - 1;
@@ -7014,21 +7882,54 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
        continue;
       }
 
+#if 0
+      if (Changed[ex][ey])     /* do not change already changed elements */
+      {
+       can_replace[xx][yy] = FALSE;
+       complete_replace = FALSE;
+
+       continue;
+      }
+#endif
+
       e = Feld[ex][ey];
 
       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
        e = MovingOrBlocked2Element(ex, ey);
 
 #if 1
-      is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
-                                     IS_WALKABLE(content_element)));
-      is_diggable = (is_empty || IS_DIGGABLE(e));
+
+#if 0
+      is_empty = (IS_FREE(ex, ey) ||
+                 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
+                 (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
+                  !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
+#else
+
+#if 0
+      is_empty = (IS_FREE(ex, ey) ||
+                 (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
+#else
+      is_empty = (IS_FREE(ex, ey) ||
+                 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
+#endif
+
+#endif
+
+      is_walkable     = (is_empty || IS_WALKABLE(e));
+      is_diggable     = (is_empty || IS_DIGGABLE(e));
+      is_collectible  = (is_empty || IS_COLLECTIBLE(e));
       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
+      is_removable    = (is_diggable || is_collectible);
 
       can_replace[xx][yy] =
-       ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
-        (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
-        (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
+       (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
+         (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
+         (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
+         (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
+         (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
+         (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
+        !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
 
       if (!can_replace[xx][yy])
        complete_replace = FALSE;
@@ -7099,6 +8000,11 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
   }
 
+#if 1
+  /* this uses direct change before indirect change */
+  CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
+#endif
+
   return TRUE;
 }
 
@@ -7108,9 +8014,8 @@ static void ChangeElement(int x, int y, int page)
   struct ElementInfo *ei = &element_info[element];
   struct ElementChangeInfo *change = &ei->change_page[page];
 
-#if 0
 #ifdef DEBUG
-  if (!CAN_CHANGE(element))
+  if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
   {
     printf("\n\n");
     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
@@ -7119,8 +8024,18 @@ static void ChangeElement(int x, int y, int page)
     printf("\n\n");
   }
 #endif
+
+  /* this can happen with classic bombs on walkable, changing elements */
+  if (!CAN_CHANGE(element))
+  {
+#if 0
+    if (!CAN_CHANGE(Back[x][y]))       /* prevent permanent repetition */
+      ChangeDelay[x][y] = 0;
 #endif
 
+    return;
+  }
+
   if (ChangeDelay[x][y] == 0)          /* initialize element change */
   {
     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
@@ -7264,6 +8179,20 @@ static boolean CheckElementChangeExt(int x, int y,
     element = Feld[x][y];
   }
 
+#if 1
+  if (Feld[x][y] != element)   /* check if element has already changed */
+  {
+#if 0
+    printf("::: %d ('%s') != %d ('%s') [%d]\n",
+          Feld[x][y], element_info[Feld[x][y]].token_name,
+          element, element_info[element].token_name,
+          trigger_event);
+#endif
+
+    return FALSE;
+  }
+#endif
+
 #if 1
   if (trigger_page < 0)
   {
@@ -7663,10 +8592,49 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
 }
 #endif
 
+void AdvanceFrameAndPlayerCounters(int player_nr)
+{
+  int i;
+
+  /* advance frame counters (global frame counter and time frame counter) */
+  FrameCounter++;
+  TimeFrames++;
+
+  /* advance player counters (counters for move delay, move animation etc.) */
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    boolean advance_player_counters = (player_nr == -1 || player_nr == i);
+    int move_frames =
+      MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
+
+    if (!advance_player_counters)      /* not all players may be affected */
+      continue;
+
+    stored_player[i].Frame += move_frames;
+
+    if (stored_player[i].MovPos != 0)
+      stored_player[i].StepFrame += move_frames;
+
+#if USE_NEW_MOVE_DELAY
+    if (stored_player[i].move_delay > 0)
+      stored_player[i].move_delay--;
+#endif
+
+#if USE_NEW_PUSH_DELAY
+    /* due to bugs in previous versions, counter must count up, not down */
+    if (stored_player[i].push_delay != -1)
+      stored_player[i].push_delay++;
+#endif
+
+    if (stored_player[i].drop_delay > 0)
+      stored_player[i].drop_delay--;
+  }
+}
+
 void GameActions()
 {
-  static unsigned long action_delay = 0;
-  unsigned long action_delay_value;
+  static unsigned long game_frame_delay = 0;
+  unsigned long game_frame_delay_value;
   int magic_wall_x = 0, magic_wall_y = 0;
   int i, x, y, element, graphic;
   byte *recorded_player_action;
@@ -7678,15 +8646,15 @@ void GameActions()
   if (game_status != GAME_MODE_PLAYING)
     return;
 
-  action_delay_value =
+  game_frame_delay_value =
     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
 
-  if (tape.playing && tape.index_search && !tape.pausing)
-    action_delay_value = 0;
+  if (tape.playing && tape.warp_forward && !tape.pausing)
+    game_frame_delay_value = 0;
 
   /* ---------- main game synchronization point ---------- */
 
-  WaitUntilDelayReached(&action_delay, action_delay_value);
+  WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
 
   if (network_playing && !network_player_action_received)
   {
@@ -7696,7 +8664,7 @@ void GameActions()
 #endif
     */
 
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
     /* last chance to get network player actions without main loop delay */
     HandleNetworking();
 #endif
@@ -7725,6 +8693,7 @@ void GameActions()
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
 #if 1
+  /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
   if (recorded_player_action == NULL && tape.pausing)
     return;
 #endif
@@ -7747,7 +8716,7 @@ void GameActions()
       stored_player[i].effective_action = stored_player[i].action;
   }
 
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
   if (network_playing)
     SendToServer_MovePlayer(summarized_player_action);
 #endif
@@ -7857,7 +8826,7 @@ void GameActions()
 #endif
 
 #if 1
-  /* for downwards compatibility, the following code emulates a fixed bug that
+  /* for backwards compatibility, the following code emulates a fixed bug that
      occured when pushing elements (causing elements that just made their last
      pushing step to already (if possible) make their first falling step in the
      same game frame, which is bad); this code is also needed to use the famous
@@ -7904,6 +8873,16 @@ void GameActions()
     Changed[x][y] = CE_BITMASK_DEFAULT;
     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
 
+#if USE_NEW_BLOCK_STYLE
+    /* this must be handled before main playfield loop */
+    if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
+    {
+      MovDelay[x][y]--;
+      if (MovDelay[x][y] <= 0)
+       RemoveField(x, y);
+    }
+#endif
+
 #if DEBUG
     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
     {
@@ -7919,6 +8898,8 @@ void GameActions()
       WasJustMoving[x][y]--;
     if (WasJustFalling[x][y] > 0)
       WasJustFalling[x][y]--;
+    if (CheckCollision[x][y] > 0)
+      CheckCollision[x][y]--;
 
     GfxFrame[x][y]++;
 
@@ -8065,7 +9046,7 @@ void GameActions()
       CheckDynamite(x, y);
 #if 0
     else if (element == EL_EXPLOSION && !game.explosions_delayed)
-      Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
+      Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
 #endif
     else if (element == EL_AMOEBA_GROWING)
       AmoebeWaechst(x, y);
@@ -8148,6 +9129,21 @@ void GameActions()
 #endif
       element = Feld[x][y];
 
+#if 1
+      if (!IS_PLAYER(x,y) &&
+         (element == EL_EMPTY ||
+          CAN_GROW_INTO(element) ||
+          element == EL_QUICKSAND_EMPTY ||
+          element == EL_ACID_SPLASH_LEFT ||
+          element == EL_ACID_SPLASH_RIGHT))
+      {
+       if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
+         Feld[x][y] = EL_AMOEBA_DROP;
+      }
+#else
       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
       if (!IS_PLAYER(x,y) &&
          (element == EL_EMPTY ||
@@ -8162,6 +9158,7 @@ void GameActions()
            (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
          Feld[x][y] = EL_AMOEBA_DROP;
       }
+#endif
 
       random = random * 129 + 1;
     }
@@ -8181,9 +9178,9 @@ void GameActions()
       if (ExplodeField[x][y])
        Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
       else if (element == EL_EXPLOSION)
-       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
+       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
 
-      ExplodeField[x][y] = EX_NO_EXPLOSION;
+      ExplodeField[x][y] = EX_TYPE_NONE;
     }
 
     game.explosions_delayed = TRUE;
@@ -8265,22 +9262,22 @@ void GameActions()
     TimeFrames = 0;
     TapeTime++;
 
-    if (!level.use_step_counter)
+    for (i = 0; i < MAX_PLAYERS; i++)
     {
-      TimePlayed++;
+      struct PlayerInfo *player = &stored_player[i];
 
-      for (i = 0; i < MAX_PLAYERS; i++)
+      if (SHIELD_ON(player))
       {
-       struct PlayerInfo *player = &stored_player[i];
-
-       if (SHIELD_ON(player))
-       {
-         player->shield_normal_time_left--;
+       player->shield_normal_time_left--;
 
-         if (player->shield_deadly_time_left > 0)
-           player->shield_deadly_time_left--;
-       }
+       if (player->shield_deadly_time_left > 0)
+         player->shield_deadly_time_left--;
       }
+    }
+
+    if (!level.use_step_counter)
+    {
+      TimePlayed++;
 
       if (TimeLeft > 0)
       {
@@ -8336,7 +9333,9 @@ void GameActions()
           stored_player[0].StepFrame);
 #endif
 
-#if 1
+#if USE_NEW_MOVE_DELAY
+  AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
+#else
   FrameCounter++;
   TimeFrames++;
 
@@ -8350,6 +9349,11 @@ void GameActions()
     if (stored_player[i].MovPos != 0)
       stored_player[i].StepFrame += move_frames;
 
+#if USE_NEW_MOVE_DELAY
+    if (stored_player[i].move_delay > 0)
+      stored_player[i].move_delay--;
+#endif
+
     if (stored_player[i].drop_delay > 0)
       stored_player[i].drop_delay--;
   }
@@ -8363,6 +9367,12 @@ void GameActions()
     local_player->show_envelope = 0;
   }
 #endif
+
+#if USE_NEW_RANDOMIZE
+  /* use random number generator in every frame to make it less predictable */
+  if (game.engine_version >= VERSION_IDENT(3,1,1,0))
+    RND(1);
+#endif
 }
 
 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
@@ -8434,6 +9444,7 @@ void ScrollLevel(int dx, int dy)
   redraw_mask |= REDRAW_FIELD;
 }
 
+#if 0
 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
 {
   int nextx = x + dx, nexty = y + dy;
@@ -8465,6 +9476,85 @@ static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
 
   return TRUE;
 }
+#endif
+
+static boolean canFallDown(struct PlayerInfo *player)
+{
+  int jx = player->jx, jy = player->jy;
+
+  return (IN_LEV_FIELD(jx, jy + 1) &&
+         (IS_FREE(jx, jy + 1) ||
+#if USE_NEW_BLOCK_STYLE
+          Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
+#endif
+          (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
+         IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
+         !IS_WALKABLE_INSIDE(Feld[jx][jy]));
+}
+
+static boolean canPassField(int x, int y, int move_dir)
+{
+  int opposite_dir = MV_DIR_OPPOSITE(move_dir);
+  int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
+  int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
+  int nextx = x + dx;
+  int nexty = y + dy;
+  int element = Feld[x][y];
+
+  return (IS_PASSABLE_FROM(element, opposite_dir) &&
+         !CAN_MOVE(element) &&
+         IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
+         IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
+         (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
+}
+
+static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
+{
+  int opposite_dir = MV_DIR_OPPOSITE(move_dir);
+  int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
+  int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
+  int newx = x + dx;
+  int newy = y + dy;
+#if 0
+  int nextx = newx + dx;
+  int nexty = newy + dy;
+#endif
+
+#if 1
+  return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+         IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
+#if 0
+         (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
+#endif
+         (IS_DIGGABLE(Feld[newx][newy]) ||
+          IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+          canPassField(newx, newy, move_dir)));
+#else
+#if 1
+  return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+         IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
+         (IS_DIGGABLE(Feld[newx][newy]) ||
+          IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+          canPassField(newx, newy, move_dir)));
+#else
+#if 1
+  return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+         (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
+          IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+          canPassField(newx, newy, move_dir)));
+#else
+  return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+         (IS_DIGGABLE(Feld[newx][newy]) ||
+          IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+          (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
+           !CAN_MOVE(Feld[newx][newy]) &&
+           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
+           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
+           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
+#endif
+#endif
+#endif
+}
 
 static void CheckGravityMovement(struct PlayerInfo *player)
 {
@@ -8477,56 +9567,97 @@ static void CheckGravityMovement(struct PlayerInfo *player)
     int move_dir_horizontal = player->action & MV_HORIZONTAL;
     int move_dir_vertical   = player->action & MV_VERTICAL;
 #endif
-    int move_dir =
-      (player->last_move_dir & MV_HORIZONTAL ?
-       (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
-       (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
-    int jx = player->jx, jy = player->jy;
-    int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
-    int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
-    int new_jx = jx + dx, new_jy = jy + dy;
+
 #if 1
     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
 #else
     boolean player_is_snapping = player->action & JOY_BUTTON_1;
 #endif
+
+    int jx = player->jx, jy = player->jy;
+
+    boolean player_is_moving_to_valid_field =
+      (!player_is_snapping &&
+       (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
+       canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
+
+#if 0
+    int move_dir =
+      (player->last_move_dir & MV_HORIZONTAL ?
+       (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
+       (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
+#endif
+
+#if 0
+    int opposite_dir = MV_DIR_OPPOSITE(move_dir);
+    int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
+    int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
+    int new_jx = jx + dx, new_jy = jy + dy;
+    int nextx = new_jx + dx, nexty = new_jy + dy;
+#endif
+
+#if 1
+
 #if 1
+    boolean player_can_fall_down = canFallDown(player);
+#else
     boolean player_can_fall_down =
       (IN_LEV_FIELD(jx, jy + 1) &&
        (IS_FREE(jx, jy + 1) ||
        (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
+#endif
+
 #else
     boolean player_can_fall_down =
       (IN_LEV_FIELD(jx, jy + 1) &&
        (IS_FREE(jx, jy + 1)));
 #endif
+
+#if 0
     boolean player_is_moving_to_valid_field =
       (
 #if 1
        !player_is_snapping &&
 #endif
+
+#if 1
+       IN_LEV_FIELD(new_jx, new_jy) &&
+       (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
+       (IS_SP_PORT(Feld[new_jx][new_jy]) &&
+        element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
+        IN_LEV_FIELD(nextx, nexty) &&
+        element_info[Feld[nextx][nexty]].access_direction & move_dir))
+#else
        IN_LEV_FIELD(new_jx, new_jy) &&
        (Feld[new_jx][new_jy] == EL_SP_BASE ||
        Feld[new_jx][new_jy] == EL_SAND ||
        (IS_SP_PORT(Feld[new_jx][new_jy]) &&
-        canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
+        canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
     /* !!! extend EL_SAND to anything diggable !!! */
+#endif
+       );
+#endif
 
+#if 0
     boolean player_is_standing_on_valid_field =
       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
-       (IS_WALKABLE(Feld[jx][jy]) &&
-       !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
+       (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
+#endif
 
 #if 0
-    printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
+    printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
           player_can_fall_down,
           player_is_standing_on_valid_field,
           player_is_moving_to_valid_field,
-          (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
+          (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
+          player->effective_action,
+          player->can_fall_into_acid);
 #endif
 
     if (player_can_fall_down &&
+#if 0
        !player_is_standing_on_valid_field &&
+#endif
        !player_is_moving_to_valid_field)
     {
 #if 0
@@ -8634,7 +9765,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 
   /* check if DigField() has caused relocation of the player */
   if (player->jx != jx || player->jy != jy)
-    return MF_NO_ACTION;
+    return MF_NO_ACTION;       /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
 
   StorePlayer[jx][jy] = 0;
   player->last_jx = jx;
@@ -8648,7 +9779,9 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 
   player->step_counter++;
 
+#if 0
   player->drop_delay = 0;
+#endif
 
   PlayerVisit[jx][jy] = FrameCounter;
 
@@ -8657,17 +9790,17 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 #if 0
   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
   {
-    CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
-                                   leave_side);
-    CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
+    CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
+                                     leave_side);
+    CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
   }
 
   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
   {
-    CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
-                                   CE_OTHER_GETS_ENTERED, enter_side);
-    CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
-                          CE_ENTERED_BY_PLAYER, enter_side);
+    CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
+                                     CE_OTHER_GETS_ENTERED, enter_side);
+    CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
+                            CE_ENTERED_BY_PLAYER, enter_side);
   }
 #endif
 
@@ -8709,8 +9842,24 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 #else
 
 #if 1
+
+#if 0
+  printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
+        player->move_delay + player->move_delay_value);
+#endif
+
+#if USE_NEW_MOVE_DELAY
+  if (player->move_delay > 0)
+#else
   if (!FrameReached(&player->move_delay, player->move_delay_value))
+#endif
+  {
+#if 0
+    printf("::: can NOT move\n");
+#endif
+
     return FALSE;
+  }
 #else
   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
@@ -8719,6 +9868,17 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
 #endif
 
+#if 0
+  printf("::: COULD move now\n");
+#endif
+
+#if USE_NEW_MOVE_DELAY
+  player->move_delay = -1;             /* set to "uninitialized" value */
+#endif
+
+  /* store if player is automatically moved to next field */
+  player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
+
   /* remove the last programmed player action */
   player->programmed_action = 0;
 
@@ -8741,7 +9901,13 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     {
       ScrollPlayer(player, SCROLL_GO_ON);
       ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if USE_NEW_MOVE_DELAY
+      AdvanceFrameAndPlayerCounters(player->index_nr);
+#else
       FrameCounter++;
+#endif
+
       DrawAllPlayers();
       BackToFront();
     }
@@ -8845,6 +10011,10 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
   if (moved & MF_MOVING)
   {
+#if 0
+    printf("::: REALLY moves now\n");
+#endif
+
     if (old_jx != jx && old_jy == jy)
       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
     else if (old_jx == jx && old_jy != jy)
@@ -8867,7 +10037,16 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
 #if 0
     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
+
+#if 1
+    if (game.engine_version < VERSION_IDENT(3,1,0,0))
+#endif
     {
+      int move_direction = player->MovDir;
+#if 1
+      int enter_side = MV_DIR_OPPOSITE(move_direction);
+      int leave_side = move_direction;
+#else
       static int trigger_sides[4][2] =
       {
        /* enter side           leave side */
@@ -8876,29 +10055,30 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
        { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
        { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
       };
-      int move_direction = player->MovDir;
       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
+#endif
+      int old_element = Feld[old_jx][old_jy];
+      int new_element = Feld[jx][jy];
 
 #if 1
-      if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
-      {
-       CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+      /* !!! TEST ONLY !!! */
+      if (IS_CUSTOM_ELEMENT(old_element))
+       CheckElementChangeByPlayer(old_jx, old_jy, old_element,
+                                  CE_LEFT_BY_PLAYER,
+                                  player->index_bit, leave_side);
+
+      CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
                                          CE_OTHER_GETS_LEFT,
                                          player->index_bit, leave_side);
-       CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
-                                CE_LEFT_BY_PLAYER,
-                                player->index_bit, leave_side);
-      }
 
-      if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
-      {
-       CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
+      if (IS_CUSTOM_ELEMENT(new_element))
+       CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
+                                  player->index_bit, enter_side);
+
+      CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
                                          CE_OTHER_GETS_ENTERED,
                                          player->index_bit, enter_side);
-       CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
-                                player->index_bit, enter_side);
-      }
 #endif
 
     }
@@ -8914,8 +10094,23 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->last_move_dir = MV_NO_MOVING;
     */
     player->is_moving = FALSE;
+
+#if USE_NEW_MOVE_STYLE
+    /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
+    /* ensure that the player is also allowed to move in the next frame */
+    /* (currently, the player is forced to wait eight frames before he can try
+       again!!!) */
+
+    if (game.engine_version >= VERSION_IDENT(3,1,1,0))
+      player->move_delay = 0;  /* allow direct movement in the next frame */
+#endif
   }
 
+#if USE_NEW_MOVE_DELAY
+  if (player->move_delay == -1)                /* not yet initialized by DigField() */
+    player->move_delay = player->move_delay_value;
+#endif
+
   if (game.engine_version < VERSION_IDENT(3,0,7,0))
   {
     TestIfHeroTouchesBadThing(jx, jy);
@@ -8942,8 +10137,37 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     player->actual_frame_counter = FrameCounter;
     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
+#if 0
+    printf("::: %06d: %d,%d: %d (%d) [%d]\n",
+          FrameCounter,
+          last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
+          player->block_delay);
+#endif
+
+#if USE_NEW_BLOCK_STYLE
+
+#if 0
+    if (player->block_delay <= 0)
+      printf("::: ALERT! block_delay == %d\n", player->block_delay);
+#endif
+
+    if (player->block_delay > 0 &&
+       Feld[last_jx][last_jy] == EL_EMPTY)
+    {
+      Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+      MovDelay[last_jx][last_jy] = player->block_delay + 1;
+    }
+#else
+#if USE_NEW_MOVE_STYLE
+    if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
+        player->block_last_field) &&
+       Feld[last_jx][last_jy] == EL_EMPTY)
+      Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+#else
     if (Feld[last_jx][last_jy] == EL_EMPTY)
       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+#endif
+#endif
 
 #if 0
     DrawPlayer(player);
@@ -8957,9 +10181,16 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
+#if USE_NEW_BLOCK_STYLE
+#else
   if (!player->block_last_field &&
       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+#if 1
+    RemoveField(last_jx, last_jy);
+#else
     Feld[last_jx][last_jy] = EL_EMPTY;
+#endif
+#endif
 
   /* before DrawPlayer() to draw correct player graphic for this case */
   if (player->MovPos == 0)
@@ -8996,9 +10227,16 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     }
 #endif
 
+#if USE_NEW_BLOCK_STYLE
+#else
     if (player->block_last_field &&
        Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+#if 1
+      RemoveField(last_jx, last_jy);
+#else
       Feld[last_jx][last_jy] = EL_EMPTY;
+#endif
+#endif
 
     player->last_jx = jx;
     player->last_jy = jy;
@@ -9018,7 +10256,15 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 #if 1
     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
     /* this breaks one level: "machine", level 000 */
+#if 0
+    if (game.engine_version >= VERSION_IDENT(3,1,0,0))
+#endif
     {
+      int move_direction = player->MovDir;
+#if 1
+      int enter_side = MV_DIR_OPPOSITE(move_direction);
+      int leave_side = move_direction;
+#else
       static int trigger_sides[4][2] =
       {
        /* enter side           leave side */
@@ -9027,31 +10273,32 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
        { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
       };
-      int move_direction = player->MovDir;
       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
+#endif
       int old_jx = last_jx;
       int old_jy = last_jy;
+      int old_element = Feld[old_jx][old_jy];
+      int new_element = Feld[jx][jy];
 
 #if 1
-      if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
-      {
-       CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+      /* !!! TEST ONLY !!! */
+      if (IS_CUSTOM_ELEMENT(old_element))
+       CheckElementChangeByPlayer(old_jx, old_jy, old_element,
+                                  CE_LEFT_BY_PLAYER,
+                                  player->index_bit, leave_side);
+
+      CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
                                          CE_OTHER_GETS_LEFT,
                                          player->index_bit, leave_side);
-       CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
-                                CE_LEFT_BY_PLAYER,
-                                player->index_bit, leave_side);
-      }
 
-      if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
-      {
-       CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
+      if (IS_CUSTOM_ELEMENT(new_element))
+       CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
+                                  player->index_bit, enter_side);
+
+      CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
                                          CE_OTHER_GETS_ENTERED,
                                          player->index_bit, enter_side);
-       CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
-                                player->index_bit, enter_side);
-      }
 #endif
 
     }
@@ -9062,7 +10309,12 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       TestIfHeroTouchesBadThing(jx, jy);
       TestIfPlayerTouchesCustomElement(jx, jy);
 #if 1
-      TestIfElementTouchesCustomElement(jx, jy);       /* for empty space */
+#if 1
+      /* needed because pushed element has not yet reached its destination,
+        so it would trigger a change event at its previous field location */
+      if (!player->is_pushing)
+#endif
+       TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
 #endif
 
       if (!player->active)
@@ -9075,19 +10327,6 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
       TimePlayed++;
 
-      for (i = 0; i < MAX_PLAYERS; i++)
-      {
-       struct PlayerInfo *player = &stored_player[i];
-
-       if (SHIELD_ON(player))
-       {
-         player->shield_normal_time_left--;
-
-         if (player->shield_deadly_time_left > 0)
-           player->shield_deadly_time_left--;
-       }
-      }
-
       if (TimeLeft > 0)
       {
        TimeLeft--;
@@ -9190,11 +10429,20 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       else
        continue;               /* center and border element do not touch */
 
-      CheckTriggeredElementChangePlayer(xx, yy, border_element,
-                                       CE_OTHER_GETS_TOUCHED,
-                                       player->index_bit, border_side);
-      CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
-                              player->index_bit, border_side);
+#if 1
+      /* !!! TEST ONLY !!! */
+      CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, border_side);
+      CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
+                                         CE_OTHER_GETS_TOUCHED,
+                                         player->index_bit, border_side);
+#else
+      CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
+                                         CE_OTHER_GETS_TOUCHED,
+                                         player->index_bit, border_side);
+      CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, border_side);
+#endif
     }
     else if (IS_PLAYER(xx, yy))
     {
@@ -9206,11 +10454,20 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
          continue;             /* center and border element do not touch */
       }
 
-      CheckTriggeredElementChangePlayer(x, y, center_element,
-                                       CE_OTHER_GETS_TOUCHED,
-                                       player->index_bit, center_side);
-      CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
-                              player->index_bit, center_side);
+#if 1
+      /* !!! TEST ONLY !!! */
+      CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, center_side);
+      CheckTriggeredElementChangeByPlayer(x, y, center_element,
+                                         CE_OTHER_GETS_TOUCHED,
+                                         player->index_bit, center_side);
+#else
+      CheckTriggeredElementChangeByPlayer(x, y, center_element,
+                                         CE_OTHER_GETS_TOUCHED,
+                                         player->index_bit, center_side);
+      CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, center_side);
+#endif
 
       break;
     }
@@ -9244,7 +10501,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
   boolean change_center_element = FALSE;
   int center_element_change_page = 0;
   int center_element = Feld[x][y];     /* should always be non-moving! */
-  int border_trigger_element;
+  int border_trigger_element = EL_UNDEFINED;
   int i, j;
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
@@ -9319,8 +10576,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
          printf("::: border_element %d, %d\n", x, y);
 #endif
 
-         CheckElementChangePage(xx, yy, border_element, center_element,
-                                CE_OTHER_IS_TOUCHING, j);
+         CheckElementChangeByPage(xx, yy, border_element, center_element,
+                                  CE_OTHER_IS_TOUCHING, j);
          break;
        }
       }
@@ -9333,8 +10590,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
     printf("::: center_element %d, %d\n", x, y);
 #endif
 
-    CheckElementChangePage(x, y, center_element, border_trigger_element,
-                          CE_OTHER_IS_TOUCHING, center_element_change_page);
+    CheckElementChangeByPage(x, y, center_element, border_trigger_element,
+                            CE_OTHER_IS_TOUCHING, center_element_change_page);
   }
 }
 
@@ -9364,8 +10621,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
   touched_element = (IN_LEV_FIELD(hitx, hity) ?
                     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
 
-  CheckElementChangeSide(x, y, hitting_element, touched_element,
-                        CE_HITTING_SOMETHING, direction);
+  CheckElementChangeBySide(x, y, hitting_element, touched_element,
+                          CE_HITTING_SOMETHING, direction);
 
   if (IN_LEV_FIELD(hitx, hity))
   {
@@ -9387,8 +10644,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
     {
       int i;
 
-      CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
-                            CE_HIT_BY_SOMETHING, opposite_direction);
+      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+                              CE_HIT_BY_SOMETHING, opposite_direction);
 
       if (IS_CUSTOM_ELEMENT(hitting_element) &&
          HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
@@ -9409,8 +10666,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementChangePage(x, y, hitting_element, touched_element,
-                                  CE_OTHER_IS_HITTING, i);
+           CheckElementChangeByPage(x, y, hitting_element, touched_element,
+                                    CE_OTHER_IS_HITTING, i);
            break;
          }
        }
@@ -9434,8 +10691,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementChangePage(hitx, hity, touched_element,
-                                  hitting_element, CE_OTHER_GETS_HIT, i);
+           CheckElementChangeByPage(hitx, hity, touched_element,
+                                    hitting_element, CE_OTHER_GETS_HIT, i);
            break;
          }
        }
@@ -9471,8 +10728,8 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
   touched_element = (IN_LEV_FIELD(hitx, hity) ?
                     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
 
-  CheckElementChangeSide(x, y, hitting_element, touched_element,
-                        EP_CAN_SMASH_EVERYTHING, direction);
+  CheckElementChangeBySide(x, y, hitting_element, touched_element,
+                          EP_CAN_SMASH_EVERYTHING, direction);
 
   if (IN_LEV_FIELD(hitx, hity))
   {
@@ -9494,8 +10751,8 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
     {
       int i;
 
-      CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
-                            CE_SMASHED_BY_SOMETHING, opposite_direction);
+      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+                              CE_SMASHED_BY_SOMETHING, opposite_direction);
 
       if (IS_CUSTOM_ELEMENT(hitting_element) &&
          HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
@@ -9516,8 +10773,8 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementChangePage(x, y, hitting_element, touched_element,
-                                  CE_OTHER_IS_SMASHING, i);
+           CheckElementChangeByPage(x, y, hitting_element, touched_element,
+                                    CE_OTHER_IS_SMASHING, i);
            break;
          }
        }
@@ -9541,8 +10798,8 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementChangePage(hitx, hity, touched_element,
-                                  hitting_element, CE_OTHER_GETS_SMASHED, i);
+           CheckElementChangeByPage(hitx, hity, touched_element,
+                                    hitting_element, CE_OTHER_GETS_SMASHED,i);
            break;
          }
        }
@@ -9555,6 +10812,7 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 {
   int i, kill_x = -1, kill_y = -1;
+  int bad_element = -1;
   static int test_xy[4][2] =
   {
     { 0, -1 },
@@ -9576,6 +10834,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 
     test_x = good_x + test_xy[i][0];
     test_y = good_y + test_xy[i][1];
+
     if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
@@ -9596,6 +10855,8 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
     {
       kill_x = test_x;
       kill_y = test_y;
+      bad_element = test_element;
+
       break;
     }
   }
@@ -9606,10 +10867,18 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
     {
       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
 
+#if 1
+      if (player->shield_deadly_time_left > 0 &&
+         !IS_INDESTRUCTIBLE(bad_element))
+       Bang(kill_x, kill_y);
+      else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
+       KillHero(player);
+#else
       if (player->shield_deadly_time_left > 0)
        Bang(kill_x, kill_y);
       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
        KillHero(player);
+#endif
     }
     else
       Bang(good_x, good_y);
@@ -9698,10 +10967,18 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
     {
       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
 
+#if 1
+      if (player->shield_deadly_time_left > 0 &&
+         !IS_INDESTRUCTIBLE(bad_element))
+       Bang(bad_x, bad_y);
+      else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
+       KillHero(player);
+#else
       if (player->shield_deadly_time_left > 0)
        Bang(bad_x, bad_y);
       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
        KillHero(player);
+#endif
     }
     else
       Bang(kill_x, kill_y);
@@ -9883,16 +11160,11 @@ int DigField(struct PlayerInfo *player,
             int oldx, int oldy, int x, int y,
             int real_dx, int real_dy, int mode)
 {
-  static int trigger_sides[4] =
-  {
-    CH_SIDE_RIGHT,     /* moving left  */
-    CH_SIDE_LEFT,      /* moving right */
-    CH_SIDE_BOTTOM,    /* moving up    */
-    CH_SIDE_TOP,       /* moving down  */
-  };
 #if 0
   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
 #endif
+  boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
+  boolean player_was_pushing = player->is_pushing;
   int jx = oldx, jy = oldy;
   int dx = x - jx, dy = y - jy;
   int nextx = x + dx, nexty = y + dy;
@@ -9901,25 +11173,43 @@ int DigField(struct PlayerInfo *player,
                        dy == -1 ? MV_UP :
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
+#if 1
+  int dig_side = MV_DIR_OPPOSITE(move_direction);
+#else
+  static int trigger_sides[4] =
+  {
+    CH_SIDE_RIGHT,     /* moving left  */
+    CH_SIDE_LEFT,      /* moving right */
+    CH_SIDE_BOTTOM,    /* moving up    */
+    CH_SIDE_TOP,       /* moving down  */
+  };
   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
+#endif
   int old_element = Feld[jx][jy];
   int element;
 
-  if (player->MovPos == 0)
+  if (is_player)               /* function can also be called by EL_PENGUIN */
   {
-    player->is_digging = FALSE;
-    player->is_collecting = FALSE;
-  }
+    if (player->MovPos == 0)
+    {
+      player->is_digging = FALSE;
+      player->is_collecting = FALSE;
+    }
 
-  if (player->MovPos == 0)     /* last pushing move finished */
-    player->is_pushing = FALSE;
+    if (player->MovPos == 0)   /* last pushing move finished */
+      player->is_pushing = FALSE;
 
-  if (mode == DF_NO_PUSH)      /* player just stopped pushing */
-  {
-    player->is_switching = FALSE;
-    player->push_delay = 0;
+    if (mode == DF_NO_PUSH)    /* player just stopped pushing */
+    {
+      player->is_switching = FALSE;
+#if USE_NEW_PUSH_DELAY
+      player->push_delay = -1;
+#else
+      player->push_delay = 0;
+#endif
 
-    return MF_NO_ACTION;
+      return MF_NO_ACTION;
+    }
   }
 
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
@@ -9970,18 +11260,38 @@ int DigField(struct PlayerInfo *player,
 
 #endif
 
-  if (IS_WALKABLE(old_element) &&
-      !(element_info[old_element].access_direction & move_direction))
+  if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
+    return MF_NO_ACTION;       /* field has no opening in this direction */
+
+  if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
     return MF_NO_ACTION;       /* field has no opening in this direction */
 
   element = Feld[x][y];
 
+  if (!is_player && !IS_COLLECTIBLE(element))  /* penguin cannot collect it */
+    return MF_NO_ACTION;
+
   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
       game.engine_version >= VERSION_IDENT(2,2,0,0))
     return MF_NO_ACTION;
 
+#if 1
+  if (game.gravity && is_player && !player->is_auto_moving &&
+      canFallDown(player) && move_direction != MV_DOWN &&
+      !canMoveToValidFieldWithGravity(jx, jy, move_direction))
+    return MF_NO_ACTION;       /* player cannot walk here due to gravity */
+#endif
+
+#if 0
+  if (element == EL_EMPTY_SPACE &&
+      game.gravity && !player->is_auto_moving &&
+      canFallDown(player) && move_direction != MV_DOWN)
+    return MF_NO_ACTION;       /* player cannot walk here due to gravity */
+#endif
+
   switch (element)
   {
+#if 0
     case EL_SP_PORT_LEFT:
     case EL_SP_PORT_RIGHT:
     case EL_SP_PORT_UP:
@@ -10049,6 +11359,7 @@ int DigField(struct PlayerInfo *player,
 
       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
       break;
+#endif
 
 #if 0
     case EL_TUBE_ANY:
@@ -10097,21 +11408,35 @@ int DigField(struct PlayerInfo *player,
 
     default:
 
+#if 1
+      if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
+#else
       if (IS_WALKABLE(element))
+#endif
       {
+       int sound_element = SND_ELEMENT(element);
        int sound_action = ACTION_WALKING;
 
-       if (!(element_info[element].access_direction & opposite_direction))
+#if 0
+       if (!ACCESS_FROM(element, opposite_direction))
          return MF_NO_ACTION;  /* field not accessible from this direction */
+#endif
 
-       if (element >= EL_GATE_1 && element <= EL_GATE_4)
+#if 0
+       if (element == EL_EMPTY_SPACE &&
+           game.gravity && !player->is_auto_moving &&
+           canFallDown(player) && move_direction != MV_DOWN)
+         return MF_NO_ACTION;  /* player cannot walk here due to gravity */
+#endif
+
+       if (IS_RND_GATE(element))
        {
-         if (!player->key[element - EL_GATE_1])
+         if (!player->key[RND_GATE_NR(element)])
            return MF_NO_ACTION;
        }
-       else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
+       else if (IS_RND_GATE_GRAY(element))
        {
-         if (!player->key[element - EL_GATE_1_GRAY])
+         if (!player->key[RND_GATE_GRAY_NR(element)])
            return MF_NO_ACTION;
        }
        else if (element == EL_EXIT_OPEN ||
@@ -10126,37 +11451,80 @@ int DigField(struct PlayerInfo *player,
        }
 
        /* play sound from background or player, whatever is available */
-       if (element_info[element].sound[sound_action] != SND_UNDEFINED)
-         PlayLevelSoundElementAction(x, y, element, sound_action);
+       if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
+         PlayLevelSoundElementAction(x, y, sound_element, sound_action);
        else
          PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
 
        break;
       }
+#if 1
+      else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
+#else
       else if (IS_PASSABLE(element))
+#endif
       {
+#if 0
+       if (!canPassField(x, y, move_direction))
+         return MF_NO_ACTION;
+#else
+
+#if 0
+#if 1
+       if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
+           !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
+           (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
+         return MF_NO_ACTION;
+#else
        if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
          return MF_NO_ACTION;
+#endif
+#endif
 
+#if 1
+       if (!ACCESS_FROM(element, opposite_direction))
+         return MF_NO_ACTION;  /* field not accessible from this direction */
+#else
        if (IS_CUSTOM_ELEMENT(element) &&
-           !(element_info[element].access_direction & opposite_direction))
+           !ACCESS_FROM(element, opposite_direction))
          return MF_NO_ACTION;  /* field not accessible from this direction */
+#endif
 
 #if 1
        if (CAN_MOVE(element))  /* only fixed elements can be passed! */
          return MF_NO_ACTION;
 #endif
 
-       if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
+#endif
+
+       if (IS_EM_GATE(element))
        {
-         if (!player->key[element - EL_EM_GATE_1])
+         if (!player->key[EM_GATE_NR(element)])
            return MF_NO_ACTION;
        }
-       else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
+       else if (IS_EM_GATE_GRAY(element))
        {
-         if (!player->key[element - EL_EM_GATE_1_GRAY])
+         if (!player->key[EM_GATE_GRAY_NR(element)])
            return MF_NO_ACTION;
        }
+       else if (IS_SP_PORT(element))
+       {
+         if (element == EL_SP_GRAVITY_PORT_LEFT ||
+             element == EL_SP_GRAVITY_PORT_RIGHT ||
+             element == EL_SP_GRAVITY_PORT_UP ||
+             element == EL_SP_GRAVITY_PORT_DOWN)
+           game.gravity = !game.gravity;
+         else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
+                  element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
+                  element == EL_SP_GRAVITY_ON_PORT_UP ||
+                  element == EL_SP_GRAVITY_ON_PORT_DOWN)
+           game.gravity = TRUE;
+         else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
+                  element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
+                  element == EL_SP_GRAVITY_OFF_PORT_UP ||
+                  element == EL_SP_GRAVITY_OFF_PORT_DOWN)
+           game.gravity = FALSE;
+       }
 
        /* automatically move to the next field with double speed */
        player->programmed_action = move_direction;
@@ -10194,8 +11562,8 @@ int DigField(struct PlayerInfo *player,
 
        PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
 
-       CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
-                                         player->index_bit, CH_SIDE_ANY);
+       CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
+                                           player->index_bit, dig_side);
 
 #if 1
        if (mode == DF_SNAP)
@@ -10208,7 +11576,7 @@ int DigField(struct PlayerInfo *player,
       {
        RemoveField(x, y);
 
-       if (mode != DF_SNAP)
+       if (is_player && mode != DF_SNAP)
        {
          GfxElement[x][y] = element;
          player->is_collecting = TRUE;
@@ -10247,15 +11615,11 @@ int DigField(struct PlayerInfo *player,
        {
          player->dynabomb_xl = TRUE;
        }
-       else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
-                (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
+       else if (IS_KEY(element))
        {
-         int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
-                       element - EL_KEY_1 : element - EL_EM_KEY_1);
-
-         player->key[key_nr] = TRUE;
+         player->key[KEY_NR(element)] = TRUE;
 
-         DrawGameValue_Keys(player);
+         DrawGameValue_Keys(player->key);
 
          redraw_mask |= REDRAW_DOOR_1;
        }
@@ -10267,7 +11631,8 @@ int DigField(struct PlayerInfo *player,
          ShowEnvelope(element - EL_ENVELOPE_1);
 #endif
        }
-       else if (IS_DROPPABLE(element)) /* can be collected and dropped */
+       else if (IS_DROPPABLE(element) ||
+                IS_THROWABLE(element)) /* can be collected and dropped */
        {
          int i;
 
@@ -10293,9 +11658,10 @@ int DigField(struct PlayerInfo *player,
        RaiseScoreElement(element);
        PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
-       CheckTriggeredElementChangePlayer(x, y, element,
-                                         CE_OTHER_GETS_COLLECTED,
-                                         player->index_bit, CH_SIDE_ANY);
+       if (is_player)
+         CheckTriggeredElementChangeByPlayer(x, y, element,
+                                             CE_OTHER_GETS_COLLECTED,
+                                             player->index_bit, dig_side);
 
 #if 1
        if (mode == DF_SNAP)
@@ -10342,11 +11708,25 @@ int DigField(struct PlayerInfo *player,
 #endif
 
 #if 1
-       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+
+#if 1
+       if (game.engine_version >= VERSION_IDENT(3,1,0,0))
+       {
+         if (player->push_delay_value == -1 || !player_was_pushing)
+           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+       }
+       else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
        {
          if (player->push_delay_value == -1)
            player->push_delay_value = GET_NEW_PUSH_DELAY(element);
        }
+#else
+       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+       {
+         if (player->push_delay_value == -1 || !player_was_pushing)
+           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+       }
+#endif
        else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
        {
          if (!player->is_pushing)
@@ -10366,9 +11746,12 @@ int DigField(struct PlayerInfo *player,
 #endif
 
 #if 0
-       printf("::: push delay: %ld [%d, %d] [%d]\n",
-              player->push_delay_value, FrameCounter, game.engine_version,
-              player->is_pushing);
+       printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
+              player->push_delay, player->push_delay_value,
+              FrameCounter, game.engine_version,
+              player_was_pushing, player->is_pushing,
+              element, element_info[element].token_name,
+              GET_NEW_PUSH_DELAY(element));
 #endif
 
        player->is_pushing = TRUE;
@@ -10382,16 +11765,56 @@ int DigField(struct PlayerInfo *player,
        if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
          return MF_NO_ACTION;
 
+#if USE_NEW_PUSH_DELAY
+
+#if 0
+       if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
+         printf("::: ALERT: %d, %d [%d / %d]\n",
+                player->push_delay, player->push_delay2,
+                FrameCounter, FrameCounter / 50);
+#endif
+
+       if (player->push_delay == -1)   /* new pushing; restart delay */
+         player->push_delay = 0;
+#else
        if (player->push_delay == 0)    /* new pushing; restart delay */
          player->push_delay = FrameCounter;
+#endif
+
+#if USE_NEW_PUSH_DELAY
+#if 0
+       if ( (player->push_delay > 0) != (!xxx_fr) )
+         printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
+                player->push_delay,
+                xxx_pdv2, player->push_delay2, player->push_delay_value,
+                FrameCounter, FrameCounter / 50);
+#endif
+
+#if 0
+       if (player->push_delay > 0 &&
+           !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
+           element != EL_SPRING && element != EL_BALLOON)
+#else
+       /* !!! */
+       if (player->push_delay < player->push_delay_value &&
+           !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
+           element != EL_SPRING && element != EL_BALLOON)
+#endif
 
+#else
        if (!FrameReached(&player->push_delay, player->push_delay_value) &&
            !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
            element != EL_SPRING && element != EL_BALLOON)
+#endif
        {
          /* make sure that there is no move delay before next try to push */
+#if USE_NEW_MOVE_DELAY
+         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+           player->move_delay = 0;
+#else
          if (game.engine_version >= VERSION_IDENT(3,0,7,1))
            player->move_delay = INITIAL_MOVE_DELAY_OFF;
+#endif
 
          return MF_NO_ACTION;
        }
@@ -10451,17 +11874,54 @@ int DigField(struct PlayerInfo *player,
        else
          player->push_delay_value = -1;        /* get new value later */
 
-       CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
-                                         player->index_bit, dig_side);
-       CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
-                                player->index_bit, dig_side);
+#if USE_PUSH_BUGFIX
+       /* now: check for element change _after_ element has been pushed! */
+#if 1
+       if (game.use_bug_change_when_pushing)
+#else
+       if (game.engine_version < VERSION_IDENT(3,1,0,0))
+#endif
+       {
+         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
+                                    player->index_bit, dig_side);
+         CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
+                                             player->index_bit, dig_side);
+       }
+
+#else
+
+#if 1
+       /* check for element change _after_ element has been pushed! */
+#else
+
+#if 1
+       /* !!! TEST ONLY !!! */
+       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
+                                  player->index_bit, dig_side);
+       CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
+                                           player->index_bit, dig_side);
+#else
+       CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
+                                           player->index_bit, dig_side);
+       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
+                                  player->index_bit, dig_side);
+#endif
+#endif
+
+#endif
 
        break;
       }
       else if (IS_SWITCHABLE(element))
       {
        if (PLAYER_SWITCHING(player, x, y))
+       {
+         CheckTriggeredElementChangeByPlayer(x,y, element,
+                                             CE_OTHER_GETS_PRESSED,
+                                             player->index_bit, dig_side);
+
          return MF_ACTION;
+       }
 
        player->is_switching = TRUE;
        player->switch_x = x;
@@ -10548,6 +12008,13 @@ int DigField(struct PlayerInfo *player,
 #endif
        }
 
+       CheckTriggeredElementChangeByPlayer(x, y, element,
+                                           CE_OTHER_IS_SWITCHING,
+                                           player->index_bit, dig_side);
+
+       CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
+                                           player->index_bit, dig_side);
+
        return MF_ACTION;
       }
       else
@@ -10558,23 +12025,44 @@ int DigField(struct PlayerInfo *player,
          player->switch_x = x;
          player->switch_y = y;
 
-         CheckTriggeredElementChangePlayer(x, y, element,
-                                           CE_OTHER_IS_SWITCHING,
-                                           player->index_bit, dig_side);
-         CheckElementChangePlayer(x, y, element, CE_SWITCHED,
-                                  player->index_bit, dig_side);
+#if 1
+         /* !!! TEST ONLY !!! */
+         CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
+                                    player->index_bit, dig_side);
+         CheckTriggeredElementChangeByPlayer(x, y, element,
+                                             CE_OTHER_IS_SWITCHING,
+                                             player->index_bit, dig_side);
+#else
+         CheckTriggeredElementChangeByPlayer(x, y, element,
+                                             CE_OTHER_IS_SWITCHING,
+                                             player->index_bit, dig_side);
+         CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
+                                    player->index_bit, dig_side);
+#endif
        }
 
-       CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
-                                         player->index_bit, dig_side);
-       CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
-                                player->index_bit, dig_side);
+#if 1
+       /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
+       CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
+                                  player->index_bit, dig_side);
+       CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
+                                           player->index_bit, dig_side);
+#else
+       CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
+                                           player->index_bit, dig_side);
+       CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
+                                  player->index_bit, dig_side);
+#endif
       }
 
       return MF_NO_ACTION;
   }
 
+#if USE_NEW_PUSH_DELAY
+  player->push_delay = -1;
+#else
   player->push_delay = 0;
+#endif
 
   if (Feld[x][y] != element)           /* really digged/collected something */
     player->is_collecting = !player->is_digging;
@@ -10592,10 +12080,10 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
 
 #if 0
-  if (player->MovPos)
+  if (player->MovPos != 0)
     return FALSE;
 #else
-  if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
+  if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
     return FALSE;
 #endif
 
@@ -10652,23 +12140,56 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
     player->is_collecting = FALSE;
   }
 
+#if 1
+  if (player->MovPos != 0)     /* prevent graphic bugs in versions < 2.2.0 */
+    DrawLevelField(player->last_jx, player->last_jy);
+#endif
+
   DrawLevelField(x, y);
+
+#if 0
   BackToFront();
+#endif
 
   return TRUE;
 }
 
 boolean DropElement(struct PlayerInfo *player)
 {
-  int jx = player->jx, jy = player->jy;
-  int old_element = Feld[jx][jy];
-  int new_element = (player->inventory_size > 0 ?
-                    player->inventory_element[player->inventory_size - 1] :
-                    player->inventory_infinite_element != EL_UNDEFINED ?
-                    player->inventory_infinite_element :
-                    player->dynabombs_left > 0 ?
-                    EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
-                    EL_UNDEFINED);
+  int old_element, new_element;
+  int dropx = player->jx, dropy = player->jy;
+  int drop_direction = player->MovDir;
+#if 1
+  int drop_side = drop_direction;
+#else
+  static int trigger_sides[4] =
+  {
+    CH_SIDE_LEFT,      /* dropping left  */
+    CH_SIDE_RIGHT,     /* dropping right */
+    CH_SIDE_TOP,       /* dropping up    */
+    CH_SIDE_BOTTOM,    /* dropping down  */
+  };
+  int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
+#endif
+  int drop_element = (player->inventory_size > 0 ?
+                     player->inventory_element[player->inventory_size - 1] :
+                     player->inventory_infinite_element != EL_UNDEFINED ?
+                     player->inventory_infinite_element :
+                     player->dynabombs_left > 0 ?
+                     EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
+                     EL_UNDEFINED);
+
+  if (IS_THROWABLE(drop_element))
+  {
+    dropx += GET_DX_FROM_DIR(drop_direction);
+    dropy += GET_DY_FROM_DIR(drop_direction);
+
+    if (!IN_LEV_FIELD(dropx, dropy))
+      return FALSE;
+  }
+
+  old_element = Feld[dropx][dropy];    /* old element at dropping position */
+  new_element = drop_element;          /* default: no change when dropping */
 
   /* check if player is active, not moving and ready to drop */
   if (!player->active || player->MovPos || player->drop_delay > 0)
@@ -10701,10 +12222,10 @@ boolean DropElement(struct PlayerInfo *player)
 #endif
 
   if (old_element != EL_EMPTY)
-    Back[jx][jy] = old_element;                /* store old element on this field */
+    Back[dropx][dropy] = old_element;  /* store old element on this field */
 
-  ResetGfxAnimation(jx, jy);
-  ResetRandomAnimationValue(jx, jy);
+  ResetGfxAnimation(dropx, dropy);
+  ResetRandomAnimationValue(dropx, dropy);
 
   if (player->inventory_size > 0 ||
       player->inventory_infinite_element != EL_UNDEFINED)
@@ -10725,25 +12246,35 @@ boolean DropElement(struct PlayerInfo *player)
        new_element = EL_SP_DISK_RED_ACTIVE;
     }
 
-    Feld[jx][jy] = new_element;
+    Feld[dropx][dropy] = new_element;
 
-    if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
-      DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
+    if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
+      DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
+                         el2img(Feld[dropx][dropy]), 0);
 
-    PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
+    PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
 
 #if 1
     /* needed if previous element just changed to "empty" in the last frame */
-    Changed[jx][jy] = 0;               /* allow another change */
+    Changed[dropx][dropy] = 0;         /* allow another change */
 #endif
 
-    CheckTriggeredElementChangePlayer(jx, jy, new_element,
-                                     CE_OTHER_GETS_DROPPED,
-                                     player->index_bit, CH_SIDE_ANY);
-    CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
-                            player->index_bit, CH_SIDE_ANY);
+#if 1
+    /* !!! TEST ONLY !!! */
+    CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
+                              player->index_bit, drop_side);
+    CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
+                                       CE_OTHER_GETS_DROPPED,
+                                       player->index_bit, drop_side);
+#else
+    CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
+                                       CE_OTHER_GETS_DROPPED,
+                                       player->index_bit, drop_side);
+    CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
+                              player->index_bit, drop_side);
+#endif
 
-    TestIfElementTouchesCustomElement(jx, jy);
+    TestIfElementTouchesCustomElement(dropx, dropy);
   }
   else         /* player is dropping a dyna bomb */
   {
@@ -10753,74 +12284,93 @@ boolean DropElement(struct PlayerInfo *player)
     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
 #endif
 
-    Feld[jx][jy] = new_element;
+    Feld[dropx][dropy] = new_element;
 
-    if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
-      DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
+    if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
+      DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
+                         el2img(Feld[dropx][dropy]), 0);
 
-    PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
+    PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
   }
 
 
 
 #if 1
 
-  if (Feld[jx][jy] == new_element)     /* uninitialized unless CE change */
+  if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
   {
 #if 1
-    InitField_WithBug1(jx, jy, FALSE);
+    InitField_WithBug1(dropx, dropy, FALSE);
 #else
-    InitField(jx, jy, FALSE);
-    if (CAN_MOVE(Feld[jx][jy]))
-      InitMovDir(jx, jy);
+    InitField(dropx, dropy, FALSE);
+    if (CAN_MOVE(Feld[dropx][dropy]))
+      InitMovDir(dropx, dropy);
 #endif
   }
 
-  new_element = Feld[jx][jy];
+  new_element = Feld[dropx][dropy];    /* element might have changed */
 
   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
   {
+#if 0
     int move_stepsize = element_info[new_element].move_stepsize;
-    int direction, dx, dy, nextx, nexty;
+#endif
+    int move_direction, nextx, nexty;
 
     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
-      MovDir[jx][jy] = player->MovDir;
+      MovDir[dropx][dropy] = drop_direction;
 
-    direction = MovDir[jx][jy];
-    dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
-    dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
-    nextx = jx + dx;
-    nexty = jy + dy;
+    move_direction = MovDir[dropx][dropy];
+    nextx = dropx + GET_DX_FROM_DIR(move_direction);
+    nexty = dropy + GET_DY_FROM_DIR(move_direction);
 
-    if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
+#if 1
+    Changed[dropx][dropy] = 0;         /* allow another change */
+    CheckCollision[dropx][dropy] = 2;
+#else
+
+    if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
     {
 #if 0
-      WasJustMoving[jx][jy] = 3;
+      WasJustMoving[dropx][dropy] = 3;
 #else
-      InitMovingField(jx, jy, direction);
-      ContinueMoving(jx, jy);
+#if 1
+      InitMovingField(dropx, dropy, move_direction);
+      ContinueMoving(dropx, dropy);
+#endif
 #endif
     }
+#if 0
+    /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
     else
     {
-      Changed[jx][jy] = 0;             /* allow another change */
+      Changed[dropx][dropy] = 0;       /* allow another change */
 
 #if 1
-      TestIfElementHitsCustomElement(jx, jy, direction);
+      TestIfElementHitsCustomElement(dropx, dropy, move_direction);
 #else
-      CheckElementChangeSide(jx, jy, new_element, touched_element,
-                            CE_HITTING_SOMETHING, direction);
+      CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
+                              CE_HITTING_SOMETHING, move_direction);
 #endif
     }
+#endif
 
+#endif
+
+#if 0
     player->drop_delay = 2 * TILEX / move_stepsize + 1;
+#endif
   }
 
 #if 0
   player->drop_delay = 8 + 8 + 8;
 #endif
 
+#if 1
+  player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
+#endif
+
 #endif
 
   player->is_dropping = TRUE;
@@ -10908,7 +12458,7 @@ static void PlayLevelSoundAction(int x, int y, int action)
 
 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
 {
-  int sound_effect = element_info[element].sound[action];
+  int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
 
   if (sound_effect != SND_UNDEFINED)
     PlayLevelSound(x, y, sound_effect);
@@ -10917,7 +12467,7 @@ static void PlayLevelSoundElementAction(int x, int y, int element, int action)
 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
                                              int action)
 {
-  int sound_effect = element_info[element].sound[action];
+  int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
 
   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
     PlayLevelSound(x, y, sound_effect);
@@ -10925,7 +12475,7 @@ static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
 
 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
 {
-  int sound_effect = element_info[Feld[x][y]].sound[action];
+  int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
 
   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
     PlayLevelSound(x, y, sound_effect);
@@ -10933,7 +12483,7 @@ static void PlayLevelSoundActionIfLoop(int x, int y, int action)
 
 static void StopLevelSoundActionIfLoop(int x, int y, int action)
 {
-  int sound_effect = element_info[Feld[x][y]].sound[action];
+  int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
 
   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
     StopSound(sound_effect);
@@ -10947,6 +12497,168 @@ static void PlayLevelMusic()
     PlayMusic(MAP_NOCONF_MUSIC(level_nr));     /* from music dir */
 }
 
+void PlayLevelSound_EM(int x, int y, int element_em, int sample)
+{
+  int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
+
+#if 0
+  if (sample == SAMPLE_bug)
+    printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
+#endif
+
+  switch (sample)
+  {
+    case SAMPLE_blank:
+      PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
+      break;
+
+    case SAMPLE_roll:
+      PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
+      break;
+
+    case SAMPLE_stone:
+      PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+      break;
+
+    case SAMPLE_nut:
+      PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+      break;
+
+    case SAMPLE_crack:
+      PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
+      break;
+
+    case SAMPLE_bug:
+      PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+      break;
+
+    case SAMPLE_tank:
+      PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+      break;
+
+    case SAMPLE_android_clone:
+      PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+      break;
+
+    case SAMPLE_android_move:
+      PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+      break;
+
+    case SAMPLE_spring:
+      PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+      break;
+
+    case SAMPLE_slurp:
+      PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
+      break;
+
+    case SAMPLE_eater:
+      PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
+      break;
+
+    case SAMPLE_eater_eat:
+      PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
+      break;
+
+    case SAMPLE_alien:
+      PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+      break;
+
+    case SAMPLE_collect:
+      PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
+      break;
+
+    case SAMPLE_diamond:
+      PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+      break;
+
+    case SAMPLE_squash:
+      /* !!! CHECK THIS !!! */
+#if 1
+      PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
+#else
+      PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
+#endif
+      break;
+
+    case SAMPLE_wonderfall:
+      PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
+      break;
+
+    case SAMPLE_drip:
+      PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+      break;
+
+    case SAMPLE_push:
+      PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
+      break;
+
+    case SAMPLE_dirt:
+      PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
+      break;
+
+    case SAMPLE_acid:
+      PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
+      break;
+
+    case SAMPLE_ball:
+      PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+      break;
+
+    case SAMPLE_grow:
+      PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
+      break;
+
+    case SAMPLE_wonder:
+      PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+      break;
+
+    case SAMPLE_door:
+      PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
+      break;
+
+    case SAMPLE_exit_open:
+      PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
+      break;
+
+    case SAMPLE_exit_leave:
+      PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
+      break;
+
+    case SAMPLE_dynamite:
+      PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+      break;
+
+    case SAMPLE_tick:
+      PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+      break;
+
+    case SAMPLE_press:
+      PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
+      break;
+
+    case SAMPLE_wheel:
+      PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+      break;
+
+    case SAMPLE_boom:
+      PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
+      break;
+
+    case SAMPLE_die:
+      PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
+      break;
+
+    case SAMPLE_time:
+      PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+      break;
+
+    default:
+      PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
+      break;
+  }
+}
+
 void RaiseScore(int value)
 {
   local_player->score += value;
@@ -11016,6 +12728,14 @@ void RaiseScoreElement(int element)
     case EL_KEY_2:
     case EL_KEY_3:
     case EL_KEY_4:
+    case EL_EM_KEY_1:
+    case EL_EM_KEY_2:
+    case EL_EM_KEY_3:
+    case EL_EM_KEY_4:
+    case EL_EMC_KEY_5:
+    case EL_EMC_KEY_6:
+    case EL_EMC_KEY_7:
+    case EL_EMC_KEY_8:
       RaiseScore(level.score[SC_KEY]);
       break;
     default:
@@ -11032,7 +12752,7 @@ void RequestQuitGame(boolean ask_if_really_quit)
       Request("Do you really want to quit the game ?",
              REQ_ASK | REQ_STAY_CLOSED))
   {
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
     if (options.network)
       SendToServer_StopPlaying();
     else
@@ -11046,21 +12766,15 @@ void RequestQuitGame(boolean ask_if_really_quit)
   {
 
 #if 1
-    if (tape.playing && tape.index_search)
-    {
-      SetDrawDeactivationMask(REDRAW_NONE);
-      audio.sound_deactivated = FALSE;
-    }
+    if (tape.playing && tape.deactivate_display)
+      TapeDeactivateDisplayOff(TRUE);
 #endif
 
     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
 
 #if 1
-    if (tape.playing && tape.index_search)
-    {
-      SetDrawDeactivationMask(REDRAW_FIELD);
-      audio.sound_deactivated = TRUE;
-    }
+    if (tape.playing && tape.deactivate_display)
+      TapeDeactivateDisplayOn();
 #endif
 
   }
@@ -11229,7 +12943,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
     case GAME_CTRL_ID_PAUSE:
       if (options.network)
       {
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
        if (tape.pausing)
          SendToServer_ContinuePlaying();
        else
@@ -11243,7 +12957,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
     case GAME_CTRL_ID_PLAY:
       if (tape.pausing)
       {
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
        if (options.network)
          SendToServer_ContinuePlaying();
        else