rnd-20050615-1-src
[rocksndiamonds.git] / src / game.c
index bd41a32415039cc7952246e04ff2f1f2341f60b1..eb58f0c36b96bbd369d0b3d246244e54b1c05a69 100644 (file)
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 /* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF          TRUE                            * 1
+#define USE_NEW_STUFF          (TRUE                           * 1)
 
-#define USE_NEW_MOVE_STYLE     TRUE    * USE_NEW_STUFF         * 1
-#define USE_NEW_MOVE_DELAY     TRUE    * USE_NEW_STUFF         * 1
-#define USE_NEW_PUSH_DELAY     TRUE    * USE_NEW_STUFF         * 1
-#define USE_NEW_BLOCK_STYLE    TRUE    * USE_NEW_STUFF         * 1 * 1
-#define USE_NEW_SP_SLIPPERY    TRUE    * USE_NEW_STUFF         * 1
-#define USE_NEW_RANDOMIZE      TRUE    * USE_NEW_STUFF         * 1
+#define USE_NEW_MOVE_STYLE     (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_MOVE_DELAY     (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_PUSH_DELAY     (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_BLOCK_STYLE    (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_SP_SLIPPERY    (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_RANDOMIZE      (TRUE   * USE_NEW_STUFF         * 1)
+
+#define USE_CAN_MOVE_NOT_MOVING        (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_PREVIOUS_MOVE_DIR  (TRUE   * USE_NEW_STUFF         * 1)
+
+#define USE_PUSH_BUGFIX                (TRUE   * USE_NEW_STUFF         * 1)
+#if 0
+#define USE_BLOCK_DELAY_BUGFIX (TRUE   * USE_NEW_STUFF         * 1)
+#endif
+#define USE_GRAVITY_BUGFIX_NEW (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_GRAVITY_BUGFIX_OLD (TRUE   * USE_NEW_STUFF         * 0)
+
+#define USE_PENGUIN_COLLECT_BUG        (TRUE   * USE_NEW_STUFF         * 1)
 
-#define USE_PUSH_BUGFIX                TRUE    * USE_NEW_STUFF         * 1
 
 /* for DigField() */
 #define DF_NO_PUSH             0
@@ -776,6 +787,45 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
                                level.block_last_field);
 
 #if USE_NEW_BLOCK_STYLE
+#if 1
+
+    /* ---------- initialize player's last field block delay --------------- */
+
+    /* always start with reliable default value (no adjustment needed) */
+    player->block_delay_adjustment = 0;
+
+    /* special case 1: in Supaplex, Murphy blocks last field one more frame */
+    if (player->block_last_field && element == EL_SP_MURPHY)
+      player->block_delay_adjustment = 1;
+
+    /* special case 2: in game engines before 3.1.1, blocking was different */
+    if (game.use_block_last_field_bug)
+      player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
+
+#if 0
+    /* blocking the last field when moving was corrected in version 3.1.1 */
+    if (game.use_block_last_field_bug)
+    {
+      /* even "not blocking" was blocking the last field for one frame */
+      level.block_delay    = (level.block_last_field    ? 7 : 1);
+      level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
+
+      level.block_last_field = TRUE;
+      level.sp_block_last_field = TRUE;
+    }
+#endif
+
+#if 0  /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
+    level.block_delay = 8;             /* when blocking, block 8 frames */
+    level.sp_block_delay = 9;          /* SP indeed blocks 9 frames, not 8 */
+#endif
+
+#if 0
+    printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
+#endif
+
+#else
+
 #if 1
     player->block_delay = (player->block_last_field ?
                           (element == EL_SP_MURPHY ?
@@ -787,6 +837,8 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
                           (player->block_last_field ? 7 : 1));
 #endif
 
+#endif
+
 #if 0
     printf("::: block_last_field == %d, block_delay = %d\n",
           player->block_last_field, player->block_delay);
@@ -1228,7 +1280,13 @@ static void InitGameEngine()
   /* ---------------------------------------------------------------------- */
 
   /*
-    Type of bug/change:
+    Summary of bugfix/change:
+    Fixed handling for custom elements that change when pushed by the player.
+
+    Fixed/changed in version:
+    3.1.0
+
+    Description:
     Before 3.1.0, custom elements that "change when pushing" changed directly
     after the player started pushing them (until then handled in "DigField()").
     Since 3.1.0, these custom elements are not changed until the "pushing"
@@ -1249,12 +1307,40 @@ static void InitGameEngine()
     Machine" by Juergen Bonhagen.
   */
 
-  game.use_bug_change_when_pushing =
+  game.use_change_when_pushing_bug =
     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
      !(tape.playing &&
        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
        tape.game_version <  VERSION_IDENT(3,1,1,0)));
 
