rnd-20040326-1-src
[rocksndiamonds.git] / src / game.c
index 0c4100d2f6da2afb37174b7c373eaf1341b2590e..b1254556955778c3d660d44bc45a19d023a1c171 100644 (file)
 
 #define GET_NEW_PUSH_DELAY(e)  (   (element_info[e].push_delay_fixed) + \
                                 RND(element_info[e].push_delay_random))
+#define GET_NEW_DROP_DELAY(e)  (   (element_info[e].drop_delay_fixed) + \
+                                RND(element_info[e].drop_delay_random))
 #define GET_NEW_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
                                 RND(element_info[e].move_delay_random))
 #define GET_MAX_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
        ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
         (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
 
+#define CAN_GROW_INTO(e)                                               \
+       (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
+
 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                        \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
                                        (condition)))
 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
 
+#define ACCESS_FROM(e, d)              (element_info[e].access_direction &(d))
+#define IS_WALKABLE_FROM(e, d)         (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
+#define IS_PASSABLE_FROM(e, d)         (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
+#define IS_ACCESSIBLE_FROM(e, d)       (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
+
 /* game button identifiers */
 #define GAME_CTRL_ID_STOP              0
 #define GAME_CTRL_ID_PAUSE             1
@@ -594,7 +604,7 @@ struct
   int element;
   int direction;
 }
-tube_access[] =
+access_direction_list[] =
 {
   { EL_TUBE_ANY,               MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
   { EL_TUBE_VERTICAL,                               MV_UP | MV_DOWN },
@@ -608,7 +618,19 @@ tube_access[] =
   { EL_TUBE_RIGHT_UP,                    MV_RIGHT | MV_UP           },
   { EL_TUBE_RIGHT_DOWN,                          MV_RIGHT |         MV_DOWN },
 
-  { EL_UNDEFINED,              0                                    }
+  { 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                         }
 };
 
 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
@@ -977,9 +999,10 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
   /* this case is in fact a combination of not less than three bugs:
      first, it calls InitMovDir() for elements that can move, although this is
      already done by InitField(); then, it checks the element that was at this
-     field _before_ the call to InitField() (which can change it)
-
- */
+     field _before_ the call to InitField() (which can change it); lastly, it
+     was not called for "mole with direction" elements, which were treated as
+     "cannot move" due to (fixed) wrong element initialization in "src/init.c"
+  */
 }
 
 inline void DrawGameValue_Emeralds(int value)
@@ -1375,15 +1398,15 @@ static void InitGameEngine()
 
   /* ---------- initialize access direction -------------------------------- */
 
-  /* initialize access direction values to default */
+  /* initialize access direction values to default (access from every side) */
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     if (!IS_CUSTOM_ELEMENT(i))
       element_info[i].access_direction = MV_ALL_DIRECTIONS;
 
   /* set access direction value for certain elements from pre-defined list */
-  for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
-    element_info[tube_access[i].element].access_direction =
-      tube_access[i].direction;
+  for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
+    element_info[access_direction_list[i].element].access_direction =
+      access_direction_list[i].direction;
 }
 
 
@@ -1467,6 +1490,7 @@ void InitGame()
 
     player->is_waiting = FALSE;
     player->is_moving = FALSE;
+    player->is_auto_moving = FALSE;
     player->is_digging = FALSE;
     player->is_snapping = FALSE;
     player->is_collecting = FALSE;
@@ -2078,6 +2102,12 @@ void InitMovDir(int x, int y)
        else if (move_pattern == MV_ALONG_LEFT_SIDE ||
                 move_pattern == MV_ALONG_RIGHT_SIDE)
        {
+#if 1
+         /* use random direction as default start direction */
+         if (game.engine_version >= VERSION_IDENT(3,1,0,2))
+           MovDir[x][y] = 1 << RND(4);
+#endif
+
          for (i = 0; i < NUM_DIRECTIONS; i++)
          {
            int x1 = x + xy[i][0];
@@ -3390,12 +3420,16 @@ void DynaExplode(int ex, int ey)
 
       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
 
+#if 1
+      if (element != EL_EMPTY && element != EL_EXPLOSION &&
+         !CAN_GROW_INTO(element) && !dynabomb_xl)
+       break;
+#else
       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
-      if (element != EL_EMPTY &&
-         element != EL_SAND &&
-         element != EL_EXPLOSION &&
-         !dynabomb_xl)
+      if (element != EL_EMPTY && element != EL_EXPLOSION &&
+         element != EL_SAND && !dynabomb_xl)
        break;
+#endif
     }
   }
 }
