rnd-20060812-1-src
[rocksndiamonds.git] / src / game.c
index ed436b4ab836241c9c2149c7f9b6cd17b493924a..97b4be7f06e173f958b664b58a26b1206ea22746 100644 (file)
 #define USE_GFX_RESET_GFX_ANIMATION    (USE_NEW_STUFF          * 1)
 #define USE_BOTH_SWITCHGATE_SWITCHES   (USE_NEW_STUFF          * 1)
 #define USE_PLAYER_GRAVITY             (USE_NEW_STUFF          * 1)
+#define USE_FIXED_BORDER_RUNNING_GFX   (USE_NEW_STUFF          * 1)
+#define USE_QUICKSAND_BD_ROCK_BUGFIX   (USE_NEW_STUFF          * 0)
 
 #define USE_QUICKSAND_IMPACT_BUGFIX    (USE_NEW_STUFF          * 0)
 
+#define USE_CODE_THAT_BREAKS_SNAKE_BITE        (USE_NEW_STUFF          * 1)
+
+
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
        ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
 #endif
 
-#define GET_TARGET_ELEMENT(e, ch, cv, cs)                              \
+#define RESOLVED_REFERENCE_ELEMENT(be, e)                              \
+       ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
+        (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
+        (be) + (e) - EL_SELF)
+
+#define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                          \
        ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
         (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
         (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
         (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
         (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
-        (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
+        (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
+        (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
+        RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
+        (e))
 
 #define CAN_GROW_INTO(e)                                               \
        ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
@@ -1196,6 +1209,7 @@ static void InitField(int x, int y, boolean init_game)
        InitField(x, y, init_game);
 #endif
       }
+
       break;
   }
 
@@ -1352,6 +1366,7 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
 
 void DrawGameDoorValues()
 {
+  int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
   int dynamite_state = 0;
   int key_bits = 0;
   int i, j;
@@ -1400,7 +1415,7 @@ void DrawGameDoorValues()
   }
 
   DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
-                   local_player->score, TimeLeft, key_bits);
+                   local_player->score, time_value, key_bits);
 #endif
 }
 
@@ -1450,6 +1465,18 @@ static void resolve_group_element(int group_element, int recursion_depth)
 }
 #endif
 
+#if 0
+static void replace_reference_element(int base_element, int *element)
+{
+  if (*element >= EL_LAST_CE_8 && *element <= EL_NEXT_CE_8)
+  {
+    *element = base_element + *element - EL_SELF;
+    *element = (*element < EL_CUSTOM_START ? EL_CUSTOM_START :
+               *element > EL_CUSTOM_END   ? EL_CUSTOM_END   : *element);
+  }
+}
+#endif
+
 /*
   =============================================================================
   InitGameEngine()
@@ -1752,6 +1779,11 @@ static void InitGameEngine()
              for (l = 0; l < group->num_elements_resolved; l++)
                trigger_events[group->element_resolved[l]][k] = TRUE;
            }
+#if 1
+           else if (trigger_element == EL_ANY_ELEMENT)
+             for (l = 0; l < MAX_NUM_ELEMENTS; l++)
+               trigger_events[l][k] = TRUE;
+#endif
            else
              trigger_events[trigger_element][k] = TRUE;
          }
@@ -1889,6 +1921,29 @@ static void InitGameEngine()
         EL_EMPTY);
     }
   }
+
+#if 0
+  /* ---------- initialize reference elements ------------------------------- */
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+    struct ElementInfo *ei = &element_info[element];
+
+    for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+      replace_reference_element(element, &ei->content.e[x][y]);
+
+    for (j = 0; j < ei->num_change_pages; j++)
+    {
+      struct ElementChangeInfo *change = &ei->change_page[j];
+
+      replace_reference_element(element, &change->target_element);
+      replace_reference_element(element, &change->trigger_element);
+
+      for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+       replace_reference_element(element, &change->target_content.e[x][y]);
+    }
+  }
+#endif
 }
 
 int get_num_special_action(int element, int action_first, int action_last)
@@ -1993,6 +2048,8 @@ void InitGame()
 
     player->last_move_dir = MV_NONE;
 
+    player->is_active = FALSE;
+
     player->is_waiting = FALSE;
     player->is_moving = FALSE;
     player->is_auto_moving = FALSE;
@@ -5858,7 +5915,12 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
 
        Feld[x][y] = EL_QUICKSAND_EMPTYING;
+#if USE_QUICKSAND_BD_ROCK_BUGFIX
+       if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
+         Store[x][y] = EL_ROCK;
+#else
        Store[x][y] = EL_ROCK;
+#endif
 
        PlayLevelSoundAction(x, y, ACTION_EMPTYING);
       }
@@ -7801,7 +7863,8 @@ void MauerAbleger(int ax, int ay)
 
   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
       element == EL_EXPANDABLE_WALL_ANY ||