+  /*
+    Summary of bugfix/change:
+    Fixed handling for blocking the field the player leaves when moving.
+
+    Fixed/changed in version:
+    3.1.1
+
+    Description:
+    Before 3.1.1, when "block last field when moving" was enabled, the field
+    the player is leaving when moving was blocked for the time of the move,
+    and was directly unblocked afterwards. This resulted in the last field
+    being blocked for exactly one less than the number of frames of one player
+    move. Additionally, even when blocking was disabled, the last field was
+    blocked for exactly one frame.
+    Since 3.1.1, due to changes in player movement handling, the last field
+    is not blocked at all when blocking is disabled. When blocking is enabled,
+    the last field is blocked for exactly the number of frames of one player
+    move. Additionally, if the player is Murphy, the hero of Supaplex, the
+    last field is blocked for exactly one more than the number of frames of
+    one player move.
+
+    Affected levels/tapes:
+    (!!! yet to be determined -- probably many !!!)
+  */
+
+  game.use_block_last_field_bug =
+    (game.engine_version < VERSION_IDENT(3,1,1,0));
+
   /* ---------------------------------------------------------------------- */
 
   /* dynamically adjust element properties according to game engine version */
@@ -1606,7 +1692,7 @@ void InitGame()
     player->use_murphy_graphic = FALSE;
 
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
-    player->block_delay = -1;          /* initialized in InitPlayerField() */
+    player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
 
     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
 
@@ -1874,8 +1960,8 @@ void InitGame()
 #endif
 
 #if USE_NEW_BLOCK_STYLE
-         player->block_last_field = some_player->block_last_field;
-         player->block_delay = some_player->block_delay;
+         player->block_last_field       = some_player->block_last_field;
+         player->block_delay_adjustment = some_player->block_delay_adjustment;
 #endif
 
          StorePlayer[jx][jy] = player->element_nr;
@@ -2614,14 +2700,14 @@ void InitMovingField(int x, int y, int direction)
   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
     ResetGfxAnimation(x, y);
 
-#if 1
+#if USE_CAN_MOVE_NOT_MOVING
 
   MovDir[x][y] = direction;
   GfxDir[x][y] = direction;
   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
                     ACTION_FALLING : ACTION_MOVING);
 
