renamed another preprocessor constant
[rocksndiamonds.git] / src / game_bd / bd_caveengine.c
index b2c78716ecb08b8379e6e09215257c8e564cd0cc..2793d26f2f93c968e7b4d4d83ff2df997abac611 100644 (file)
@@ -126,9 +126,19 @@ void gd_cave_set_seconds_sound(GdCave *cave)
   }
 }
 
   }
 }
 
+// returns true if the element can fall
+static inline boolean el_can_fall(const int element)
+{
+  return (gd_elements[element & O_MASK].properties & P_CAN_FALL) != 0;
+}
+
 // play diamond or stone sound of given element.
 static void play_sound_of_element(GdCave *cave, GdElement element, int x, int y)
 {
 // play diamond or stone sound of given element.
 static void play_sound_of_element(GdCave *cave, GdElement element, int x, int y)
 {
+  // check if sound should be skipped for falling elements (and only be played on impact)
+  if (el_can_fall(element) && skip_bd_falling_sounds())
+    return;
+
   // stone and diamond fall sounds.
   switch (element)
   {
   // stone and diamond fall sounds.
   switch (element)
   {
@@ -449,7 +459,7 @@ static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
 }
 
 // returns true if the element is a player
 }
 
 // returns true if the element is a player
-static inline boolean is_player(const GdCave *cave, const int x, const int y)
+boolean is_player(const GdCave *cave, const int x, const int y)
 {
   return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
 }
 {
   return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
 }
@@ -467,6 +477,13 @@ static inline boolean can_be_hammered_dir(const GdCave *cave, const int x, const
   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
 }
 
   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
 }
 
+// returns true if the element can be pushed
+boolean can_be_pushed_dir(const GdCave *cave, const int x, const int y,
+                         const GdDirection dir)
+{
+  return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PUSHABLE) != 0;
+}
+
 // returns true if the element is explodable and explodes to space, for example the player
 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
 {
 // returns true if the element is explodable and explodes to space, for example the player
 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
 {
@@ -748,6 +765,13 @@ static void explode(GdCave *cave, int x, int y)
       creature_explode(cave, x, y, O_EXPLODE_1);
       break;
 
       creature_explode(cave, x, y, O_EXPLODE_1);
       break;
 
+    case O_ROCKET_1:
+    case O_ROCKET_2:
+    case O_ROCKET_3:
+    case O_ROCKET_4:
+      creature_explode(cave, x, y, O_EXPLODE_1);
+      break;
+
     case O_BUTTER_1:
     case O_BUTTER_2:
     case O_BUTTER_3:
     case O_BUTTER_1:
     case O_BUTTER_2:
     case O_BUTTER_3:
@@ -780,6 +804,7 @@ static void explode(GdCave *cave, int x, int y)
     case O_PLAYER_BOMB:
     case O_PLAYER_GLUED:
     case O_PLAYER_STIRRING:
     case O_PLAYER_BOMB:
     case O_PLAYER_GLUED:
     case O_PLAYER_STIRRING:
+    case O_PLAYER_ROCKET_LAUNCHER:
     case O_PLAYER_PNEUMATIC_LEFT:
     case O_PLAYER_PNEUMATIC_RIGHT:
       creature_explode(cave, x, y, O_EXPLODE_1);
     case O_PLAYER_PNEUMATIC_LEFT:
     case O_PLAYER_PNEUMATIC_RIGHT:
       creature_explode(cave, x, y, O_EXPLODE_1);
@@ -983,17 +1008,18 @@ static GdElement player_get_element(GdCave* cave, const GdElement object, int x,
   process a crazy dream-style teleporter.
   called from gd_cave_iterate, for a player or a player_bomb.
   player is standing at px, py, and trying to move in the direction player_move,
   process a crazy dream-style teleporter.
   called from gd_cave_iterate, for a player or a player_bomb.
   player is standing at px, py, and trying to move in the direction player_move,
-  where there is a teleporter.
-  we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
+  where there is a teleporter at (tx_start, ty_start). we check the whole cave,
+  from (tx_start + 1, ty_start), till we get back to (tx_start, ty_start) (by wrapping
   around). the first teleporter we find, and which is suitable, will be the destination.
   return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
   around). the first teleporter we find, and which is suitable, will be the destination.
   return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
- */
+*/
 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
 {
 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
 {
-  int tx, ty;
-
-  tx = px;
-  ty = py;
+  // start at teleporter position (not at player position!)
+  int tx_start = px + gd_dx[player_move];
+  int ty_start = py + gd_dy[player_move];
+  int tx = tx_start;
+  int ty = ty_start;
 
   do
   {
 
   do
   {
@@ -1025,7 +1051,7 @@ static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_mo
     }
   }
   // loop until we get back to original coordinates
     }
   }
   // loop until we get back to original coordinates
-  while (tx != px || ty != py);
+  while (tx != tx_start || ty != ty_start);
 
   // return false as we did not find any usable teleporter
   return FALSE;
 
   // return false as we did not find any usable teleporter
   return FALSE;
@@ -1362,6 +1388,9 @@ static boolean do_fall_try_magic(GdCave *cave, int x, int y,
     // active or non-active or anything, element falling in will always disappear
     store(cave, x, y, O_SPACE);
 
     // active or non-active or anything, element falling in will always disappear
     store(cave, x, y, O_SPACE);
 
+    if (cave->magic_wall_breakscan && cave->amoeba_state == GD_AM_AWAKE)
+      cave->convert_amoeba_this_frame = TRUE;
+
     return TRUE;
   }
   else
     return TRUE;
   }
   else
@@ -1595,6 +1624,9 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
   // score collected this frame
   cave->score = 0;
 
   // score collected this frame
   cave->score = 0;
 
+  // to implement buggy bd1 amoeba+magic wall behaviour
+  cave->convert_amoeba_this_frame = FALSE;
+
   // suicide only kills the active player
   // player_x, player_y was set by the previous iterate routine, or the cave setup.
   // we must check if there is a player or not - he may have exploded or something like that
   // suicide only kills the active player
   // player_x, player_y was set by the previous iterate routine, or the cave setup.
   // we must check if there is a player or not - he may have exploded or something like that
@@ -1746,6 +1778,19 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
                    move(cave, x, y, player_move, O_PLAYER_BOMB);
                  break;
 
                    move(cave, x, y, player_move, O_PLAYER_BOMB);
                  break;
 
+               case O_ROCKET_LAUNCHER:
+                 // if its a rocket launcher, remember he now has one.
+                 // we do not change the "remains" and "what" variables,
+                 // so that part of the code will be ineffective
+                 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
+                 store_dir(cave, x, y, player_move, O_SPACE);
+
+                 if (player_fire)
+                   store(cave, x, y, O_PLAYER_ROCKET_LAUNCHER);
+                 else
+                   move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
+                 break;
+
                case O_POT:
                  // we do not change the "remains" and "what" variables,
                  // so that part of the code will be ineffective
                case O_POT:
                  // we do not change the "remains" and "what" variables,
                  // so that part of the code will be ineffective
@@ -1887,6 +1932,98 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
          }
          break;
 
          }
          break;
 
