}
}
+// 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)
{
+ // 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)
{
}
// 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_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)
{
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_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);
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.
- */
+*/
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
{
}
}
// 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;
// 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
// 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
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
}
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_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)
{
}
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
// ============================================================================
{
if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
(amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
- play_sound_of_element(cave, O_AMOEBA, x, y);
+ play_sound_of_element(cave, O_AMOEBA, -1, -1);
}
// pneumatic hammer sound - overrides everything.
// 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)