rnd-20040612-2-src
[rocksndiamonds.git] / src / game.c
index 59ced41619717c82f6fc88b6e1a34d074651ccb7..a8389022119adf4edf8b101a8d74d59844df3d6b 100644 (file)
 /* EXPERIMENTAL STUFF */
 #define USE_NEW_AMOEBA_CODE    FALSE
 
+/* EXPERIMENTAL STUFF */
+#define USE_NEW_MOVE_STYLE     TRUE    *0
+#define USE_NEW_MOVE_DELAY     TRUE    *1
+#define USE_NEW_PUSH_DELAY     TRUE    *1
+#define USE_NEW_BLOCK_STYLE    TRUE    *1
+
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
        ((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))
+       ((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) ||                \
 
 /* 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);
@@ -381,6 +392,18 @@ struct ChangingElementInfo
 
 static struct ChangingElementInfo change_delay_list[] =
 {
+#if USE_NEW_BLOCK_STYLE
+#if 0
+  {
+    EL_PLAYER_IS_LEAVING,
+    EL_EMPTY,
+    -1,                /* delay for blocking field left by player set at runtime */
+    NULL,
+    NULL,
+    NULL
+  },
+#endif
+#endif
   {
     EL_NUT_BREAKING,
     EL_EMERALD,
@@ -609,31 +632,39 @@ struct
 }
 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_UNDEFINED,              MV_NO_MOVING                         }
+  { 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];
@@ -743,6 +774,10 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
   {
     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
     int jx = player->jx, jy = player->jy;
+    int sp_block_delay = 7;
+    int em_block_delay = 7;
+    int sp_no_block_delay = 1;
+    int em_no_block_delay = 1;
 
     player->present = TRUE;
 
@@ -750,6 +785,11 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
                                level.sp_block_last_field :
                                level.block_last_field);
 
+    player->block_delay_value =
+      (element == EL_SP_MURPHY ?
+       (player->block_last_field ? sp_block_delay : sp_no_block_delay) :
+       (player->block_last_field ? em_block_delay : em_no_block_delay));
+
     if (!options.network || player->connected)
     {
       player->active = TRUE;
@@ -770,6 +810,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;
   }
@@ -1138,7 +1179,7 @@ static void resolve_group_element(int group_element, int recursion_depth)
 
 /*
   =============================================================================
 InitGameEngine()
+ InitGameEngine()
   -----------------------------------------------------------------------------
   initialize game engine due to level / tape version number
   =============================================================================
@@ -1174,6 +1215,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 :
@@ -1182,6 +1232,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 --------------------- */
 
@@ -1356,8 +1407,13 @@ static void InitGameEngine()
     {
       if (IS_SP_ELEMENT(i))
       {
+#if USE_NEW_MOVE_STYLE
+       element_info[i].push_delay_fixed  = 7;  /* just enough to escape ... */
+       element_info[i].push_delay_random = 0;  /* ... from falling zonk     */
+#else
        element_info[i].push_delay_fixed  = 6;  /* just enough to escape ... */
        element_info[i].push_delay_random = 0;  /* ... from falling zonk     */
+#endif
       }
     }
   }
@@ -1482,7 +1538,10 @@ void InitGame()
 
     player->use_murphy_graphic = FALSE;
 
-    player->block_last_field = FALSE;
+    player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
+    player->block_delay = 0;
+    player->block_delay_value = -1;    /* initialized in InitPlayerField() */
+
     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
 
     player->actual_frame_counter = 0;
@@ -1558,8 +1617,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;
 
@@ -1581,7 +1645,7 @@ 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);
@@ -2108,7 +2172,7 @@ void InitMovDir(int x, int y)
        {
 #if 1
          /* use random direction as default start direction */
-         if (game.engine_version >= VERSION_IDENT(3,1,0,2))
+         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
            MovDir[x][y] = 1 << RND(4);
 #endif
 
@@ -2651,57 +2715,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.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)))
     {
@@ -2751,8 +2777,6 @@ void RelocatePlayer(int x, int y, int element_raw)
 #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;
 
@@ -2848,7 +2872,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)
@@ -2963,10 +3134,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) &&
@@ -2976,13 +3154,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;
@@ -2992,9 +3187,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 */
@@ -3079,10 +3286,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_TYPE_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 ||
@@ -3129,6 +3341,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;
   }
 
@@ -3174,8 +3391,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);
@@ -3187,7 +3414,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
@@ -3202,6 +3434,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);
@@ -3429,9 +3666,15 @@ void DynaExplode(int ex, int ey)
       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_EXPLOSION &&
@@ -3513,13 +3756,21 @@ void Bang(int x, int y)
        Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
       break;
     default:
