rocksndiamonds-3.1.0
[rocksndiamonds.git] / src / game.c
index 994c04f3f124846da6029299f01dd44c1bfba131..762035dc5656bc5479fc3158d263620b0675cb1c 100644 (file)
        ((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) ||                \
@@ -609,31 +612,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];
@@ -1582,7 +1593,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);
@@ -2109,7 +2120,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
 
@@ -2818,7 +2829,11 @@ void DrawRelocatePlayer(struct PlayerInfo *player)
 
 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);
@@ -2830,6 +2845,14 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   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 */
@@ -2838,13 +2861,12 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
     { CH_SIDE_BOTTOM,          CH_SIDE_TOP     },      /* moving up    */
     { CH_SIDE_TOP,             CH_SIDE_BOTTOM  }       /* moving down  */
   };
-  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);
   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 enter_side = enter_side_horiz | enter_side_vert;
   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 */
@@ -2909,10 +2931,28 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   TestIfPlayerTouchesCustomElement(jx, jy);
 #endif
 
-#if 1
+#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))
@@ -3037,10 +3077,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) &&
@@ -3050,13 +3097,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;
@@ -3068,9 +3132,15 @@ void Explode(int ex, int ey, int phase, int mode)
        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;
@@ -3159,10 +3229,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 ||
@@ -3209,6 +3284,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;
   }
 
@@ -3254,8 +3334,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);
@@ -3267,7 +3357,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
@@ -3282,6 +3377,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);
@@ -5344,7 +5444,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) &&
@@ -6275,6 +6375,9 @@ void ContinueMoving(int x, int y)
 #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  */
@@ -6283,6 +6386,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,
@@ -7337,6 +7441,11 @@ static void ChangeElementNowExt(int x, int y, int target_element)
   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;
   }
@@ -7377,7 +7486,9 @@ static void ChangeElementNowExt(int x, int y, int target_element)
       DrawLevelFieldCrumbledSandNeighbours(x, y);
   }
 
+#if 0
   Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
+#endif
 
 #if 0
   TestIfBadThingTouchesHero(x, y);
@@ -7385,9 +7496,14 @@ static void ChangeElementNowExt(int x, int y, int target_element)
   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);
@@ -7474,6 +7590,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))
@@ -7487,9 +7613,17 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
                  (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));
@@ -7497,12 +7631,13 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
       is_removable    = (is_diggable || is_collectible);
 
       can_replace[xx][yy] =
-       ((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));
+       (((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;
@@ -8198,7 +8333,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
@@ -8249,7 +8384,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
@@ -9027,6 +9162,23 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
   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]) ||
@@ -9042,6 +9194,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)
@@ -9496,6 +9650,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 */
@@ -9504,9 +9663,9 @@ 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];
 
@@ -9651,6 +9810,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 */
@@ -9659,9 +9823,9 @@ 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];
@@ -9695,7 +9859,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)
@@ -9882,7 +10051,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++)
@@ -10541,16 +10710,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;
@@ -10559,25 +10723,39 @@ 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;
+      player->push_delay = 0;
 
-    return MF_NO_ACTION;
+      return MF_NO_ACTION;
+    }
   }
 
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
@@ -10636,12 +10814,15 @@ 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 && !player->is_auto_moving &&
+  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 */
@@ -10779,6 +10960,7 @@ int DigField(struct PlayerInfo *player,
       if (IS_WALKABLE(element))
 #endif
       {
+       int sound_element = SND_ELEMENT(element);
        int sound_action = ACTION_WALKING;
 
 #if 0
@@ -10815,8 +10997,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);
 
@@ -10878,6 +11060,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 */
@@ -10930,7 +11122,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;
@@ -11016,9 +11208,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)
@@ -11065,11 +11258,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)
@@ -11089,9 +11296,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;
@@ -11197,7 +11407,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;
@@ -11284,6 +11500,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
@@ -11421,6 +11644,12 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
 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  */
@@ -11428,10 +11657,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 ?
@@ -11587,11 +11814,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;
@@ -11602,7 +11829,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 */
@@ -11718,7 +11946,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);
@@ -11727,7 +11955,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);
@@ -11735,7 +11963,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);
@@ -11743,7 +11971,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);
@@ -11842,7 +12070,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
@@ -12033,7 +12261,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
@@ -12047,7 +12275,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