-  if (getElementMoveStepsize(x, y) != 0)
+  if (getElementMoveStepsize(x, y) != 0)       /* moving or being moved */
   {
     if (Feld[newx][newy] == EL_EMPTY)
       Feld[newx][newy] = EL_BLOCKED;
@@ -5028,8 +5114,8 @@ inline static void TurnRoundExt(int x, int y)
     boolean can_turn_right =
       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
 
-#if 1
-    if (getElementMoveStepsize(x, y) == 0)
+#if USE_CAN_MOVE_NOT_MOVING
+    if (element_info[element].move_stepsize == 0)      /* not moving */
       return;
 #endif
 
@@ -5143,8 +5229,8 @@ inline static void TurnRoundExt(int x, int y)
       boolean first_horiz = RND(2);
       int new_move_dir = MovDir[x][y];
 
-#if 1
-      if (getElementMoveStepsize(x, y) == 0)
+#if USE_CAN_MOVE_NOT_MOVING
+      if (element_info[element].move_stepsize == 0)    /* not moving */
       {
        first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
        MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
@@ -6459,7 +6545,9 @@ void ContinueMoving(int x, int y)
 #endif
 
   Store[x][y] = EL_EMPTY;
-  MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+  MovPos[x][y] = 0;
+  MovDir[x][y] = 0;
+  MovDelay[x][y] = 0;
   MovDelay[newx][newy] = 0;
 
   if (CAN_CHANGE(element))
@@ -6484,7 +6572,10 @@ void ContinueMoving(int x, int y)
 
   Pushed[x][y] = Pushed[newx][newy] = FALSE;
 
+#if 0
+  /* do this after checking for left-behind element */
   ResetGfxAnimation(x, y);     /* reset animation values for old field */
+#endif
 
 #if 1
   /* some elements can leave other elements behind after moving */
@@ -6501,6 +6592,12 @@ void ContinueMoving(int x, int y)
     int move_leave_element = ei->move_leave_element;
 
     Feld[x][y] = move_leave_element;
+
+#if USE_PREVIOUS_MOVE_DIR
+    if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+      MovDir[x][y] = direction;
+#endif
+
     InitField(x, y, FALSE);
 
     if (GFX_CRUMBLED(Feld[x][y]))
@@ -6529,6 +6626,11 @@ void ContinueMoving(int x, int y)
   ei->can_leave_element = FALSE;
 #endif
 
+#if 1
+  /* do this after checking for left-behind element */
+  ResetGfxAnimation(x, y);     /* reset animation values for old field */
+#endif
+
 #if 0
   /* 2.1.1 (does not work correctly for spring) */
   if (!CAN_MOVE(element))
@@ -6638,7 +6740,7 @@ void ContinueMoving(int x, int y)
 
 #if USE_PUSH_BUGFIX
 #if 1
-  if (pushed_by_player && !game.use_bug_change_when_pushing)
+  if (pushed_by_player && !game.use_change_when_pushing_bug)
 #else
   if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
 #endif
@@ -9464,6 +9566,11 @@ static boolean canFallDown(struct PlayerInfo *player)
 
   return (IN_LEV_FIELD(jx, jy + 1) &&
          (IS_FREE(jx, jy + 1) ||
+#if USE_NEW_BLOCK_STYLE
+#if USE_GRAVITY_BUGFIX_OLD
+          Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
+#endif
+#endif
           (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
          IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
          !IS_WALKABLE_INSIDE(Feld[jx][jy]));
@@ -10128,11 +10235,48 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       printf("::: ALERT! block_delay == %d\n", player->block_delay);
 #endif
 
-    if (player->block_delay > 0 &&
+    if ((player->block_last_field || player->block_delay_adjustment > 0) &&
        Feld[last_jx][last_jy] == EL_EMPTY)
     {
+      int last_field_block_delay = 0;  /* start with no blocking at all */
+      int block_delay_adjustment = player->block_delay_adjustment;
+
+      /* if player blocks last field, add delay for exactly one move */
+      if (player->block_last_field)
+      {
+       last_field_block_delay += player->move_delay_value;
+
+#if USE_GRAVITY_BUGFIX_NEW
+       /* when blocking enabled, prevent moving up despite gravity */
+       if (game.gravity && player->MovDir == MV_UP)
+         block_delay_adjustment = -1;
+#endif
+      }
+
+      /* add block delay adjustment (also possible when not blocking) */
+      last_field_block_delay += block_delay_adjustment;
+
+#if 0
+#if USE_BLOCK_DELAY_BUGFIX
+      /* when blocking enabled, correct block delay for fast movement */
+      if (player->block_last_field &&
+         player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
+       last_field_block_delay =
+         player->move_delay_value + player->block_delay_adjustment;
+#endif
+#endif
+
+#if 0
+#if USE_GRAVITY_BUGFIX_NEW
+      /* when blocking enabled, correct block delay for gravity movement */
+      if (player->block_last_field &&
+         game.gravity && player->MovDir == MV_UP)
+       last_field_block_delay = player->move_delay_value - 1;
+#endif
+#endif
+
       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
-      MovDelay[last_jx][last_jy] = player->block_delay + 1;
+      MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
     }
 #else
 #if USE_NEW_MOVE_STYLE
@@ -11854,7 +11998,7 @@ int DigField(struct PlayerInfo *player,
 #if USE_PUSH_BUGFIX
        /* now: check for element change _after_ element has been pushed! */
 #if 1
-       if (game.use_bug_change_when_pushing)
+       if (game.use_change_when_pushing_bug)
 #else
        if (game.engine_version < VERSION_IDENT(3,1,0,0))
 #endif
@@ -11970,6 +12114,7 @@ int DigField(struct PlayerInfo *player,
          Feld[x][y] = EL_LAMP_ACTIVE;
          local_player->lights_still_needed--;
 
+         ResetGfxAnimation(x, y);
          DrawLevelField(x, y);
        }
        else if (element == EL_TIME_ORB_FULL)
@@ -11978,6 +12123,7 @@ int DigField(struct PlayerInfo *player,
          TimeLeft += 10;
          DrawGameValue_Time(TimeLeft);
 
+         ResetGfxAnimation(x, y);
          DrawLevelField(x, y);
 
 #if 0
@@ -12041,8 +12187,13 @@ int DigField(struct PlayerInfo *player,
   player->push_delay = 0;
 #endif
 
-  if (Feld[x][y] != element)           /* really digged/collected something */
-    player->is_collecting = !player->is_digging;
+#if USE_PENGUIN_COLLECT_BUG
+  if (is_player)               /* function can also be called by EL_PENGUIN */
+#endif
+  {
+    if (Feld[x][y] != element)         /* really digged/collected something */
+      player->is_collecting = !player->is_digging;
+  }
 
   return MF_MOVING;
 }