-      element == EL_EXPANDABLE_WALL)
+      element == EL_EXPANDABLE_WALL ||
+      element == EL_BD_EXPANDABLE_WALL)
   {
     if (links_frei)
     {
@@ -8410,6 +8473,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
        if (ei->collect_score == 0)
        {
+         int xx, yy;
+
 #if 0
          printf("::: CE_SCORE_GETS_ZERO\n");
 #endif
@@ -8420,6 +8485,26 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 #if 0
          printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
 #endif
+
+#if 1
+         /*
+           This is a very special case that seems to be a mixture between
+           CheckElementChange() and CheckTriggeredElementChange(): while
+           the first one only affects single elements that are triggered
+           directly, the second one affects multiple elements in the playfield
+           that are triggered indirectly by another element. This is a third
+           case: Changing the CE score always affects multiple identical CEs,
+           so every affected CE must be checked, not only the single CE for
+           which the CE score was changed in the first place (as every instance
+           of that CE shares the same CE score, and therefore also can change)!
+         */
+         SCAN_PLAYFIELD(xx, yy)
+         {
+           if (Feld[xx][yy] == element)
+             CheckElementChange(xx, yy, element, EL_UNDEFINED,
+                                CE_SCORE_GETS_ZERO);
+         }
+#endif
        }
       }
 
@@ -8450,8 +8535,15 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
 #if USE_NEW_CUSTOM_VALUE
   int last_ce_value = CustomValue[x][y];
 #endif
-  boolean add_player = (ELEM_IS_PLAYER(new_element) &&
-                       IS_WALKABLE(old_element));
+  boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
+  boolean add_player_onto_element = (new_element_is_player &&
+#if USE_CODE_THAT_BREAKS_SNAKE_BITE
+                                    /* this breaks SnakeBite when a snake is
+                                       halfway through a door that closes */
+                                    /* NOW FIXED AT LEVEL INIT IN files.c */
+                                    new_element != EL_SOKOBAN_FIELD_PLAYER &&
+#endif
+                                    IS_WALKABLE(old_element));
 
 #if 0
   /* check if element under the player changes from accessible to unaccessible
@@ -8465,7 +8557,7 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
   }
 #endif
 
-  if (!add_player)
+  if (!add_player_onto_element)
   {
     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
       RemoveMovingField(x, y);
@@ -8516,7 +8608,7 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
 #endif
 
   /* "ChangeCount" not set yet to allow "entered by player" change one time */
-  if (ELEM_IS_PLAYER(new_element))
+  if (new_element_is_player)
     RelocatePlayer(x, y, new_element);
 
   if (is_change)
@@ -8677,7 +8769,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
          ChangeEvent[ex][ey] = ChangeEvent[x][y];
 
          content_element = change->target_content.e[xx][yy];
-         target_element = GET_TARGET_ELEMENT(content_element, change,
+         target_element = GET_TARGET_ELEMENT(element, content_element, change,
                                              ce_value, ce_score);
 
          CreateElementFromChange(ex, ey, target_element);
@@ -8699,7 +8791,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
   }
   else
   {
-    target_element = GET_TARGET_ELEMENT(change->target_element, change,
+    target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
                                        ce_value, ce_score);
 
     if (element == EL_DIAGONAL_GROWING ||
@@ -9639,6 +9731,10 @@ void GameActions_EM_Main()
   for (i = 0; i < MAX_PLAYERS; i++)
     effective_action[i] = stored_player[i].effective_action;
 
+#if 0
+  printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
+#endif
+
   GameActions_EM(effective_action, warp_mode);
 
   CheckLevelTime();
@@ -10009,7 +10105,8 @@ void GameActions_RND()
     else if (element == EL_EXPANDABLE_WALL ||
             element == EL_EXPANDABLE_WALL_HORIZONTAL ||
             element == EL_EXPANDABLE_WALL_VERTICAL ||
-            element == EL_EXPANDABLE_WALL_ANY)
+            element == EL_EXPANDABLE_WALL_ANY ||
+            element == EL_BD_EXPANDABLE_WALL)
       MauerAbleger(x, y);
     else if (element == EL_FLAMES)
       CheckForDragon(x, y);
@@ -10362,7 +10459,7 @@ static void CheckGravityMovement(struct PlayerInfo *player)
   {
     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
-    boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
+    boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
     int jx = player->jx, jy = player->jy;
     boolean player_is_moving_to_valid_field =
       (!player_is_snapping &&
@@ -10585,6 +10682,8 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->move_delay_value = original_move_delay_value;
   }
 
+  player->is_active = FALSE;
+
   if (player->last_move_dir & MV_HORIZONTAL)
   {
     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
@@ -10596,6 +10695,17 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
   }
 
+#if USE_FIXED_BORDER_RUNNING_GFX
+  if (!moved && !player->is_active)
+  {
+    player->is_moving = FALSE;
+    player->is_digging = FALSE;
+    player->is_collecting = FALSE;
+    player->is_snapping = FALSE;
+    player->is_pushing = FALSE;
+  }
+#endif
+
   jx = player->jx;
   jy = player->jy;
 
@@ -11730,7 +11840,11 @@ int DigField(struct PlayerInfo *player,
   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
   {
     SplashAcid(x, y);
+#if 1
+    Feld[jx][jy] = player->artwork_element;
+#else
     Feld[jx][jy] = EL_PLAYER_1;
+#endif
     InitMovingField(jx, jy, MV_DOWN);
     Store[jx][jy] = EL_ACID;
     ContinueMoving(jx, jy);
@@ -12176,6 +12290,7 @@ int DigField(struct PlayerInfo *player,
     }
 
     player->is_pushing = TRUE;
+    player->is_active = TRUE;
 
     if (!(IN_LEV_FIELD(nextx, nexty) &&
          (IS_FREE(nextx, nexty) ||
@@ -12428,7 +12543,10 @@ int DigField(struct PlayerInfo *player,
   if (is_player)               /* function can also be called by EL_PENGUIN */
   {
     if (Feld[x][y] != element)         /* really digged/collected something */
+    {
       player->is_collecting = !player->is_digging;
+      player->is_active = TRUE;
+    }
   }
 
   return MP_MOVING;
@@ -12497,6 +12615,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
     return FALSE;
 
   player->is_snapping = TRUE;
+  player->is_active = TRUE;
 
   if (player->MovPos == 0)
   {