+       case O_PLAYER_ROCKET_LAUNCHER:
+         // much simpler; cannot snap-push stones
+         if (cave->kill_player)
+         {
+           explode(cave, x, y);
+           break;
+         }
+
+         cave->player_seen_ago = 0;
+         // bd4 intermission caves have many players. so if one of them has exited,
+         // do not change the flag anymore. so this if () is needed
+         if (cave->player_state != GD_PL_EXITED)
+           cave->player_state = GD_PL_LIVING;
+
+         // firing a rocket?
+         if (player_move != GD_MV_STILL)
+         {
+           // if the player does not move, nothing to do
+           GdElement what = get_dir(cave, x, y, player_move);
+           GdElement remains = what;
+
+           // to fire a rocket, diagonal movement should not be allowed.
+           // so either x or y must be zero
+           if (player_fire)
+           {
+             // placing a rocket into empty space
+             if (is_space_dir(cave, x, y, player_move))
+             {
+               switch (player_move)
+               {
+                 case GD_MV_RIGHT:
+                   store_dir(cave, x, y, player_move, O_ROCKET_1);
+                   if (!cave->infinite_rockets)
+                     store(cave, x, y, O_PLAYER);
+                   break;
+
+                 case GD_MV_UP:
+                   store_dir(cave, x, y, player_move, O_ROCKET_2);
+                   if (!cave->infinite_rockets)
+                     store(cave, x, y, O_PLAYER);
+                   break;
+
+                 case GD_MV_LEFT:
+                   store_dir(cave, x, y, player_move, O_ROCKET_3);
+                   if (!cave->infinite_rockets)
+                     store(cave, x, y, O_PLAYER);
+                   break;
+
+                 case GD_MV_DOWN:
+                   store_dir(cave, x, y, player_move, O_ROCKET_4);
+                   if (!cave->infinite_rockets)
+                     store(cave, x, y, O_PLAYER);
+                   break;
+
+                 default:
+                   // cannot fire in other directions
+                   break;
+               }
+
+               gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
+             }
+
+             // a player with rocket launcher cannot snap elements, so stop here
+             break;
+           }
+
+           // pushing and collecting
+           // if we are 'eating' a teleporter, and the function returns true
+           // (teleporting worked), break here
+           if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
+             break;
+
+           // player fire is false...
+           if (do_push(cave, x, y, player_move, FALSE))
+           {
+             remains = O_SPACE;
+           }
+           else
+           {
+             // get element. if cannot get, player_get_element will return the same
+             remains = player_get_element(cave, what, x, y);
+           }
+
+           // if something changed, OR there is space, move.
+           if (remains != what || remains == O_SPACE)
+           {
+             // if anything changed, apply the change.
+             move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
+           }
+         }
+         break;
+
        case O_PLAYER_STIRRING:
          if (cave->kill_player)
          {
        case O_PLAYER_STIRRING:
          if (cave->kill_player)
          {
@@ -2733,6 +2870,13 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
          // ============================================================================
 
        case O_AMOEBA:
          // ============================================================================
 
        case O_AMOEBA:
+         // emulating BD1 amoeba+magic wall bug
+         if (cave->convert_amoeba_this_frame && amoeba_found_enclosed)
+         {
+           store(cave, x, y, cave->amoeba_enclosed_effect);
+           break;
+         }
+
          amoeba_count++;
          switch (cave->amoeba_state)
          {
          amoeba_count++;
          switch (cave->amoeba_state)
          {
@@ -3168,6 +3312,38 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
          }
          break;
 
          }
          break;
 
+         // ============================================================================
+         //    R O C K E T S
+         // ============================================================================
+
+       case O_ROCKET_1:
+         if (is_space_dir(cave, x, y, GD_MV_RIGHT))
+           move(cave, x, y, GD_MV_RIGHT, O_ROCKET_1);
+         else
+           explode(cave, x, y);
+         break;
+
+       case O_ROCKET_2:
+         if (is_space_dir(cave, x, y, GD_MV_UP))
+           move(cave, x, y, GD_MV_UP, O_ROCKET_2);
+         else
+           explode(cave, x, y);
+         break;
+
+       case O_ROCKET_3:
+         if (is_space_dir(cave, x, y, GD_MV_LEFT))
+           move(cave, x, y, GD_MV_LEFT, O_ROCKET_3);
+         else
+           explode(cave, x, y);
+         break;
+
+       case O_ROCKET_4:
+         if (is_space_dir(cave, x, y, GD_MV_DOWN))
+           move(cave, x, y, GD_MV_DOWN, O_ROCKET_4);
+         else
+           explode(cave, x, y);
+         break;
+
          // ============================================================================
          //    S I M P L E   C H A N G I N G;   E X P L O S I O N S
          // ============================================================================
          // ============================================================================
          //    S I M P L E   C H A N G I N G;   E X P L O S I O N S
          // ============================================================================
@@ -3472,7 +3648,7 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
   // PLAYER
 
   // check if player is alive.
   // PLAYER
 
   // check if player is alive.
-  if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
+  if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
     cave->player_state = GD_PL_DIED;
 
   // check if any voodoo exploded, and kill players the next scan if that happended.
     cave->player_state = GD_PL_DIED;
 
   // check if any voodoo exploded, and kill players the next scan if that happended.
@@ -3567,7 +3743,8 @@ void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire,
 
   // magic wall; if active&wait or not wait for hatching
   if (cave->magic_wall_state == GD_MW_ACTIVE &&
 
   // magic wall; if active&wait or not wait for hatching
   if (cave->magic_wall_state == GD_MW_ACTIVE &&
-      (cave->hatched || !cave->magic_timer_wait_for_hatching))
+      (cave->hatched || !cave->magic_timer_wait_for_hatching) &&
+      !(cave->magic_wall_time == 0 && cave->magic_timer_zero_is_infinite))
   {
     cave->magic_wall_time -= cave->speed;
     if (cave->magic_wall_time < 0)
   {
     cave->magic_wall_time -= cave->speed;
     if (cave->magic_wall_time < 0)