+#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))
+#endif
        Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
       else
        Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
@@ -5097,6 +5348,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)
@@ -5211,11 +5469,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
@@ -5233,7 +5506,7 @@ void StartMoving(int x, int y)
 
 #if 1
     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
-       CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
+       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) &&
@@ -5254,6 +5527,8 @@ void StartMoving(int x, int y)
       WasJustMoving[x][y] = 0;
 #endif
 
+      CheckCollision[x][y] = 0;
+
       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
 
 #if 0
@@ -5808,6 +6083,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);
@@ -5891,7 +6187,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;
 
@@ -5950,10 +6252,12 @@ 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 &&
@@ -5999,18 +6303,27 @@ void ContinueMoving(int x, int y)
   ResetGfxAnimation(x, y);     /* reset animation values for old field */
 
 #if 1
-  if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
-      ei->move_leave_element != EL_EMPTY &&
-      (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
-       stored != EL_EMPTY))
+  /* 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
   {
-    /* some elements can leave other elements behind after moving */
+    int move_leave_element = ei->move_leave_element;
 
-    Feld[x][y] = ei->move_leave_element;
+    Feld[x][y] = move_leave_element;
     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
 
@@ -6080,8 +6393,17 @@ 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)
   {
@@ -6108,13 +6430,31 @@ 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)) &&
+      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))
+    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 (pushed_by_player)
   {
+#if 1
+    int dig_side = MV_DIR_OPPOSITE(direction);
+#else
     static int trigger_sides[4] =
     {
       CH_SIDE_RIGHT,   /* moving left  */
@@ -6123,6 +6463,7 @@ void ContinueMoving(int x, int y)
       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,
@@ -7163,52 +7504,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];
-
-  /* check if element under player changes from accessible to unaccessible
+#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)
@@ -7232,7 +7616,10 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
 
   Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
 
+#if 0
+  /* !!! indirect change before direct change !!! */
   CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
+#endif
 
   if (change->explode)
   {
@@ -7250,6 +7637,7 @@ 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;
@@ -7279,6 +7667,16 @@ 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))
@@ -7286,24 +7684,37 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
 
 #if 1
 
-#if 1
-      is_empty = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
-                                     IS_WALKABLE(content_element)));
+#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_PLAYER(ex, ey) &&
-                                     IS_WALKABLE(content_element)));
+      is_empty = (IS_FREE(ex, ey) ||
+                 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
+#endif
+
 #endif
-      is_diggable = (is_empty || IS_DIGGABLE(e));
-      is_collectible = (is_empty || IS_COLLECTIBLE(e));
-      is_removable = (is_diggable || is_collectible);
+
+      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_COLLECTIBLE  && is_collectible) ||
-        (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
-        (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;
@@ -7374,6 +7785,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;
 }
 
@@ -7961,6 +8377,45 @@ 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;
@@ -7994,7 +8449,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
@@ -8045,7 +8500,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
@@ -8202,6 +8657,14 @@ void GameActions()
     Changed[x][y] = CE_BITMASK_DEFAULT;
     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
 
+    /* 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);
+    }
+
 #if DEBUG
     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
     {
@@ -8581,22 +9044,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];
+       player->shield_normal_time_left--;
 
-       if (SHIELD_ON(player))
-       {
-         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)
       {
@@ -8652,7 +9115,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++;
 
@@ -8666,6 +9131,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--;
   }
@@ -8825,9 +9295,26 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
 
 #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]) ||
@@ -8838,6 +9325,8 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
            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)
@@ -9126,14 +9615,38 @@ 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))
     return FALSE;
 #endif
 
+#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 */
@@ -9161,7 +9674,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();
     }
@@ -9265,6 +9784,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)
@@ -9292,6 +9815,11 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     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 */
@@ -9300,26 +9828,28 @@ 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
       /* !!! TEST ONLY !!! */
-      if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
-       CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+      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, Feld[old_jx][old_jy],
+      CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
                                          CE_OTHER_GETS_LEFT,
                                          player->index_bit, leave_side);
 
-      if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
-       CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+      if (IS_CUSTOM_ELEMENT(new_element))
+       CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
                                   player->index_bit, enter_side);
 
-      CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
+      CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
                                          CE_OTHER_GETS_ENTERED,
                                          player->index_bit, enter_side);
 #endif
@@ -9337,8 +9867,22 @@ 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!!!) */
+
+    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);
@@ -9365,8 +9909,27 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     player->actual_frame_counter = FrameCounter;
     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