@@ -5558,11 +5592,17 @@ void StartMoving(int x, int y)
        PlayLevelSoundAction(x, y, action);
       }
 
+#if 1
 #if 1
+      Store[newx][newy] = EL_EMPTY;
+      if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
+       Store[newx][newy] = element_info[element].move_leave_element;
+#else
       Store[newx][newy] = EL_EMPTY;
       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
          element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
        Store[newx][newy] = element_info[element].move_leave_element;
+#endif
 #else
       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
        element_info[element].can_leave_element = TRUE;
@@ -5769,6 +5809,7 @@ void StartMoving(int x, int y)
 void ContinueMoving(int x, int y)
 {
   int element = Feld[x][y];
+  int stored = Store[x][y];
   struct ElementInfo *ei = &element_info[element];
   int direction = MovDir[x][y];
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
@@ -5873,13 +5914,15 @@ void ContinueMoving(int x, int y)
   {
     element = Feld[newx][newy] = EL_ACID;
   }
-#if 1
+#if 0
   else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
-          ei->move_leave_element != EL_EMPTY && Store[x][y] != EL_EMPTY)
+          ei->move_leave_element != EL_EMPTY &&
+          (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
+           Store[x][y] != EL_EMPTY))
   {
     /* some elements can leave other elements behind after moving */
 
-    Feld[x][y] = Store[x][y];
+    Feld[x][y] = ei->move_leave_element;
     InitField(x, y, FALSE);
 
     if (GFX_CRUMBLED(Feld[x][y]))
@@ -5915,6 +5958,22 @@ 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 */
+
+    Feld[x][y] = ei->move_leave_element;
+    InitField(x, y, FALSE);
+
+    if (GFX_CRUMBLED(Feld[x][y]))
+      DrawLevelFieldCrumbledSandNeighbours(x, y);
+  }
+#endif
+
 #if 0
   /* some elements can leave other elements behind after moving */
   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
@@ -5950,9 +6009,18 @@ void ContinueMoving(int x, int y)
     MovDir[newx][newy] = 0;
   */
 
+#if 0
   if (!CAN_MOVE(element) ||
       (CAN_FALL(element) && direction == MV_DOWN))
     GfxDir[x][y] = MovDir[newx][newy] = 0;
+#else
+  if (!CAN_MOVE(element) ||
+      (CAN_FALL(element) && direction == MV_DOWN &&
+       (element == EL_SPRING ||
+       element_info[element].move_pattern == MV_WHEN_PUSHED ||
+       element_info[element].move_pattern == MV_WHEN_DROPPED)))
+    GfxDir[x][y] = MovDir[newx][newy] = 0;
+#endif
 
 #endif
 #endif
@@ -6389,6 +6457,15 @@ void AmoebeAbleger(int ax, int ay)
     if (!IN_LEV_FIELD(x, y))
       return;
 
+#if 1
+    if (IS_FREE(x, y) ||
+       CAN_GROW_INTO(Feld[x][y]) ||
+       Feld[x][y] == EL_QUICKSAND_EMPTY)
+    {
+      newax = x;
+      neway = y;
+    }
+#else
     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
     if (IS_FREE(x, y) ||
        Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
@@ -6396,6 +6473,7 @@ void AmoebeAbleger(int ax, int ay)
       newax = x;
       neway = y;
     }
+#endif
 
     if (newax == ax && neway == ay)
       return;
@@ -6414,6 +6492,16 @@ void AmoebeAbleger(int ax, int ay)
       if (!IN_LEV_FIELD(x, y))
        continue;
 
+#if 1
+      if (IS_FREE(x, y) ||
+         CAN_GROW_INTO(Feld[x][y]) ||
+         Feld[x][y] == EL_QUICKSAND_EMPTY)
+      {
+       newax = x;
+       neway = y;
+       break;
+      }
+#else
       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
       if (IS_FREE(x, y) ||
          Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
@@ -6422,6 +6510,7 @@ void AmoebeAbleger(int ax, int ay)
        neway = y;
        break;
       }
+#endif
       else if (IS_PLAYER(x, y))
        waiting_for_player = TRUE;
     }
@@ -6559,6 +6648,20 @@ void Life(int ax, int ay)
        changed = TRUE;
       }
     }
