+static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
+{
+ int opposite_dir = MV_DIR_OPPOSITE(move_dir);
+ int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
+ int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
+ int newx = x + dx;
+ int newy = y + dy;
+#if 0
+ int nextx = newx + dx;
+ int nexty = newy + dy;
+#endif
+
+#if 1
+ return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+ IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
+#if 0
+ (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
+#endif
+ (IS_DIGGABLE(Feld[newx][newy]) ||
+ IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+ canPassField(newx, newy, move_dir)));
+#else
+#if 1
+ return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+ IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
+ (IS_DIGGABLE(Feld[newx][newy]) ||
+ IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+ canPassField(newx, newy, move_dir)));
+#else
+#if 1
+ return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+ (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
+ IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+ canPassField(newx, newy, move_dir)));
+#else
+ return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+ (IS_DIGGABLE(Feld[newx][newy]) ||
+ IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+ (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
+ !CAN_MOVE(Feld[newx][newy]) &&
+ IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
+ IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
+ (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
+#endif
+#endif
+#endif
+}
+
+static void CheckGravityMovement(struct PlayerInfo *player)
+{
+ if (game.gravity && !player->programmed_action)
+ {
+#if 1
+ int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
+ int move_dir_vertical = player->effective_action & MV_VERTICAL;
+#else
+ 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));
+#endif
+
+#if 0
+ int opposite_dir = MV_DIR_OPPOSITE(move_dir);
+ int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
+ int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
+ int new_jx = jx + dx, new_jy = jy + dy;
+ int nextx = new_jx + dx, nexty = new_jy + dy;
+#endif
+
+#if 1
+
+#if 1
+ boolean player_can_fall_down = canFallDown(player);
+#else
+ boolean player_can_fall_down =
+ (IN_LEV_FIELD(jx, jy + 1) &&
+ (IS_FREE(jx, jy + 1) ||
+ (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
+#endif
+
+#else
+ boolean player_can_fall_down =
+ (IN_LEV_FIELD(jx, jy + 1) &&
+ (IS_FREE(jx, jy + 1)));
+#endif
+
+#if 0
+ boolean player_is_moving_to_valid_field =
+ (
+#if 1
+ !player_is_snapping &&
+#endif
+
+#if 1
+ IN_LEV_FIELD(new_jx, new_jy) &&
+ (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
+ (IS_SP_PORT(Feld[new_jx][new_jy]) &&
+ element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
+ IN_LEV_FIELD(nextx, nexty) &&
+ element_info[Feld[nextx][nexty]].access_direction & move_dir))
+#else
+ IN_LEV_FIELD(new_jx, new_jy) &&
+ (Feld[new_jx][new_jy] == EL_SP_BASE ||
+ Feld[new_jx][new_jy] == EL_SAND ||
+ (IS_SP_PORT(Feld[new_jx][new_jy]) &&
+ canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
+ /* !!! 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]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
+#endif
+
+#if 0
+ printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
+ player_can_fall_down,
+ player_is_standing_on_valid_field,
+ player_is_moving_to_valid_field,
+ (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
+ player->effective_action,
+ player->can_fall_into_acid);
+#endif
+
+ if (player_can_fall_down &&
+#if 0
+ !player_is_standing_on_valid_field &&
+#endif
+ !player_is_moving_to_valid_field)
+ {
+#if 0
+ printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
+ jx, jy, FrameCounter);
+#endif
+
+ player->programmed_action = MV_DOWN;
+ }
+ }
+}
+
+static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
+{
+#if 1
+ return CheckGravityMovement(player);
+#endif
+
+ if (game.gravity && !player->programmed_action)
+ {
+ int jx = player->jx, jy = player->jy;
+ boolean field_under_player_is_free =
+ (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
+ 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)));
+
+ if (field_under_player_is_free && !player_is_standing_on_valid_field)
+ player->programmed_action = MV_DOWN;
+ }
+}
+
+/*