+#if USE_NEW_BLOCK_STYLE
+    if (player->block_delay_value > 0 &&
+       Feld[last_jx][last_jy] == EL_EMPTY)
+    {
+      Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+#if 1
+      MovDelay[last_jx][last_jy] = player->block_delay_value + 1;
+#else
+      ChangeDelay[last_jx][last_jy] = player->block_last_field_delay;
+#endif
+    }
+#else
+#if USE_NEW_MOVE_STYLE
+    if (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);
@@ -9380,9 +9943,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)
@@ -9419,9 +9989,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;
@@ -9445,6 +10022,11 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     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 */
@@ -9453,28 +10035,30 @@ 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
       /* !!! TEST ONLY !!! */
-      if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
-       CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+      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, Feld[old_jx][old_jy],
+      CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
                                          CE_OTHER_GETS_LEFT,
                                          player->index_bit, leave_side);
 
-      if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
-       CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+      if (IS_CUSTOM_ELEMENT(new_element))
+       CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
                                   player->index_bit, enter_side);
 
-      CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
+      CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
                                          CE_OTHER_GETS_ENTERED,
                                          player->index_bit, enter_side);
 #endif
@@ -9487,7 +10071,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)
@@ -9500,19 +10089,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--;
@@ -9687,7 +10263,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++)
@@ -10346,16 +10922,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;
@@ -10364,25 +10935,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))
@@ -10441,10 +11030,27 @@ int DigField(struct PlayerInfo *player,
 
   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
@@ -10570,6 +11176,7 @@ int DigField(struct PlayerInfo *player,
       if (IS_WALKABLE(element))
 #endif
       {
+       int sound_element = SND_ELEMENT(element);
        int sound_action = ACTION_WALKING;
 
 #if 0
@@ -10577,7 +11184,7 @@ int DigField(struct PlayerInfo *player,
          return MF_NO_ACTION;  /* field not accessible from this direction */
 #endif
 
-#if 1
+#if 0
        if (element == EL_EMPTY_SPACE &&
            game.gravity && !player->is_auto_moving &&
            canFallDown(player) && move_direction != MV_DOWN)
@@ -10606,8 +11213,8 @@ 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);
 
@@ -10669,6 +11276,16 @@ int DigField(struct PlayerInfo *player,
              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 */
@@ -10721,7 +11338,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;
@@ -10807,9 +11424,10 @@ int DigField(struct PlayerInfo *player,
        RaiseScoreElement(element);
        PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
-       CheckTriggeredElementChangeByPlayer(x, y, element,
-                                           CE_OTHER_GETS_COLLECTED,
-                                           player->index_bit, dig_side);
+       if (is_player)
+         CheckTriggeredElementChangeByPlayer(x, y, element,
+                                             CE_OTHER_GETS_COLLECTED,
+                                             player->index_bit, dig_side);
 
 #if 1
        if (mode == DF_SNAP)
@@ -10856,11 +11474,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)
@@ -10880,9 +11512,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;
@@ -10896,16 +11531,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;
        }
@@ -10988,7 +11663,13 @@ int DigField(struct PlayerInfo *player,
       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;
@@ -11075,6 +11756,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
@@ -11118,7 +11806,11 @@ int DigField(struct PlayerInfo *player,
       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;
@@ -11136,10 +11828,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
 
@@ -11196,14 +11888,28 @@ 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 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  */
@@ -11211,10 +11917,8 @@ boolean DropElement(struct PlayerInfo *player)
     CH_SIDE_TOP,       /* dropping up    */
     CH_SIDE_BOTTOM,    /* dropping down  */
   };
-  int old_element, new_element;
-  int dropx = player->jx, dropy = player->jy;
-  int drop_direction = player->MovDir;
   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 ?
@@ -11370,11 +12074,11 @@ boolean DropElement(struct PlayerInfo *player)
     nexty = dropy + GET_DY_FROM_DIR(move_direction);
 
 #if 1
-      Changed[dropx][dropy] = 0;       /* allow another change */
-      CheckCollision[dropx][dropy] = 2;
+    Changed[dropx][dropy] = 0;         /* allow another change */
+    CheckCollision[dropx][dropy] = 2;
 #else
 
-    if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
+    if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
     {
 #if 0
       WasJustMoving[dropx][dropy] = 3;
@@ -11385,7 +12089,8 @@ boolean DropElement(struct PlayerInfo *player)
 #endif
 #endif
     }
-#if 1
+#if 0
+    /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
     else
     {
       Changed[dropx][dropy] = 0;       /* allow another change */
@@ -11501,7 +12206,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);
@@ -11510,7 +12215,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);
@@ -11518,7 +12223,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);
@@ -11526,7 +12231,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);
@@ -11625,7 +12330,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
@@ -11816,7 +12521,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
@@ -11830,7 +12535,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