+#if 1
+    else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
+    {                                  /* free border field */
+      if (nachbarn >= life[2] && nachbarn <= life[3])
+      {
+       Feld[xx][yy] = element;
+       MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
+       if (!Stop[xx][yy])
+         DrawLevelField(xx, yy);
+       Stop[xx][yy] = TRUE;
+       changed = TRUE;
+      }
+    }
+#else
     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
     {                                  /* free border field */
@@ -6572,6 +6675,7 @@ void Life(int ax, int ay)
        changed = TRUE;
       }
     }
+#endif
   }
 
   if (changed)
@@ -8255,6 +8359,21 @@ void GameActions()
 #endif
       element = Feld[x][y];
 
+#if 1
+      if (!IS_PLAYER(x,y) &&
+         (element == EL_EMPTY ||
+          CAN_GROW_INTO(element) ||
+          element == EL_QUICKSAND_EMPTY ||
+          element == EL_ACID_SPLASH_LEFT ||
+          element == EL_ACID_SPLASH_RIGHT))
+      {
+       if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
+           (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
+         Feld[x][y] = EL_AMOEBA_DROP;
+      }
+#else
       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
       if (!IS_PLAYER(x,y) &&
          (element == EL_EMPTY ||
@@ -8269,6 +8388,7 @@ void GameActions()
            (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
          Feld[x][y] = EL_AMOEBA_DROP;
       }
+#endif
 
       random = random * 129 + 1;
     }
@@ -8541,6 +8661,7 @@ void ScrollLevel(int dx, int dy)
   redraw_mask |= REDRAW_FIELD;
 }
 
+#if 0
 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
 {
   int nextx = x + dx, nexty = y + dy;
@@ -8572,6 +8693,63 @@ static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
 
   return TRUE;
 }
+#endif
+
+static boolean canFallDown(struct PlayerInfo *player)
+{
+  int jx = player->jx, jy = player->jy;
+
+  return (IN_LEV_FIELD(jx, jy + 1) &&
+         (IS_FREE(jx, jy + 1) ||
+          (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
+         IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
+         !IS_WALKABLE_INSIDE(Feld[jx][jy]));
+}
+
+static boolean canPassField(int x, int y, int move_dir)
+{
+  int opposite_dir = MV_DIR_OPPOSITE(move_dir);
+  int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
+  int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
+  int nextx = x + dx;
+  int nexty = y + dy;
+  int element = Feld[x][y];
+
+  return (IS_PASSABLE_FROM(element, opposite_dir) &&
+         !CAN_MOVE(element) &&
+         IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
+         IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
+         (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
+}
+
+static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
+{
+  int opposite_dir = MV_DIR_OPPOSITE(move_dir);
+  int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
+  int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
+  int newx = x + dx;
+  int newy = y + dy;
+#if 0
+  int nextx = newx + dx;
+  int nexty = newy + dy;
+#endif
+
+#if 1
+  return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+         (IS_DIGGABLE(Feld[newx][newy]) ||
+          IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+          canPassField(newx, newy, move_dir)));
+#else
+  return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+         (IS_DIGGABLE(Feld[newx][newy]) ||
+          IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+          (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
+           !CAN_MOVE(Feld[newx][newy]) &&
+           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
+           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
+           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
+#endif
+}
 
 static void CheckGravityMovement(struct PlayerInfo *player)
 {
@@ -8584,45 +8762,82 @@ static void CheckGravityMovement(struct PlayerInfo *player)
     int move_dir_horizontal = player->action & MV_HORIZONTAL;
     int move_dir_vertical   = player->action & MV_VERTICAL;
 #endif
+
+#if 1
+    boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
+#else
+    boolean player_is_snapping = player->action & JOY_BUTTON_1;
+#endif
+
+    int jx = player->jx, jy = player->jy;
+
+    boolean player_is_moving_to_valid_field =
+      (!player_is_snapping &&
+       (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
+       canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
+
+#if 0
     int move_dir =
       (player->last_move_dir & MV_HORIZONTAL ?
        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
-    int jx = player->jx, jy = player->jy;
+#endif
+
+#if 0
+    int opposite_dir = MV_DIR_OPPOSITE(move_dir);
     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
     int new_jx = jx + dx, new_jy = jy + dy;
-#if 1
-    boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
-#else
-    boolean player_is_snapping = player->action & JOY_BUTTON_1;
+    int nextx = new_jx + dx, nexty = new_jy + dy;
 #endif
+
+#if 1
+
 #if 1
+    boolean player_can_fall_down = canFallDown(player);
+#else
     boolean player_can_fall_down =
       (IN_LEV_FIELD(jx, jy + 1) &&
        (IS_FREE(jx, jy + 1) ||
        (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
+#endif
+
 #else
     boolean player_can_fall_down =
       (IN_LEV_FIELD(jx, jy + 1) &&
        (IS_FREE(jx, jy + 1)));
 #endif
+
+#if 0
     boolean player_is_moving_to_valid_field =
       (
 #if 1
        !player_is_snapping &&
 #endif
+
+#if 1
+       IN_LEV_FIELD(new_jx, new_jy) &&
+       (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
+       (IS_SP_PORT(Feld[new_jx][new_jy]) &&
+        element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
+        IN_LEV_FIELD(nextx, nexty) &&
+        element_info[Feld[nextx][nexty]].access_direction & move_dir))
+#else
        IN_LEV_FIELD(new_jx, new_jy) &&
        (Feld[new_jx][new_jy] == EL_SP_BASE ||
        Feld[new_jx][new_jy] == EL_SAND ||
        (IS_SP_PORT(Feld[new_jx][new_jy]) &&
-        canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
+        canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
     /* !!! extend EL_SAND to anything diggable !!! */
+#endif
+       );
+#endif
 
+#if 0
     boolean player_is_standing_on_valid_field =
       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
-       (IS_WALKABLE(Feld[jx][jy]) &&
-       !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
+       (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
+#endif
 
 #if 0
     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
@@ -8635,7 +8850,9 @@ static void CheckGravityMovement(struct PlayerInfo *player)
 #endif
 
     if (player_can_fall_down &&
+#if 0
        !player_is_standing_on_valid_field &&
+#endif
        !player_is_moving_to_valid_field)
     {
 #if 0
@@ -8757,7 +8974,9 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 
   player->step_counter++;
 
+#if 0
   player->drop_delay = 0;
+#endif
 
   PlayerVisit[jx][jy] = FrameCounter;
 
@@ -8828,6 +9047,9 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
 #endif
 
+  /* store if player is automatically moved to next field */
+  player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
+
   /* remove the last programmed player action */
   player->programmed_action = 0;
 
@@ -10075,8 +10297,10 @@ int DigField(struct PlayerInfo *player,
 
 #endif
 
-  if (IS_WALKABLE(old_element) &&
-      !(element_info[old_element].access_direction & move_direction))
+  if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
+    return MF_NO_ACTION;       /* field has no opening in this direction */
+
+  if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
     return MF_NO_ACTION;       /* field has no opening in this direction */
 
   element = Feld[x][y];
@@ -10087,6 +10311,7 @@ int DigField(struct PlayerInfo *player,
 
   switch (element)
   {
+#if 0
     case EL_SP_PORT_LEFT:
     case EL_SP_PORT_RIGHT:
     case EL_SP_PORT_UP:
@@ -10154,6 +10379,7 @@ int DigField(struct PlayerInfo *player,
 
       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
       break;
+#endif
 
 #if 0
     case EL_TUBE_ANY:
@@ -10206,15 +10432,22 @@ int DigField(struct PlayerInfo *player,
       {
        int sound_action = ACTION_WALKING;
 
-       if (!(element_info[element].access_direction & opposite_direction))
+       if (!ACCESS_FROM(element, opposite_direction))
          return MF_NO_ACTION;  /* field not accessible from this direction */
 
-       if (element >= EL_GATE_1 && element <= EL_GATE_4)
+#if 1
+       if (element == EL_EMPTY_SPACE &&
+           game.gravity && !player->is_auto_moving &&
+           canFallDown(player) && move_direction != MV_DOWN)
+         return MF_NO_ACTION;  /* player cannot walk here due to gravity */
+#endif
+
+       if (IS_GATE(element))
        {
          if (!player->key[element - EL_GATE_1])
            return MF_NO_ACTION;
        }
-       else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
+       else if (IS_GATE_GRAY(element))
        {
          if (!player->key[element - EL_GATE_1_GRAY])
            return MF_NO_ACTION;
@@ -10240,28 +10473,55 @@ int DigField(struct PlayerInfo *player,
       }
       else if (IS_PASSABLE(element))
       {
+#if 1
+       if (!canPassField(x, y, move_direction))
+         return MF_NO_ACTION;
+#else
+
+#if 1
+       if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
+           !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
+           (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
+         return MF_NO_ACTION;
+#else
        if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
          return MF_NO_ACTION;
+#endif
 
+#if 1
+       if (!ACCESS_FROM(element, opposite_direction))
+         return MF_NO_ACTION;  /* field not accessible from this direction */
+#else
        if (IS_CUSTOM_ELEMENT(element) &&
-           !(element_info[element].access_direction & opposite_direction))
+           !ACCESS_FROM(element, opposite_direction))
          return MF_NO_ACTION;  /* field not accessible from this direction */
+#endif
 
 #if 1
        if (CAN_MOVE(element))  /* only fixed elements can be passed! */
          return MF_NO_ACTION;
 #endif
 
-       if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
+#endif
+
+       if (IS_EM_GATE(element))
        {
          if (!player->key[element - EL_EM_GATE_1])
            return MF_NO_ACTION;
        }
-       else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
+       else if (IS_EM_GATE_GRAY(element))
        {
          if (!player->key[element - EL_EM_GATE_1_GRAY])
            return MF_NO_ACTION;
        }
+       else if (IS_SP_PORT(element))
+       {
+         if (element == EL_SP_GRAVITY_PORT_LEFT ||
+             element == EL_SP_GRAVITY_PORT_RIGHT ||
+             element == EL_SP_GRAVITY_PORT_UP ||
+             element == EL_SP_GRAVITY_PORT_DOWN)
+           game.gravity = !game.gravity;
+       }
 
        /* automatically move to the next field with double speed */
        player->programmed_action = move_direction;
@@ -10776,13 +11036,14 @@ boolean DropElement(struct PlayerInfo *player)
   int drop_direction = player->MovDir;
   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
   int old_element = Feld[jx][jy];
-  int new_element = (player->inventory_size > 0 ?
-                    player->inventory_element[player->inventory_size - 1] :
-                    player->inventory_infinite_element != EL_UNDEFINED ?
-                    player->inventory_infinite_element :
-                    player->dynabombs_left > 0 ?
-                    EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
-                    EL_UNDEFINED);
+  int drop_element = (player->inventory_size > 0 ?
+                     player->inventory_element[player->inventory_size - 1] :
+                     player->inventory_infinite_element != EL_UNDEFINED ?
+                     player->inventory_infinite_element :
+                     player->dynabombs_left > 0 ?
+                     EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
+                     EL_UNDEFINED);
+  int new_element = drop_element;      /* default: element does not change */
 
   /* check if player is active, not moving and ready to drop */
   if (!player->active || player->MovPos || player->drop_delay > 0)
@@ -10851,11 +11112,20 @@ boolean DropElement(struct PlayerInfo *player)
     Changed[jx][jy] = 0;               /* allow another change */
 #endif
 
+#if 1
+    /* !!! TEST ONLY !!! */
+    CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
+                            player->index_bit, drop_side);
+    CheckTriggeredElementChangePlayer(jx, jy, new_element,
+                                     CE_OTHER_GETS_DROPPED,
+                                     player->index_bit, drop_side);
+#else
     CheckTriggeredElementChangePlayer(jx, jy, new_element,
                                      CE_OTHER_GETS_DROPPED,
                                      player->index_bit, drop_side);
     CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
                             player->index_bit, drop_side);
+#endif
 
     TestIfElementTouchesCustomElement(jx, jy);
   }
@@ -10890,12 +11160,14 @@ boolean DropElement(struct PlayerInfo *player)
 #endif
   }
 
-  new_element = Feld[jx][jy];
+  new_element = Feld[jx][jy];          /* element might have changed */
 
   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
   {
+#if 0
     int move_stepsize = element_info[new_element].move_stepsize;
+#endif
     int direction, dx, dy, nextx, nexty;
 
     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
@@ -10928,13 +11200,19 @@ boolean DropElement(struct PlayerInfo *player)
 #endif
     }
 
+#if 0
     player->drop_delay = 2 * TILEX / move_stepsize + 1;
+#endif
   }
 
 #if 0
   player->drop_delay = 8 + 8 + 8;
 #endif
 
+#if 1
+  player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
+#endif
+
 #endif
 
   player->is_dropping = TRUE;