2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
19 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 #include "cave/caverendered.hpp"
29 #include "cave/elementproperties.hpp"
30 #include "settings.hpp"
33 // for gravity and other routines.
34 // these arrays contain the rotated directions.
35 // ccw eighth: counter-clockwise, 1/8 turn (45 degrees)
36 // cw fourth: clockwise, 1/4 turn (90 degrees)
37 static GdDirection
const ccw_eighth
[] = { MV_STILL
, MV_UP_LEFT
, MV_UP
, MV_UP_RIGHT
, MV_RIGHT
, MV_DOWN_RIGHT
, MV_DOWN
, MV_DOWN_LEFT
};
38 static GdDirection
const ccw_fourth
[] = { MV_STILL
, MV_LEFT
, MV_UP_LEFT
, MV_UP
, MV_UP_RIGHT
, MV_RIGHT
, MV_DOWN_RIGHT
, MV_DOWN
, MV_DOWN_LEFT
, MV_LEFT
};
39 static GdDirection
const cw_eighth
[] = { MV_STILL
, MV_UP_RIGHT
, MV_RIGHT
, MV_DOWN_RIGHT
, MV_DOWN
, MV_DOWN_LEFT
, MV_LEFT
, MV_UP_LEFT
, MV_UP
};
40 static GdDirection
const cw_fourth
[] = { MV_STILL
, MV_RIGHT
, MV_DOWN_RIGHT
, MV_DOWN
, MV_DOWN_LEFT
, MV_LEFT
, MV_UP_LEFT
, MV_UP
, MV_UP_RIGHT
};
41 // 180 degrees turn of a direction
42 static GdDirection
const opposite
[] = { MV_STILL
, MV_DOWN
, MV_DOWN_LEFT
, MV_LEFT
, MV_UP_LEFT
, MV_UP
, MV_UP_RIGHT
, MV_RIGHT
, MV_DOWN_RIGHT
};
43 // doubling a direction (e.g. right=1,0 2x right=2,0
44 static GdDirection
const twice
[] = { MV_STILL
, MV_UP_2
, MV_UP_RIGHT_2
, MV_RIGHT_2
, MV_DOWN_RIGHT_2
, MV_DOWN_2
, MV_DOWN_LEFT_2
, MV_LEFT_2
, MV_UP_LEFT_2
};
47 void CaveRendered::add_particle_set(int x
, int y
, GdElementEnum particletype
) {
48 if (!gd_particle_effects
)
51 /* movements and sizes can depend on gravity. */
52 /* when stones & diamonds are falling, particle effects are perpendicular
53 * to the direction of gravity (eg. falling vertically, and some extends
54 * horizonally), so gx and gy are deliberately swapped in some expressions below. */
55 double gx
= gd_dx
[gravity
], gy
= gd_dy
[gravity
], agx
= fabs(gx
), agy
= fabs(gy
);
56 switch (particletype
) {
58 particles
.push_back(ParticleSet(75, 0.1, 0.15, x
+ 0.5, y
+ 0.5, 0.5, 0.5, 0, 0, 1, 1, dirt_particle_color
));
61 particles
.push_back(ParticleSet(75, 0.1, 0.15, x
+ 0.5, y
+ 0.5, 0.5, 0.5, 0, 0, 1, 1, dirt_2_particle_color
));
64 particles
.push_back(ParticleSet(75, 0.1, 0.15,
65 x
+ 0.5 + 0.5 * gx
, y
+ 0.5 + 0.5 * gy
, 0.25 + 0.25 * agy
, 0.25 + 0.25 * agx
,
66 0.5 * gx
, 0.5 * gy
, 1 + agy
, 1 + agx
, stone_particle_color
));
69 particles
.push_back(ParticleSet(75, 0.1, 0.15,
70 x
+ 0.5 + 0.5 * gx
, y
+ 0.5 + 0.5 * gy
, 0.25 + 0.25 * agy
, 0.25 + 0.25 * agx
,
71 0.5 * gx
, 0.5 * gy
, 1 + agy
, 1 + agx
, mega_stone_particle_color
));
75 particles
.push_back(ParticleSet(15, 0.03, 0.5,
76 x
+ 0.5 + 0.5 * gx
, y
+ 0.5 + 0.5 * gy
, 0.25, 0.25,
77 0, 0, 2, 2, diamond_particle_color
));
80 /* collecting diamond */
81 particles
.push_back(ParticleSet(8, 0.03, 0.5,
82 x
+ 0.5, y
+ 0.5, 0.25, 0.25,
83 0, 0, 2, 2, diamond_particle_color
));
86 /* for explosions, the original place of the particles is a 2x2 cave cell area, but they
88 particles
.push_back(ParticleSet(300, 0.05, 0.5, x
+ 0.5, y
+ 0.5, 1.0, 1.0, 0, 0, 4, 4, explosion_particle_color
));
91 particles
.push_back(ParticleSet(300, 0.05, 0.5, x
+ 0.5, y
+ 0.5, 1.0, 1.0, 0, 0, 4, 4, diamond_particle_color
));
94 // a magic wall creates particles in every frame. so add only very few particles!
95 // rather they should be bright like stars
96 particles
.push_back(ParticleSet(3, 0.01, 0.75, x
+ 0.5, y
+ 0.5, 0.5, 0.5, 0, 0, 1 + 2 * agx
, 1 + 2 * agy
, magic_wall_particle_color
));
98 case O_EXPANDING_WALL
:
99 particles
.push_back(ParticleSet(75, 0.1, 0.15, x
+ 0.5, y
+ 0.5, 0.5, 0.5, 0, 0, 1, 1, expanding_wall_particle_color
));
101 case O_EXPANDING_STEEL_WALL
:
102 particles
.push_back(ParticleSet(75, 0.1, 0.15, x
+ 0.5, y
+ 0.5, 0.5, 0.5, 0, 0, 1, 1, expanding_steel_wall_particle_color
));
105 // this should look like it's boiling
106 particles
.push_back(ParticleSet(10, 0.01, 0.5, x
+ 0.5, y
+ 0.5, 0.5, 0.5, 0, 0, 2, 2, lava_particle_color
));
109 particles
.push_back(ParticleSet(100, 0.03, 0.25, x
+ 0.9, y
+ 0.5, 0.5, 0.2, -4, 0.2, 3, 0.2, explosion_particle_color
));
112 particles
.push_back(ParticleSet(100, 0.03, 0.25, x
+ 0.5, y
+ 0.1, 0.2, 0.5, 0.2, 4, 0.2, 3, explosion_particle_color
));
115 particles
.push_back(ParticleSet(100, 0.03, 0.25, x
+ 0.1, y
+ 0.5, 0.5, 0.2, 4, 0.2, 3, 0.2, explosion_particle_color
));
118 particles
.push_back(ParticleSet(100, 0.03, 0.25, x
+ 0.5, y
+ 0.9, 0.2, 0.5, 0.2, -4, 0.2, 3, explosion_particle_color
));
126 /// Remembers to play the sound in the given cave.
127 /// The sound1, sound2 and sound3 variables will be handled by a game implementation.
128 /// This function only remembers to play them. It also checks the precedences.
129 void CaveRendered::sound_play(GdSound sound
, int x
, int y
) {
135 if (!water_sound
) return;
138 if (!amoeba_sound
) return;
140 case GD_S_MAGIC_WALL
:
141 if (!magic_wall_sound
) return;
144 if (!stone_sound
) return;
146 case GD_S_DIAMOND_RANDOM
:
147 if (!diamond_sound
) return;
150 if (!nut_sound
) return;
153 if (!nitro_sound
) return;
155 case GD_S_FALLING_WALL
:
156 if (!falling_wall_sound
) return;
158 case GD_S_EXPANDING_WALL
:
159 if (!expanding_wall_sound
) return;
161 case GD_S_BLADDER_SPENDER
:
162 if (!bladder_spender_sound
) return;
164 case GD_S_BLADDER_CONVERT
:
165 if (!bladder_convert_sound
) return;
168 if (!slime_sound
) return;
171 if (!lava_sound
) return;
173 case GD_S_ACID_SPREAD
:
174 if (!acid_spread_sound
) return;
176 case GD_S_BLADDER_MOVE
:
177 if (!bladder_sound
) return;
180 if (!biter_sound
) return;
183 if (!nut_sound
) return;
191 switch (gd_sound_get_channel(sound
)) {
202 g_assert_not_reached();
206 /* amoeba and magic wall _together_ make a different, mixed, "gritty" sound. */
207 /* so if we find a mixing situation (old=amoeba & new=magic or vice versa), do it. */
208 /* also, if we were already mixing, "continue" the mixing, but still check the
209 * distances of sounds below. */
210 if ((s
->sound
== GD_S_AMOEBA
&& sound
== GD_S_MAGIC_WALL
)
211 || (s
->sound
== GD_S_MAGIC_WALL
&& sound
== GD_S_AMOEBA
)
212 || (s
->sound
== GD_S_AMOEBA_MAGIC
))
213 sound
= GD_S_AMOEBA_MAGIC
;
215 /* sound coordinates relative to player */
216 int dx
= x
- player_x
, dy
= y
- player_y
;
217 /* if higher precedence, or same precedence but closer than the previous one, play. */
218 if (gd_sound_get_precedence(sound
) >= gd_sound_get_precedence(s
->sound
)
219 || (gd_sound_get_precedence(sound
) == gd_sound_get_precedence(s
->sound
)
220 && (dx
* dx
+ dy
* dy
) < (s
->dx
* s
->dx
+ s
->dy
* s
->dy
))) {
221 *s
= SoundWithPos(sound
, dx
, dy
);
226 /// play sound of a given element.
227 void CaveRendered::play_effect_of_element(GdElementEnum element
, int x
, int y
, GdDirectionEnum dir
, bool particles
) {
230 /* stone and diamond fall sounds. */
233 sound_play(GD_S_WATER
, x
, y
);
236 sound_play(GD_S_AMOEBA
, x
, y
);
239 sound_play(GD_S_MAGIC_WALL
, x
, y
);
241 add_particle_set(x
, y
, O_MAGIC_WALL
);
246 case O_FLYING_STONE_F
:
247 case O_WAITING_STONE
:
248 case O_CHASING_STONE
:
249 sound_play(GD_S_STONE
, x
, y
);
251 add_particle_set(x
, y
, O_STONE_F
);
256 sound_play(GD_S_STONE
, x
, y
);
258 add_particle_set(x
, y
, O_MEGA_STONE_F
);
263 case O_FLYING_DIAMOND
:
264 case O_FLYING_DIAMOND_F
:
265 sound_play(GD_S_DIAMOND_RANDOM
, x
, y
);
267 add_particle_set(x
, y
, O_DIAMOND_F
);
272 sound_play(GD_S_NUT
, x
, y
);
277 sound_play(GD_S_NITRO
, x
, y
);
281 case O_FALLING_WALL_F
:
282 sound_play(GD_S_FALLING_WALL
, x
, y
);
285 case O_H_EXPANDING_WALL
:
286 case O_V_EXPANDING_WALL
:
287 case O_EXPANDING_WALL
:
288 sound_play(GD_S_EXPANDING_WALL
, x
, y
);
290 add_particle_set(x
, y
, O_EXPANDING_WALL
);
293 case O_H_EXPANDING_STEEL_WALL
:
294 case O_V_EXPANDING_STEEL_WALL
:
295 case O_EXPANDING_STEEL_WALL
:
296 sound_play(GD_S_EXPANDING_WALL
, x
, y
);
298 add_particle_set(x
, y
, O_EXPANDING_STEEL_WALL
);
301 case O_BLADDER_SPENDER
:
302 sound_play(GD_S_BLADDER_SPENDER
, x
, y
);
306 sound_play(GD_S_BLADDER_CONVERT
, x
, y
);
310 sound_play(GD_S_SLIME
, x
, y
);
314 sound_play(GD_S_LAVA
, x
, y
);
318 sound_play(GD_S_ACID_SPREAD
, x
, y
);
322 sound_play(GD_S_BLADDER_MOVE
, x
, y
);
329 sound_play(GD_S_BITER_EAT
, x
, y
);
333 sound_play(GD_S_WALK_EARTH
, x
, y
);
335 add_particle_set(x
, y
, O_DIRT
);
339 sound_play(GD_S_WALK_EARTH
, x
, y
);
341 add_particle_set(x
, y
, O_DIRT2
);
348 sound_play(GD_S_DIRT_BALL
, x
, y
);
350 add_particle_set(x
, y
, O_DIRT2
);
360 void CaveRendered::play_eat_sound_of_element(GdElementEnum element
, int x
, int y
) {
363 sound_play(GD_S_DIAMOND_KEY_COLLECT
, x
, y
);
366 sound_play(GD_S_KEY_COLLECT
, x
, y
);
369 sound_play(GD_S_KEY_COLLECT
, x
, y
);
372 sound_play(GD_S_KEY_COLLECT
, x
, y
);
375 sound_play(GD_S_DOOR_OPEN
, x
, y
);
378 sound_play(GD_S_DOOR_OPEN
, x
, y
);
381 sound_play(GD_S_DOOR_OPEN
, x
, y
);
383 case O_CREATURE_SWITCH
:
384 sound_play(GD_S_SWITCH_CREATURES
, x
, y
);
386 case O_EXPANDING_WALL_SWITCH
:
387 sound_play(GD_S_SWITCH_EXPANDING
, x
, y
);
389 case O_BITER_SWITCH
: /* biter change delay */
390 sound_play(GD_S_SWITCH_BITER
, x
, y
);
392 case O_REPLICATOR_SWITCH
: /* replicator on/off switch */
393 sound_play(GD_S_SWITCH_REPLICATOR
, x
, y
);
395 case O_CONVEYOR_SWITCH
: /* conveyor belts on/off */
396 sound_play(GD_S_SWITCH_CONVEYOR
, x
, y
);
398 case O_CONVEYOR_DIR_SWITCH
: /* conveyor belts switch direction */
399 sound_play(GD_S_SWITCH_CONVEYOR
, x
, y
);
402 case O_DIRT_SLOPED_UP_RIGHT
:
403 case O_DIRT_SLOPED_UP_LEFT
:
404 case O_DIRT_SLOPED_DOWN_LEFT
:
405 case O_DIRT_SLOPED_DOWN_RIGHT
:
406 sound_play(GD_S_WALK_EARTH
, x
, y
);
411 sound_play(GD_S_WALK_EARTH
, x
, y
);
413 case O_STEEL_EATABLE
:
414 case O_BRICK_EATABLE
:
415 sound_play(GD_S_WALK_EARTH
, x
, y
);
419 sound_play(GD_S_WALK_EMPTY
, x
, y
);
422 sound_play(GD_S_SWEET_COLLECT
, x
, y
);
424 case O_PNEUMATIC_HAMMER
:
425 sound_play(GD_S_PNEUMATIC_COLLECT
, x
, y
);
428 sound_play(GD_S_CLOCK_COLLECT
, x
, y
);
431 case O_FLYING_DIAMOND
:
432 sound_play(GD_S_DIAMOND_COLLECT
, x
, y
);
435 sound_play(GD_S_SKELETON_COLLECT
, x
, y
);
444 void CaveRendered::play_eat_particle_of_element(GdElementEnum element
, int x
, int y
) {
447 case O_DIRT_SLOPED_UP_RIGHT
:
448 case O_DIRT_SLOPED_UP_LEFT
:
449 case O_DIRT_SLOPED_DOWN_LEFT
:
450 case O_DIRT_SLOPED_DOWN_RIGHT
:
451 add_particle_set(x
, y
, O_DIRT
);
456 add_particle_set(x
, y
, O_DIRT2
);
459 case O_FLYING_DIAMOND
:
460 add_particle_set(x
, y
, O_DIAMOND
);
462 case O_STEEL_EATABLE
:
463 case O_BRICK_EATABLE
:
472 /// sets timeout sound.
473 void CaveRendered::set_seconds_sound() {
474 /* this is an integer division, so 0 seconds can be 0.5 seconds... */
475 /* also, when this reaches 8, the player still has 8.9999 seconds. so the sound is played at almost t=9s. */
476 switch (time
/ timing_factor
) {
478 sound_play(GD_S_TIMEOUT_1
, player_x
, player_y
);
481 sound_play(GD_S_TIMEOUT_2
, player_x
, player_y
);
484 sound_play(GD_S_TIMEOUT_3
, player_x
, player_y
);
487 sound_play(GD_S_TIMEOUT_4
, player_x
, player_y
);
490 sound_play(GD_S_TIMEOUT_5
, player_x
, player_y
);
493 sound_play(GD_S_TIMEOUT_6
, player_x
, player_y
);
496 sound_play(GD_S_TIMEOUT_7
, player_x
, player_y
);
499 sound_play(GD_S_TIMEOUT_8
, player_x
, player_y
);
502 sound_play(GD_S_TIMEOUT_9
, player_x
, player_y
);
508 /// Returns the element at x,y.
509 inline GdElementEnum
CaveRendered::get(int x
, int y
) const {
513 /// Returns the element at (x,y)+dir.
514 inline GdElementEnum
CaveRendered::get(int x
, int y
, GdDirectionEnum dir
) const {
515 return get(x
+ gd_dx
[dir
], y
+ gd_dy
[dir
]);
518 /// Returns true, if element at (x,y)+dir explodes if hit by a stone (for example, a firefly).
519 inline bool CaveRendered::explodes_by_hit(int x
, int y
, GdDirectionEnum dir
) const {
520 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_EXPLODES_BY_HIT
) != 0;
523 /// returns true, if the element is not explodable (for example the steel wall).
524 inline bool CaveRendered::non_explodable(int x
, int y
) const {
525 return (gd_element_properties
[get(x
, y
)].flags
& P_NON_EXPLODABLE
) != 0;
528 /// returns true, if the element at (x,y)+dir can be eaten by the amoeba (dirt, space)
529 inline bool CaveRendered::amoeba_eats(int x
, int y
, GdDirectionEnum dir
) const {
530 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_AMOEBA_CONSUMES
) != 0;
533 /// Returns true if the element is sloped, so stones and diamonds roll down on it.
534 /// For example a stone or brick wall.
535 /// Some elements can be sloped in specific directions only; for example a wall
536 /// like /| is sloped from the up to the left.
537 /// @param x The x coordinate
538 /// @param y The y coordinate
539 /// @param dir The coordinate to move from (x,y), e.g. element at (x,y)+dir is checked.
540 /// @param slop The direction in which the element should be sloped.
541 bool CaveRendered::sloped(int x
, int y
, GdDirectionEnum dir
, GdDirectionEnum slop
) const {
544 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_SLOPED_LEFT
) != 0;
546 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_SLOPED_RIGHT
) != 0;
548 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_SLOPED_UP
) != 0;
550 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_SLOPED_DOWN
) != 0;
558 /// returns true if the element is sloped for bladder movement (brick=yes, diamond=no, for example)
559 inline bool CaveRendered::sloped_for_bladder(int x
, int y
, GdDirectionEnum dir
) const {
560 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_BLADDER_SLOPED
) != 0;
563 /// returns true if the element at (x,y)+dir can blow up a fly by touching it.
564 inline bool CaveRendered::blows_up_flies(int x
, int y
, GdDirectionEnum dir
) const {
565 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_BLOWS_UP_FLIES
) != 0;
568 /// returns true if the element is a counter-clockwise creature
569 inline bool CaveRendered::rotates_ccw(int x
, int y
) const {
570 return (gd_element_properties
[get(x
, y
)].flags
& P_CCW
) != 0;
573 /// returns true if the element is a player (normal player, player glued, player with bomb)
574 bool CaveRendered::is_player(int x
, int y
) const {
575 return (gd_element_properties
[get(x
, y
)].flags
& P_PLAYER
) != 0;
578 /// returns true if the element at (x,y)+dir is a player (normal player, player glued, player with bomb)
579 bool CaveRendered::is_player(int x
, int y
, GdDirectionEnum dir
) const {
580 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_PLAYER
) != 0;
583 /// returns true if the element at (x,y)+dir can be hammered.
584 inline bool CaveRendered::can_be_hammered(int x
, int y
, GdDirectionEnum dir
) const {
585 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_CAN_BE_HAMMERED
) != 0;
588 /// Returns true if the element at (x,y)+dir can be pushed.
589 /// @todo should be inlined.
590 bool CaveRendered::can_be_pushed(int x
, int y
, GdDirectionEnum dir
) const {
591 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_CAN_BE_PUSHED
) != 0;
594 /// returns true if the element at (x,y) is the first animation stage of an explosion
595 inline bool CaveRendered::is_first_stage_of_explosion(int x
, int y
) const {
596 return (gd_element_properties
[get(x
, y
)].flags
& P_EXPLOSION_FIRST_STAGE
) != 0;
599 /// returns true if the element sits on and is moved by the conveyor belt
600 inline bool CaveRendered::moved_by_conveyor_top(int x
, int y
, GdDirectionEnum dir
) const {
601 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_MOVED_BY_CONVEYOR_TOP
) != 0;
604 /// returns true if the elements floats upwards, and is conveyed by the conveyor belt which is OVER it
605 inline bool CaveRendered::moved_by_conveyor_bottom(int x
, int y
, GdDirectionEnum dir
) const {
606 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_MOVED_BY_CONVEYOR_BOTTOM
) != 0;
609 /// returns true if the element is a scanned one (needed by the engine)
610 inline bool CaveRendered::is_scanned(int x
, int y
) const {
611 return is_scanned_element(get(x
, y
));
614 /// returns true if the element is a scanned one (needed by the engine)
615 inline bool CaveRendered::is_scanned(int x
, int y
, GdDirectionEnum dir
) const {
616 return is_scanned_element(get(x
+ gd_dx
[dir
], y
+ gd_dy
[dir
]));
619 /// Returns true if neighboring element is "e", or equivalent to "e".
620 /// Dirt is treated in a special way; eg. if is_like_element(O_DIRT) is
621 /// asked, an an O_DIRT2 is there, true is returned.
622 /// Also, lava is special; if is_like_element(O_SPACE) is asked, and
623 /// an O_LAVA is there, true is returned. This way any movement
624 /// is allowed by any creature and player into lava.
625 /// @param x The x coordinate on the map
626 /// @param y The y coordinate on the map
627 /// @param dir The direction to add to (x,y) and check the element at
628 /// @param e The element to compare (x,y)+dir to
629 /// @return True, if they are equivalent
630 bool CaveRendered::is_like_element(int x
, int y
, GdDirectionEnum dir
, GdElementEnum e
) const {
631 int examined
= get(x
, y
, dir
);
633 /* if it is a dirt-like, change to dirt, so equality will evaluate to true */
634 if (gd_element_properties
[examined
].flags
& P_DIRT
)
636 if (gd_element_properties
[e
].flags
& P_DIRT
)
638 /* if the element on the map is a lava, it should be like space */
639 if (examined
== O_LAVA
)
641 /* now they are "normalized", compare and return */
642 return e
== examined
;
645 /// Returns true if the neighboring element (x,y)+dir is a dirt or lava.
646 /// This is a shorthand function to check easily if there is space somewhere.
647 /// Any movement which is possible into space, must also be possible into lava.
648 /// Therefore 'if (map(x,y)==O_SPACE)' must not be used!
649 /// Lava absorbs everything going into it. Everything.
650 /// But it does not "pull" elements; only the things disappear which
651 /// _do_ go directly into it. So if the player steps into the lava,
652 /// he will die. If a dragonfly flies over it, it will not.
653 /// This behavior is implemented in the is_like_space and the store
654 /// functions. is_like_space returns true for the lava, too. The store
655 /// function ignores any store requests into the lava.
656 /// The player_eat_element function will also behave for lava as it does for space.
657 inline bool CaveRendered::is_like_space(int x
, int y
, GdDirectionEnum dir
) const {
658 int e
= get(x
, y
, dir
);
659 return e
== O_SPACE
|| e
== O_LAVA
;
662 /// Returns true, if element at (x,y)+dir is like dirt.
663 /// All dirt types must be equivalent; for example when allowing the player to
664 /// place a bomb in dirt, or when a nitro pack is bouncing on a piece of dirt
665 /// (without exploding).
666 /// Therefore 'if (map(x,y)==O_DIRT)' must not be used!
667 inline bool CaveRendered::is_like_dirt(int x
, int y
, GdDirectionEnum dir
) const {
668 return (gd_element_properties
[get(x
, y
, dir
)].flags
& P_DIRT
) != 0;
672 /// Store an element at a given position; lava absorbs everything.
673 /// If there is a lava originally at the given position, sound is played, and
674 /// the map is NOT changed.
675 /// The element given is changed to its "scanned" state, if there is such.
676 inline void CaveRendered::store(int x
, int y
, GdElementEnum element
, bool disable_particle
) {
677 GdElementEnum
&cell
= map(x
, y
);
678 if (cell
== O_LAVA
) {
679 play_effect_of_element(O_LAVA
, x
, y
);
682 cell
= scanned_pair(element
);
686 /// Store an element to (x,y)+dir.
687 inline void CaveRendered::store(int x
, int y
, GdDirectionEnum dir
, GdElementEnum element
) {
688 store(x
+ gd_dx
[dir
], y
+ gd_dy
[dir
], element
);
691 /// Store the element to (x,y)+dir, and store a space to (x,y).
692 inline void CaveRendered::move(int x
, int y
, GdDirectionEnum dir
, GdElementEnum element
) {
693 store(x
, y
, dir
, element
);
694 store(x
, y
, O_SPACE
);
697 /// increment a cave element; can be used for elements which are one after the other, for example bladder1, bladder2, bladder3...
698 /// @todo to be removed
699 inline void CaveRendered::next(int x
, int y
) {
700 map(x
, y
) = GdElementEnum(map(x
, y
) + 1);
703 /// Remove th scanned "bit" from an element.
704 /// To be called only for scanned elements!!!
705 inline void CaveRendered::unscan(int x
, int y
) {
706 if (is_scanned(x
, y
))
707 map(x
, y
) = gd_element_properties
[get(x
, y
)].pair
;
711 /// Change the cell at (x,y) to a given explosion type.
712 /// Used by 3x3 explosion functions.
713 /// Take care of non explodable elements.
714 /// Take care of other special cases, like a voodoo dying,
715 /// and a nitro pack explosion triggered.
716 void CaveRendered::cell_explode(int x
, int y
, GdElementEnum explode_to
) {
717 if (non_explodable(x
, y
))
720 if (voodoo_any_hurt_kills_player
&& get(x
, y
) == O_VOODOO
)
721 voodoo_touched
= true;
723 if (get(x
, y
) == O_VOODOO
&& !voodoo_disappear_in_explosion
)
724 /* voodoo turns into a time penalty */
725 store(x
, y
, O_TIME_PENALTY
);
726 else if (get(x
, y
) == O_NITRO_PACK
|| get(x
, y
) == O_NITRO_PACK_F
)
727 /* nitro pack inside an explosion - it is now triggered */
728 store(x
, y
, O_NITRO_PACK_EXPLODE
);
730 /* for everything else. disable particle effects, as the explosion already generates */
731 store(x
, y
, explode_to
, true /* disable particle effects */);
734 /// A creature at (x,y) explodes to a 3x3 square.
735 void CaveRendered::creature_explode(int x
, int y
, GdElementEnum explode_to
) {
736 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
737 ckdelay_current
+= 1200;
738 sound_play(GD_S_EXPLOSION
, x
, y
);
740 for (int yy
= y
- 1; yy
<= y
+ 1; yy
++)
741 for (int xx
= x
- 1; xx
<= x
+ 1; xx
++)
742 cell_explode(xx
, yy
, explode_to
);
745 /// A nitro pack at (x,y) explodes to a 3x3 square.
746 void CaveRendered::nitro_explode(int x
, int y
) {
747 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
748 ckdelay_current
+= 1200;
749 sound_play(GD_S_NITRO_EXPLOSION
, x
, y
);
751 for (int yy
= y
- 1; yy
<= y
+ 1; yy
++)
752 for (int xx
= x
- 1; xx
<= x
+ 1; xx
++)
753 cell_explode(xx
, yy
, O_NITRO_EXPL_1
);
754 /* the current cell is explicitly changed into a nitro expl, as cell_explode changes it to a triggered nitro pack */
755 store(x
, y
, O_NITRO_EXPL_1
);
758 /// A voodoo explodes, leaving a 3x3 steel and a time penalty behind.
759 void CaveRendered::voodoo_explode(int x
, int y
) {
760 if (voodoo_any_hurt_kills_player
)
761 voodoo_touched
= true;
763 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
764 ckdelay_current
+= 1000;
765 sound_play(GD_S_VOODOO_EXPLOSION
, x
, y
);
767 /* voodoo explodes to 3x3 steel */
768 for (int yy
= y
- 1; yy
<= y
+ 1; yy
++)
769 for (int xx
= x
- 1; xx
<= x
+ 1; xx
++)
770 store(xx
, yy
, O_PRE_STEEL_1
);
771 /* middle is a time penalty (which will be turned into a gravestone) */
772 store(x
, y
, O_TIME_PENALTY
);
775 /// Explode cell at (x,y), but skip voodooo.
776 /// A bomb does not explode the voodoo, neither does the ghost.
777 /// This function checks these, and stores the new element given or not.
778 /// Destroying the voodoo is also controlled by the voodoo_disappear_in_explosion flag.
779 void CaveRendered::cell_explode_skip_voodoo(int x
, int y
, GdElementEnum expl
) {
780 if (non_explodable(x
, y
))
782 /* bomb does not explode voodoo */
783 if (!voodoo_disappear_in_explosion
&& get(x
, y
) == O_VOODOO
)
785 if (voodoo_any_hurt_kills_player
&& get(x
, y
) == O_VOODOO
)
786 voodoo_touched
= true;
790 /// An x shaped ghost explosion.
791 void CaveRendered::ghost_explode(int x
, int y
) {
792 /* the processing of an explosion took pretty much time: processing 5 elements */
793 ckdelay_current
+= 650;
794 sound_play(GD_S_GHOST_EXPLOSION
, x
, y
);
796 cell_explode_skip_voodoo(x
, y
, O_GHOST_EXPL_1
);
797 cell_explode_skip_voodoo(x
- 1, y
- 1, O_GHOST_EXPL_1
);
798 cell_explode_skip_voodoo(x
+ 1, y
+ 1, O_GHOST_EXPL_1
);
799 cell_explode_skip_voodoo(x
- 1, y
+ 1, O_GHOST_EXPL_1
);
800 cell_explode_skip_voodoo(x
+ 1, y
- 1, O_GHOST_EXPL_1
);
803 /// A + shaped bomb explosion.
804 void CaveRendered::bomb_explode(int x
, int y
) {
805 /* the processing of an explosion took pretty much time: processing 5 elements */
806 ckdelay_current
+= 650;
807 sound_play(GD_S_BOMB_EXPLOSION
, x
, y
);
809 cell_explode_skip_voodoo(x
, y
, O_BOMB_EXPL_1
);
810 cell_explode_skip_voodoo(x
- 1, y
, O_BOMB_EXPL_1
);
811 cell_explode_skip_voodoo(x
+ 1, y
, O_BOMB_EXPL_1
);
812 cell_explode_skip_voodoo(x
, y
+ 1, O_BOMB_EXPL_1
);
813 cell_explode_skip_voodoo(x
, y
- 1, O_BOMB_EXPL_1
);
816 /// Explode the thing at (x,y).
817 /// Checks the element, and selects the correct exploding type accordingly.
818 void CaveRendered::explode(int x
, int y
) {
819 /* if this remains false throughout the switch() below, particles will be added
820 * in the bottom line of the function. if there are particles already added
821 * somewhere, this is set to true. */
822 bool particles_added
= false;
834 voodoo_explode(x
, y
);
839 case O_NITRO_PACK_EXPLODE
:
844 creature_explode(x
, y
, O_AMOEBA_2_EXPL_1
);
847 case O_FALLING_WALL_F
:
848 creature_explode(x
, y
, O_EXPLODE_1
);
855 creature_explode(x
, y
, O_EXPLODE_1
);
862 add_particle_set(x
, y
, O_PRE_DIA_1
);
863 particles_added
= true;
864 creature_explode(x
, y
, butterfly_explode_to
);
871 add_particle_set(x
, y
, O_PRE_DIA_1
);
872 particles_added
= true;
873 creature_explode(x
, y
, alt_butterfly_explode_to
);
880 creature_explode(x
, y
, firefly_explode_to
);
883 case O_ALT_FIREFLY_1
:
884 case O_ALT_FIREFLY_2
:
885 case O_ALT_FIREFLY_3
:
886 case O_ALT_FIREFLY_4
:
887 creature_explode(x
, y
, alt_firefly_explode_to
);
893 case O_PLAYER_STIRRING
:
894 case O_PLAYER_ROCKET_LAUNCHER
:
895 case O_PLAYER_PNEUMATIC_LEFT
:
896 case O_PLAYER_PNEUMATIC_RIGHT
:
897 creature_explode(x
, y
, O_EXPLODE_1
);
904 creature_explode(x
, y
, stonefly_explode_to
);
911 creature_explode(x
, y
, dragonfly_explode_to
);
915 g_assert_not_reached();
919 if (!particles_added
)
920 add_particle_set(x
, y
, O_EXPLODE_1
);
923 /// Explode the element at (x,y)+dir.
924 /// A simple wrapper for the explode(int x, int y) function without
925 /// the dir parameter.
926 void CaveRendered::explode(int x
, int y
, GdDirectionEnum dir
) {
927 explode(x
+ gd_dx
[dir
], y
+ gd_dy
[dir
]);
931 /// The player eats or activates the given element.
932 /// This function does all things that should happen when the
933 /// player eats something - increments score, plays sound etc.
934 /// This function is also used to activate switches, and to collect
936 /// It returns the remaining element, which is usually space;
937 /// might be some other thing. (example: DIRT for CLOCK)
938 /// This does NOT take snap_element into consideration.
939 /// @param element Element to eat
940 /// @param x The coordinate of player
941 /// @param y The coordinate of player
942 /// @param dir The direction the player is moving
943 /// @return remaining element
944 GdElementEnum
CaveRendered::player_eat_element(GdElementEnum element
, int x
, int y
, GdDirectionEnum dir
) {
945 GdElementEnum was_element
= element
;
951 diamond_key_collected
= true;
993 case O_CREATURE_SWITCH
: /* creatures change direction. */
994 creatures_backwards
= !creatures_backwards
;
996 case O_EXPANDING_WALL_SWITCH
: /* expanding wall change direction. */
997 expanding_wall_changed
= !expanding_wall_changed
;
999 case O_BITER_SWITCH
: /* biter change delay */
1000 biter_delay_frame
++;
1001 if (biter_delay_frame
== 4)
1002 biter_delay_frame
= 0;
1004 case O_REPLICATOR_SWITCH
: /* replicator on/off switch */
1005 replicators_active
= !replicators_active
;
1007 case O_CONVEYOR_SWITCH
: /* conveyor belts on/off */
1008 conveyor_belts_active
= !conveyor_belts_active
;
1010 case O_CONVEYOR_DIR_SWITCH
: /* conveyor belts switch direction */
1011 conveyor_belts_direction_changed
= !conveyor_belts_direction_changed
;
1013 case O_GRAVITY_SWITCH
:
1014 if (gravity_switch_active
&& (dir
== MV_LEFT
|| dir
== MV_RIGHT
|| dir
== MV_UP
|| dir
== MV_DOWN
)) {
1015 sound_play(GD_S_SWITCH_GRAVITY
, x
, y
);
1016 gravity_will_change
= gravity_change_time
* timing_factor
;
1017 gravity_next_direction
= dir
;
1018 gravity_switch_active
= false;
1026 case O_DIRT_SLOPED_UP_RIGHT
:
1027 case O_DIRT_SLOPED_UP_LEFT
:
1028 case O_DIRT_SLOPED_DOWN_LEFT
:
1029 case O_DIRT_SLOPED_DOWN_RIGHT
:
1032 case O_STEEL_EATABLE
:
1033 case O_BRICK_EATABLE
:
1038 case O_LAVA
: /* player goes into lava, as if it was space */
1047 case O_PNEUMATIC_HAMMER
:
1048 got_pneumatic_hammer
= true;
1054 time
+= time_bonus
* timing_factor
;
1055 if (time
> max_time
* timing_factor
)
1056 time
-= max_time
* timing_factor
;
1057 /* no space, rather a dirt remains there... */
1062 case O_FLYING_DIAMOND
:
1063 score
+= diamond_value
;
1064 ++diamonds_collected
;
1065 if (diamonds_needed
== diamonds_collected
) {
1067 diamond_value
= extra_diamond_value
; /* extra is worth more points. */
1068 gate_open_flash
= 1;
1069 sound_play(GD_S_CRACK
, x
, y
);
1074 ++skeletons_collected
;
1075 for (int i
= 0; i
< skeletons_worth_diamonds
; i
++)
1076 player_eat_element(O_DIAMOND
, x
, y
, MV_STILL
); /* as if player got a diamond */
1080 case O_INVIS_OUTBOX
:
1081 player_state
= GD_PL_EXITED
; /* player now exits the cave! */
1086 /* non-eatable, does nothing */
1091 if (element
!= O_NONE
) {
1092 play_eat_sound_of_element(was_element
, x
, y
);
1093 play_eat_particle_of_element(was_element
, x
, y
);
1099 Process a crazy dream-style teleporter.
1100 Called from cave_iterate, for a player or a player_bomb.
1101 Player is standing at px, py, and trying to move in the direction player_move,
1102 where there is a teleporter.
1103 We check the whole cave, from (px+1,py), till we get back to (px,py) (by wrapping
1104 around). The first teleporter we find, and which is suitable, will be the destination.
1105 @param px The coordinate of the player which tries to move into the teleporter, x.
1106 @param py The coordinate of the player, y.
1107 @param player_move The direction he is moving into.
1108 @return True, if the player is teleported, false, if no suitable teleporter found.
1110 bool CaveRendered::do_teleporter(int px
, int py
, GdDirectionEnum player_move
) {
1113 bool teleported
= false;
1115 /* jump to next element; wrap around columns and rows. */
1123 /* if we found a teleporter... */
1124 if (get(tx
, ty
) == O_TELEPORTER
&& is_like_space(tx
, ty
, player_move
)) {
1125 store(tx
, ty
, player_move
, get(px
, py
)); /* new player appears near teleporter found */
1126 store(px
, py
, O_SPACE
); /* current player disappears */
1127 sound_play(GD_S_TELEPORTER
, tx
, ty
);
1128 teleported
= true; /* success */
1130 } while (!teleported
&& (tx
!= px
|| ty
!= py
)); /* loop until we get back to original coordinates */
1135 Try to push an element.
1136 Also does move the specified _element_, if possible.
1137 Up to the caller to move the _player_itself_, as the movement might be a snap, not a real movement.
1138 @return true if the push is possible.
1140 bool CaveRendered::do_push(int x
, int y
, GdDirectionEnum player_move
, bool player_fire
) {
1141 GdElementEnum what
= get(x
, y
, player_move
);
1142 GdDirection grav_compat
; /* gravity for falling wall, bladder, ... */
1143 if (gravity_affects_all
)
1144 grav_compat
= gravity
;
1146 grav_compat
= MV_DOWN
;
1147 bool result
= false;
1149 /* do a switch on what element is being pushed to determine probability. */
1151 case O_WAITING_STONE
:
1154 case O_CHASING_STONE
:
1156 case O_FLYING_STONE
:
1158 /* pushing some kind of stone or nut*/
1159 /* directions possible: 90degrees cw or ccw to current gravity. */
1160 /* only push if player dir is orthogonal to gravity, ie. gravity down, pushing left&right possible */
1161 if (player_move
== ccw_fourth
[gravity
] || player_move
== cw_fourth
[gravity
]) {
1165 /* different probabilities for different elements. */
1166 /* remember that probabilities are 1/million, stored as integers! */
1168 case O_WAITING_STONE
:
1169 prob
= 1000000; /* waiting stones are light, can always push */
1171 case O_CHASING_STONE
:
1172 if (sweet_eaten
) /* chasing can be pushed if player is turbo */
1173 prob
= 1000000; /* with p=1, always push */
1176 if (mega_stones_pushable_with_sweet
&& sweet_eaten
) /* mega may(!) be pushed if player is turbo */
1177 prob
= 1000000; /* p=1, always push */
1181 case O_FLYING_STONE
:
1184 prob
= pushing_stone_prob_sweet
; /* probability with sweet */
1186 prob
= pushing_stone_prob
; /* probability without sweet. */
1189 g_assert_not_reached();
1193 if (is_like_space(x
, y
, twice
[player_move
]) && random
.rand_int_range(0, 1000000) < prob
) {
1194 /* if decided that he is able to push, */
1195 play_effect_of_element(what
, x
+ gd_dx
[player_move
], y
+ gd_dy
[player_move
]);
1196 /* if pushed a stone, it "bounces". all other elements are simply pushed. */
1197 if (what
== O_STONE
)
1198 store(x
, y
, twice
[player_move
], stone_bouncing_effect
);
1200 store(x
, y
, twice
[player_move
], what
);
1215 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1216 * not an O_BLADDER_x. */
1217 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
1219 /* first check: we cannot push a bladder "up" */
1220 if (player_move
!= opposite
[grav_compat
]) {
1221 /* pushing a bladder "down". p=player, o=bladder, 1, 2, 3=directions to check. */
1222 /* player moving in the direction of gravity. */
1226 if (player_move
== grav_compat
) {
1227 if (is_like_space(x
, y
, twice
[player_move
])) /* pushing bladder down */
1228 store(x
, y
, twice
[player_move
], O_BLADDER
), result
= true;
1229 else if (is_like_space(x
, y
, cw_eighth
[grav_compat
])) /* if no space to push down, maybe left (down-left to player) */
1230 /* left is "down, turned right (cw)" */
1231 store(x
, y
, cw_eighth
[grav_compat
], O_BLADDER
), result
= true;
1232 else if (is_like_space(x
, y
, ccw_eighth
[grav_compat
])) /* if not, maybe right (down-right to player) */
1233 store(x
, y
, ccw_eighth
[grav_compat
], O_BLADDER
), result
= true;
1235 /* pushing a bladder "left". p=player, o=bladder, 1, 2, 3=directions to check. */
1239 else if (player_move
== cw_fourth
[grav_compat
]) {
1240 if (is_like_space(x
, y
, twice
[cw_fourth
[grav_compat
]])) /* pushing it left */
1241 store(x
, y
, twice
[cw_fourth
[grav_compat
]], O_BLADDER
), result
= true;
1242 else if (is_like_space(x
, y
, cw_eighth
[grav_compat
])) /* maybe down, and player will move left */
1243 store(x
, y
, cw_eighth
[grav_compat
], O_BLADDER
), result
= true;
1244 else if (is_like_space(x
, y
, cw_eighth
[player_move
])) /* maybe up, and player will move left */
1245 store(x
, y
, cw_eighth
[player_move
], O_BLADDER
), result
= true;
1247 /* pushing a bladder "right". p=player, o=bladder, 1, 2, 3=directions to check. */
1251 else if (player_move
== ccw_fourth
[grav_compat
]) {
1252 if (is_like_space(x
, y
, twice
[player_move
])) /* pushing it right */
1253 store(x
, y
, twice
[player_move
], O_BLADDER
), result
= true;
1254 else if (is_like_space(x
, y
, ccw_eighth
[grav_compat
])) /* maybe down, and player will move right */
1255 store(x
, y
, ccw_eighth
[grav_compat
], O_BLADDER
), result
= true;
1256 else if (is_like_space(x
, y
, ccw_eighth
[player_move
])) /* maybe up, and player will move right */
1257 store(x
, y
, ccw_eighth
[player_move
], O_BLADDER
), result
= true;
1261 play_effect_of_element(O_BLADDER
, x
, y
);
1266 /* a box is only pushed with the fire pressed */
1268 /* but always with 100% probability */
1269 switch (player_move
) {
1274 /* pushing in some dir, two steps in that dir - is there space? */
1275 if (is_like_space(x
, y
, twice
[player_move
])) {
1277 store(x
, y
, twice
[player_move
], O_BOX
);
1279 sound_play(GD_S_BOX_PUSH
, x
, y
);
1283 /* push in no other directions possible */
1289 /* pushing of other elements not possible */
1297 /// clear these to no sound; and they will be set during iteration.
1298 void CaveRendered::clear_sounds() {
1299 sound1
= SoundWithPos(GD_S_NONE
, 0, 0);
1300 sound2
= SoundWithPos(GD_S_NONE
, 0, 0);
1301 sound3
= SoundWithPos(GD_S_NONE
, 0, 0);
1304 /// Try to make an element start falling.
1305 /// When an element starts to fall, there is no particle effect. Only when bouncing and when rolling
1306 /// down sloped things. But sound there is.
1307 /// @param x The x coordinate of the element.
1308 /// @param y The y coordinate of the element.
1309 /// @param falling_direction The direction to start "falling" to.
1310 /// Down (=gravity) for a stone, Up (=opposite of gravity) for a flying stone, for example.
1311 /// @param falling_element The falling pair of the element (O_STONE -> O_STONE_F)
1312 void CaveRendered::do_start_fall(int x
, int y
, GdDirectionEnum falling_direction
, GdElementEnum falling_element
) {
1313 if (gravity_disabled
)
1316 if (is_like_space(x
, y
, falling_direction
)) { /* beginning to fall */
1317 play_effect_of_element(get(x
, y
), x
, y
, MV_STILL
, false /* no particles */);
1318 move(x
, y
, falling_direction
, falling_element
);
1320 /* check if it is on a sloped element, and it can roll. */
1321 /* for example, sloped wall looks like: */
1324 /* this is tagged as sloped up&left. */
1325 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1326 /* then check the direction to roll (left or right) */
1327 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1328 else if (sloped(x
, y
, falling_direction
, opposite
[falling_direction
])) { /* rolling down, if sitting on a sloped object */
1329 if (sloped(x
, y
, falling_direction
, cw_fourth
[falling_direction
]) && is_like_space(x
, y
, cw_fourth
[falling_direction
]) && is_like_space(x
, y
, cw_eighth
[falling_direction
])) {
1330 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw, so here we use cw_fourth */
1331 play_effect_of_element(get(x
, y
), x
, y
, MV_STILL
, false /* no particles */);
1332 move(x
, y
, cw_fourth
[falling_direction
], falling_element
);
1333 } else if (sloped(x
, y
, falling_direction
, ccw_fourth
[falling_direction
]) && is_like_space(x
, y
, ccw_fourth
[falling_direction
]) && is_like_space(x
, y
, ccw_eighth
[falling_direction
])) {
1334 /* rolling right? */
1335 play_effect_of_element(get(x
, y
), x
, y
, MV_STILL
, false /* no particles */);
1336 move(x
, y
, ccw_fourth
[falling_direction
], falling_element
);
1341 /// When the element at (x,y) is falling in the direction fall_dir,
1342 /// check if it crushes a voodoo below it. If yes, explode the voodoo,
1343 /// and return true. Otherwise return false.
1344 /// @return true if voodoo crushed.
1345 bool CaveRendered::do_fall_try_crush_voodoo(int x
, int y
, GdDirectionEnum fall_dir
) {
1346 if (get(x
, y
, fall_dir
) == O_VOODOO
&& voodoo_dies_by_stone
) {
1347 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1348 explode(x
, y
, fall_dir
);
1354 /// When the element at (x,y) is falling in the direction fall_dir,
1355 /// check if the voodoo below it can eat it. If yes, the voodoo eats it.
1356 /// @return true if successful, false if voodoo does not eat the element.
1357 bool CaveRendered::do_fall_try_eat_voodoo(int x
, int y
, GdDirectionEnum fall_dir
) {
1358 if (get(x
, y
, fall_dir
) == O_VOODOO
&& voodoo_collects_diamonds
) {
1359 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1360 player_eat_element(O_DIAMOND
, x
, y
, fall_dir
); /* as if player got diamond */
1361 store(x
, y
, O_SPACE
); /* diamond disappears */
1367 /// Element at (x,y) is falling. Try to crack nut under it.
1368 /// If successful, nut is cracked, and the element is bounced (stops moving).
1369 /// @param fall_dir The direction the element is falling in.
1370 /// @param bouncing The element which it is converted to, if it has cracked a nut.
1371 /// @return True, if nut is cracked.
1372 bool CaveRendered::do_fall_try_crack_nut(int x
, int y
, GdDirectionEnum fall_dir
, GdElementEnum bouncing
) {
1373 if (get(x
, y
, fall_dir
) == O_NUT
|| get(x
, y
, fall_dir
) == O_NUT_F
) {
1375 store(x
, y
, bouncing
);
1376 store(x
, y
, fall_dir
, nut_turns_to_when_crushed
);
1377 sound_play(GD_S_NUT_CRACK
, x
, y
);
1383 /// For a falling element, try if a magic wall is under it.
1384 /// If yes, process element using the magic wall, and return true.
1385 /// @param fall_dir The direction the element is falling to.
1386 /// @param magic The element a magic wall turns it to.
1387 /// @return If The element is processed by the magic wall.
1388 bool CaveRendered::do_fall_try_magic(int x
, int y
, GdDirectionEnum fall_dir
, GdElementEnum magic
) {
1389 if (get(x
, y
, fall_dir
) == O_MAGIC_WALL
) {
1390 play_effect_of_element(O_DIAMOND
, x
, y
, MV_STILL
, false); /* always play diamond sound, but with no particle effect */
1391 if (magic_wall_state
== GD_MW_DORMANT
)
1392 magic_wall_state
= GD_MW_ACTIVE
;
1393 if (magic_wall_state
== GD_MW_ACTIVE
&& is_like_space(x
, y
, twice
[fall_dir
])) {
1394 /* if magic wall active and place underneath, it turns element into anything the effect says to do. */
1395 store(x
, y
, twice
[fall_dir
], magic
);
1397 store(x
, y
, O_SPACE
); /* active or non-active or anything, element falling in will always disappear */
1398 if (magic_wall_breakscan
&& amoeba_state
== GD_AM_AWAKE
)
1399 convert_amoeba_this_frame
= true;
1405 /// For a falling element, test if an explodable element is under it; if yes, explode it, and return yes.
1406 /// @return True, if element at (x,y)+fall_dir is exploded.
1407 bool CaveRendered::do_fall_try_crush(int x
, int y
, GdDirectionEnum fall_dir
) {
1408 if (explodes_by_hit(x
, y
, fall_dir
)) {
1409 explode(x
, y
, fall_dir
);
1416 * For a falling element, try if a sloped element is under it.
1417 * Move element if possible, or bounce element.
1418 * If there are two directions possible for the element to roll to, left is preferred.
1419 * If no rolling is possible, it is converted to a bouncing element.
1420 * So this always "does something" with the element, and this should be the last
1421 * function to call when checking what happens to a falling element.
1423 void CaveRendered::do_fall_roll_or_stop(int x
, int y
, GdDirectionEnum fall_dir
, GdElementEnum bouncing
) {
1424 if (is_like_space(x
, y
, fall_dir
)) { /* falling further */
1425 move(x
, y
, fall_dir
, get(x
, y
));
1428 /* check if it is on a sloped element, and it can roll. */
1429 /* for example, sloped wall looks like: */
1432 /* this is tagged as sloped up&left. */
1433 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1434 /* then check the direction to roll (left or right) */
1435 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1436 if (sloped(x
, y
, fall_dir
, opposite
[fall_dir
])) { /* sloped element, falling to left or right */
1437 if (sloped(x
, y
, fall_dir
, cw_fourth
[fall_dir
]) && is_like_space(x
, y
, cw_eighth
[fall_dir
]) && is_like_space(x
, y
, cw_fourth
[fall_dir
])) {
1438 play_effect_of_element(get(x
, y
), x
, y
);
1439 move(x
, y
, cw_fourth
[fall_dir
], get(x
, y
)); /* try to roll left first - cw_fourth is because direction is down!!! left is clockwise to that */
1440 } else if (sloped(x
, y
, fall_dir
, ccw_fourth
[fall_dir
]) && is_like_space(x
, y
, ccw_eighth
[fall_dir
]) && is_like_space(x
, y
, ccw_fourth
[fall_dir
])) {
1441 play_effect_of_element(get(x
, y
), x
, y
);
1442 move(x
, y
, ccw_fourth
[fall_dir
], get(x
, y
)); /* if not, try to roll right */
1444 /* cannot roll in any direction, so it stops */
1445 play_effect_of_element(get(x
, y
), x
, y
);
1446 store(x
, y
, bouncing
);
1451 /* any other element, stops */
1452 play_effect_of_element(get(x
, y
), x
, y
);
1453 store(x
, y
, bouncing
);
1458 /// Process a cave - one iteration.
1459 /// @param player_move The direction the player moves to.
1460 /// @param player_fire True, if the fire button is pressed.
1461 /// @param suicide True, if the suicide button is pressed.
1462 /// @return A new GdDirectionEnum, which might be changed to not have diagonal movements. This is to make stored replays neater.
1463 GdDirectionEnum
CaveRendered::iterate(GdDirectionEnum player_move
, bool player_fire
, bool suicide
) {
1464 int ymin
, ymax
; /* for border scan */
1465 bool amoeba_found_enclosed
, amoeba_2_found_enclosed
; /* amoeba found to be enclosed. if not, this is cleared */
1466 int amoeba_count
, amoeba_2_count
; /* counting the number of amoebas. after scan, check if too much */
1469 GdDirection grav_compat
; /* gravity for falling wall, bladder, ... */
1470 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1471 static GdDirection
const creature_dir
[] = { MV_LEFT
, MV_UP
, MV_RIGHT
, MV_DOWN
};
1472 static GdDirection
const creature_chdir
[] = { MV_RIGHT
, MV_DOWN
, MV_LEFT
, MV_UP
};
1473 int time_decrement_sec
;
1474 GdElement biter_try
[] = { O_DIRT
, biter_eat
, O_SPACE
, O_STONE
}; /* biters eating elements preference, they try to go in this order */
1478 if (gravity_affects_all
)
1479 grav_compat
= gravity
;
1481 grav_compat
= MV_DOWN
;
1483 /* if diagonal movements not allowed, */
1484 /* horizontal movements have precedence. [BROADRIBB] */
1485 if (!diagonal_movements
) {
1486 switch (player_move
) {
1489 player_move
= MV_RIGHT
;
1493 player_move
= MV_LEFT
;
1496 /* no correction needed */
1501 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1502 if (player_seen_ago
< 100)
1505 if (pneumatic_hammer_active_delay
> 0)
1506 --pneumatic_hammer_active_delay
;
1508 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1509 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1510 inbox_flash_toggle
= !inbox_flash_toggle
;
1511 inbox_toggle
= inbox_flash_toggle
;
1513 if (gate_open_flash
> 0)
1516 /* score collected this frame */
1519 /* to implement buggy bd1 amoeba+magic wall behaviour */
1520 convert_amoeba_this_frame
= false;
1522 /* suicide only kills the active player */
1523 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1524 /* we must check if there is a player or not - he may have exploded or something like that */
1525 if (suicide
&& player_state
== GD_PL_LIVING
&& is_player(player_x
, player_y
))
1526 store(player_x
, player_y
, O_EXPLODE_1
);
1528 /* check for walls reappearing */
1529 if (hammered_walls_reappear
) {
1530 for (int y
= 0; y
< h
; y
++)
1531 for (int x
= 0; x
< w
; x
++) {
1532 /* timer for the cell > 0? */
1533 if (hammered_reappear(x
, y
) > 0) {
1534 /* decrease timer */
1535 hammered_reappear(x
, y
)--;
1536 /* check if it became zero */
1537 if (hammered_reappear(x
, y
) == 0) {
1538 store(x
, y
, O_BRICK
);
1539 sound_play(GD_S_WALL_REAPPEAR
, x
, y
);
1545 /* variables to check during the scan */
1546 amoeba_found_enclosed
= true; /* will be set to false if any of the amoeba is found free. */
1547 amoeba_2_found_enclosed
= true;
1550 ckdelay_current
= 0;
1551 time_decrement_sec
= 0;
1553 /* check whether to scan the first and last line */
1554 if (border_scan_first_and_last
) {
1561 /* the cave scan routine */
1562 for (int y
= ymin
; y
<= ymax
; y
++)
1563 for (int x
= 0; x
< w
; x
++) {
1564 /* if we find a scanned element, change it to the normal one, and that's all. */
1565 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1566 if (is_scanned(x
, y
)) {
1571 /* add the ckdelay correction value for every element seen. */
1572 ckdelay_current
+= gd_element_properties
[get(x
, y
)].ckdelay
;
1574 switch (get(x
, y
)) {
1583 player_seen_ago
= 0;
1584 /* bd4 intermission caves have many players. so if one of them has exited,
1585 * do not change the flag anymore. so this if () is needed */
1586 if (player_state
!= GD_PL_EXITED
)
1587 player_state
= GD_PL_LIVING
;
1589 /* check for pneumatic hammer things */
1590 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right for hammer 5) stand on something */
1591 if (player_fire
&& got_pneumatic_hammer
&& is_like_space(x
, y
, player_move
)
1592 && !is_like_space(x
, y
, MV_DOWN
)) {
1593 if (player_move
== MV_LEFT
&& can_be_hammered(x
, y
, MV_DOWN_LEFT
)) {
1594 pneumatic_hammer_active_delay
= pneumatic_hammer_frame
;
1595 store(x
, y
, MV_LEFT
, O_PNEUMATIC_ACTIVE_LEFT
);
1596 store(x
, y
, O_PLAYER_PNEUMATIC_LEFT
);
1597 break; /* finished. */
1599 if (player_move
== MV_RIGHT
&& can_be_hammered(x
, y
, MV_DOWN_RIGHT
)) {
1600 pneumatic_hammer_active_delay
= pneumatic_hammer_frame
;
1601 store(x
, y
, MV_RIGHT
, O_PNEUMATIC_ACTIVE_RIGHT
);
1602 store(x
, y
, O_PLAYER_PNEUMATIC_RIGHT
);
1603 break; /* finished. */
1607 if (player_move
!= MV_STILL
) {
1608 /* only do every check if he is not moving */
1609 GdElementEnum what
= get(x
, y
, player_move
);
1610 GdElementEnum remains
= O_NONE
; /* o_none in this variable will mean that there is no change. */
1613 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1614 if (what
== O_TELEPORTER
&& do_teleporter(x
, y
, player_move
))
1617 /* try to push element; if successful, break */
1618 push
= do_push(x
, y
, player_move
, player_fire
);
1624 /* if its a bomb, remember he now has one. */
1625 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1626 sound_play(GD_S_BOMB_COLLECT
, x
, y
);
1627 store(x
, y
, player_move
, O_SPACE
);
1629 store(x
, y
, O_PLAYER_BOMB
);
1631 move(x
, y
, player_move
, O_PLAYER_BOMB
);
1634 case O_ROCKET_LAUNCHER
:
1635 /* if its a rocket launcher, remember he now has one. */
1636 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1637 sound_play(GD_S_BOMB_COLLECT
, x
, y
);
1638 store(x
, y
, player_move
, O_SPACE
);
1640 store(x
, y
, O_PLAYER_ROCKET_LAUNCHER
);
1642 move(x
, y
, player_move
, O_PLAYER_ROCKET_LAUNCHER
);
1646 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1647 if (!player_fire
&& !gravity_switch_active
&& skeletons_collected
>= skeletons_needed_for_pot
) {
1648 skeletons_collected
-= skeletons_needed_for_pot
;
1649 move(x
, y
, player_move
, O_PLAYER_STIRRING
);
1650 gravity_disabled
= true;
1655 /* get element - process others. if cannot get, player_eat_element will return the same */
1656 remains
= player_eat_element(what
, x
, y
, player_move
);
1660 /* if anything changed, apply the change. */
1661 if (remains
!= O_NONE
) {
1662 /* if snapping anything and we have snapping explosions set. but these is not true for pushing. */
1663 if (remains
== O_SPACE
&& player_fire
&& !push
)
1664 remains
= snap_element
;
1665 if (remains
!= O_SPACE
|| player_fire
)
1666 /* if any other element than space, player cannot move. also if pressing fire, will not move. */
1667 store(x
, y
, player_move
, remains
);
1669 /* if space remains there, the player moves. */
1670 move(x
, y
, player_move
, O_PLAYER
);
1677 /* much simpler; cannot snap-push stones */
1682 player_seen_ago
= 0;
1683 /* bd4 intermission caves have many players. so if one of them has exited,
1684 * do not change the flag anymore. so this if () is needed */
1685 if (player_state
!= GD_PL_EXITED
)
1686 player_state
= GD_PL_LIVING
;
1688 if (player_move
!= MV_STILL
) { /* if the player does not move, nothing to do */
1689 GdElementEnum what
= get(x
, y
, player_move
);
1690 GdElementEnum remains
= O_NONE
;
1693 /* placing a bomb into empty space or dirt */
1694 if (is_like_space(x
, y
, player_move
) || is_like_dirt(x
, y
, player_move
)) {
1695 store(x
, y
, player_move
, O_BOMB_TICK_1
);
1696 /* placed bomb, he is normal player again */
1697 store(x
, y
, O_PLAYER
);
1698 sound_play(GD_S_BOMB_PLACE
, x
, y
);
1703 /* pushing and collecting */
1704 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1705 if (what
== O_TELEPORTER
&& do_teleporter(x
, y
, player_move
))
1708 if (do_push(x
, y
, player_move
, false)) /* player fire is false... */
1711 /* get element. if cannot get, player_eat_element will return the same */
1712 remains
= player_eat_element(what
, x
, y
, player_move
);
1715 /* if something changed, OR there is space, move. */
1716 if (remains
!= O_NONE
) {
1717 /* if anything changed, apply the change. */
1718 move(x
, y
, player_move
, O_PLAYER_BOMB
);
1723 case O_PLAYER_ROCKET_LAUNCHER
:
1724 /* much simpler; cannot snap-push stones */
1729 player_seen_ago
= 0;
1730 /* bd4 intermission caves have many players. so if one of them has exited,
1731 * do not change the flag anymore. so this if () is needed */
1732 if (player_state
!= GD_PL_EXITED
)
1733 player_state
= GD_PL_LIVING
;
1735 /* firing a rocket? */
1736 if (player_move
!= MV_STILL
) { /* if the player does not move, nothing to do */
1737 GdElementEnum what
= get(x
, y
, player_move
);
1738 GdElementEnum remains
= O_NONE
;
1740 /* to fire a rocket, diagonal movement should not be allowed. so either x or y must be zero */
1741 /* placing a bomb into empty space */
1743 if (is_like_space(x
, y
, player_move
)) {
1744 switch (player_move
) {
1746 store(x
, y
, player_move
, O_ROCKET_1
);
1747 if (!infinite_rockets
)
1748 store(x
, y
, O_PLAYER
);
1751 store(x
, y
, player_move
, O_ROCKET_2
);
1752 if (!infinite_rockets
)
1753 store(x
, y
, O_PLAYER
);
1756 store(x
, y
, player_move
, O_ROCKET_3
);
1757 if (!infinite_rockets
)
1758 store(x
, y
, O_PLAYER
);
1761 store(x
, y
, player_move
, O_ROCKET_4
);
1762 if (!infinite_rockets
)
1763 store(x
, y
, O_PLAYER
);
1766 /* cannot fire in other directions */
1769 sound_play(GD_S_BOMB_PLACE
, x
, y
);
1771 /* a player with rocket launcher cannot snap elements, so stop here */
1775 /* pushing and collecting */
1776 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1777 if (what
== O_TELEPORTER
&& do_teleporter(x
, y
, player_move
))
1780 if (do_push(x
, y
, player_move
, false)) /* player fire is false... */
1783 /* get element. if cannot get, player_eat_element will return the same */
1784 remains
= player_eat_element(what
, x
, y
, player_move
);
1787 /* if something changed, OR there is space, move. */
1788 if (remains
!= O_NONE
) {
1789 /* if anything changed, apply the change. */
1790 move(x
, y
, player_move
, O_PLAYER_ROCKET_LAUNCHER
);
1795 case O_PLAYER_STIRRING
:
1800 sound_play(GD_S_STIRRING
, x
, y
); /* stirring sound */
1801 player_seen_ago
= 0;
1802 /* bd4 intermission caves have many players. so if one of them has exited,
1803 * do not change the flag anymore. so this if () is needed */
1804 if (player_state
!= GD_PL_EXITED
)
1805 player_state
= GD_PL_LIVING
;
1807 /* player "exits" stirring the pot by pressing fire */
1808 gravity_disabled
= false;
1809 store(x
, y
, O_PLAYER
);
1810 gravity_switch_active
= true;
1814 /* player holding pneumatic hammer */
1815 case O_PLAYER_PNEUMATIC_LEFT
:
1816 case O_PLAYER_PNEUMATIC_RIGHT
:
1817 /* usual player stuff */
1822 player_seen_ago
= 0;
1823 if (player_state
!= GD_PL_EXITED
)
1824 player_state
= GD_PL_LIVING
;
1825 if (pneumatic_hammer_active_delay
== 0) /* if hammering time is up, becomes a normal player again. */
1826 store(x
, y
, O_PLAYER
);
1829 /* the active pneumatic hammer itself */
1830 case O_PNEUMATIC_ACTIVE_RIGHT
:
1831 case O_PNEUMATIC_ACTIVE_LEFT
:
1832 if (pneumatic_hammer_active_delay
> 0)
1833 sound_play(GD_S_PNEUMATIC_HAMMER
, x
, y
);
1834 if (pneumatic_hammer_active_delay
== 0) {
1835 GdElementEnum new_elem
;
1837 store(x
, y
, O_SPACE
); /* pneumatic hammer element disappears */
1838 /* which is the new element which appears after that one is hammered? */
1839 new_elem
= gd_element_get_hammered(get(x
, y
, MV_DOWN
));
1840 /* if there is a new element, display it */
1841 /* O_NONE might be returned, for example if the element being hammered explodes during hammering (by a nearby explosion) */
1842 if (new_elem
!= O_NONE
) {
1843 store(x
, y
, MV_DOWN
, new_elem
);
1845 /* and if walls reappear, remember it in array */
1847 if (hammered_walls_reappear
)
1848 hammered_reappear(x
, (y
+ 1) % h
) = hammered_wall_reappear_frame
;
1855 * S T O N E S, D I A M O N D S
1857 case O_STONE
: /* standing stone */
1858 do_start_fall(x
, y
, gravity
, stone_falling_effect
);
1861 case O_MEGA_STONE
: /* standing mega_stone */
1862 do_start_fall(x
, y
, gravity
, O_MEGA_STONE_F
);
1865 case O_DIAMOND
: /* standing diamond */
1866 do_start_fall(x
, y
, gravity
, diamond_falling_effect
);
1869 case O_NUT
: /* standing nut */
1870 do_start_fall(x
, y
, gravity
, O_NUT_F
);
1873 case O_DIRT_BALL
: /* standing dirt ball */
1874 do_start_fall(x
, y
, gravity
, O_DIRT_BALL_F
);
1877 case O_DIRT_LOOSE
: /* standing loose dirt */
1878 do_start_fall(x
, y
, gravity
, O_DIRT_LOOSE_F
);
1881 case O_FLYING_STONE
: /* standing stone */
1882 do_start_fall(x
, y
, opposite
[gravity
], O_FLYING_STONE_F
);
1885 case O_FLYING_DIAMOND
: /* standing diamond */
1886 do_start_fall(x
, y
, opposite
[gravity
], O_FLYING_DIAMOND_F
);
1890 * F A L L I N G E L E M E N T S, F L Y I N G S T O N E S, D I A M O N D S
1892 case O_DIRT_BALL_F
: /* falling dirt ball */
1893 if (!gravity_disabled
)
1894 do_fall_roll_or_stop(x
, y
, gravity
, O_DIRT_BALL
);
1897 case O_DIRT_LOOSE_F
: /* falling loose dirt */
1898 if (!gravity_disabled
)
1899 do_fall_roll_or_stop(x
, y
, gravity
, O_DIRT_LOOSE
);
1902 case O_STONE_F
: /* falling stone */
1903 if (!gravity_disabled
) {
1904 if (do_fall_try_crush_voodoo(x
, y
, gravity
)) break;
1905 if (do_fall_try_crack_nut(x
, y
, gravity
, stone_bouncing_effect
)) break;
1906 if (do_fall_try_magic(x
, y
, gravity
, magic_stone_to
)) break;
1907 if (do_fall_try_crush(x
, y
, gravity
)) break;
1908 do_fall_roll_or_stop(x
, y
, gravity
, stone_bouncing_effect
);
1912 case O_MEGA_STONE_F
: /* falling mega */
1913 if (!gravity_disabled
) {
1914 if (do_fall_try_crush_voodoo(x
, y
, gravity
)) break;
1915 if (do_fall_try_crack_nut(x
, y
, gravity
, O_MEGA_STONE
)) break;
1916 if (do_fall_try_magic(x
, y
, gravity
, magic_mega_stone_to
)) break;
1917 if (do_fall_try_crush(x
, y
, gravity
)) break;
1918 do_fall_roll_or_stop(x
, y
, gravity
, O_MEGA_STONE
);
1922 case O_DIAMOND_F
: /* falling diamond */
1923 if (!gravity_disabled
) {
1924 if (do_fall_try_eat_voodoo(x
, y
, gravity
)) break;
1925 if (do_fall_try_magic(x
, y
, gravity
, magic_diamond_to
)) break;
1926 if (do_fall_try_crush(x
, y
, gravity
)) break;
1927 do_fall_roll_or_stop(x
, y
, gravity
, diamond_bouncing_effect
);
1931 case O_NUT_F
: /* falling nut */
1932 if (!gravity_disabled
) {
1933 if (do_fall_try_magic(x
, y
, gravity
, magic_nut_to
)) break;
1934 if (do_fall_try_crush(x
, y
, gravity
)) break;
1935 do_fall_roll_or_stop(x
, y
, gravity
, O_NUT
);
1939 case O_FLYING_STONE_F
: /* falling stone */
1940 if (!gravity_disabled
) {
1941 GdDirectionEnum fall_dir
= opposite
[gravity
];
1943 if (do_fall_try_crush_voodoo(x
, y
, fall_dir
)) break;
1944 if (do_fall_try_crack_nut(x
, y
, fall_dir
, O_FLYING_STONE
)) break;
1945 if (do_fall_try_magic(x
, y
, fall_dir
, magic_flying_stone_to
)) break;
1946 if (do_fall_try_crush(x
, y
, fall_dir
)) break;
1947 do_fall_roll_or_stop(x
, y
, fall_dir
, O_FLYING_STONE
);
1951 case O_FLYING_DIAMOND_F
: /* falling diamond */
1952 if (!gravity_disabled
) {
1953 GdDirectionEnum fall_dir
= opposite
[gravity
];
1955 if (do_fall_try_eat_voodoo(x
, y
, fall_dir
)) break;
1956 if (do_fall_try_magic(x
, y
, fall_dir
, magic_flying_diamond_to
)) break;
1957 if (do_fall_try_crush(x
, y
, fall_dir
)) break;
1958 do_fall_roll_or_stop(x
, y
, fall_dir
, O_FLYING_DIAMOND
);
1966 case O_NITRO_PACK
: /* standing nitro pack */
1967 do_start_fall(x
, y
, gravity
, O_NITRO_PACK_F
);
1970 case O_NITRO_PACK_F
: /* falling nitro pack */
1971 if (!gravity_disabled
) {
1972 if (is_like_space(x
, y
, gravity
)) /* if space, falling further */
1973 move(x
, y
, gravity
, O_NITRO_PACK_F
);
1974 else if (do_fall_try_magic(x
, y
, gravity
, magic_nitro_pack_to
)) {
1975 /* try magic wall; if true, function did the work */
1976 } else if (is_like_dirt(x
, y
, gravity
)) {
1977 /* falling on a dirt, it does NOT explode - just stops at its place. */
1978 store(x
, y
, O_NITRO_PACK
);
1979 play_effect_of_element(O_NITRO_PACK
, x
, y
);
1981 /* falling on any other element it explodes */
1986 case O_NITRO_PACK_EXPLODE
: /* a triggered nitro pack */
1999 /* if cannot move in any direction, becomes an enclosed cow */
2000 if (!is_like_space(x
, y
, MV_UP
) && !is_like_space(x
, y
, MV_DOWN
)
2001 && !is_like_space(x
, y
, MV_LEFT
) && !is_like_space(x
, y
, MV_RIGHT
))
2002 store(x
, y
, O_COW_ENCLOSED_1
);
2004 /* THIS IS THE CREATURE MOVE thing copied. */
2005 GdDirection
const *creature_move
;
2006 bool ccw
= rotates_ccw(x
, y
); /* check if default is counterclockwise */
2007 GdElementEnum base
; /* base element number (which is like O_***_1) */
2008 int dir
, dirn
, dirp
; /* direction */
2012 dir
= get(x
, y
) - base
; /* facing where */
2013 creature_move
= creatures_backwards
? creature_chdir
: creature_dir
;
2015 /* now change direction if backwards */
2016 if (creatures_backwards
)
2020 dirn
= (dir
+ 3) & 3; /* fast turn */
2021 dirp
= (dir
+ 1) & 3; /* slow turn */
2023 dirn
= (dir
+ 1) & 3; /* fast turn */
2024 dirp
= (dir
+ 3) & 3; /* slow turn */
2027 if (is_like_space(x
, y
, creature_move
[dirn
]))
2028 move(x
, y
, creature_move
[dirn
], (GdElementEnum
)(base
+ dirn
)); /* turn and move to preferred dir */
2029 else if (is_like_space(x
, y
, creature_move
[dir
]))
2030 move(x
, y
, creature_move
[dir
], (GdElementEnum
)(base
+ dir
)); /* go on */
2032 store(x
, y
, GdElementEnum(base
+ dirp
)); /* turn in place if nothing else possible */
2035 /* enclosed cows wait some time before turning to a skeleton */
2036 case O_COW_ENCLOSED_1
:
2037 case O_COW_ENCLOSED_2
:
2038 case O_COW_ENCLOSED_3
:
2039 case O_COW_ENCLOSED_4
:
2040 case O_COW_ENCLOSED_5
:
2041 case O_COW_ENCLOSED_6
:
2042 if (is_like_space(x
, y
, MV_UP
) || is_like_space(x
, y
, MV_LEFT
) || is_like_space(x
, y
, MV_RIGHT
) || is_like_space(x
, y
, MV_DOWN
))
2043 store(x
, y
, O_COW_1
);
2047 case O_COW_ENCLOSED_7
:
2048 if (is_like_space(x
, y
, MV_UP
) || is_like_space(x
, y
, MV_LEFT
) || is_like_space(x
, y
, MV_RIGHT
) || is_like_space(x
, y
, MV_DOWN
))
2049 store(x
, y
, O_COW_1
);
2051 store(x
, y
, O_SKELETON
);
2058 case O_ALT_FIREFLY_1
:
2059 case O_ALT_FIREFLY_2
:
2060 case O_ALT_FIREFLY_3
:
2061 case O_ALT_FIREFLY_4
:
2066 case O_ALT_BUTTER_1
:
2067 case O_ALT_BUTTER_2
:
2068 case O_ALT_BUTTER_3
:
2069 case O_ALT_BUTTER_4
:
2074 /* check if touches a voodoo */
2075 if (get(x
, y
, MV_LEFT
) == O_VOODOO
|| get(x
, y
, MV_RIGHT
) == O_VOODOO
|| get(x
, y
, MV_UP
) == O_VOODOO
|| get(x
, y
, MV_DOWN
) == O_VOODOO
)
2076 voodoo_touched
= true;
2077 /* check if touches something bad and should explode (includes voodoo by the flags) */
2078 if (blows_up_flies(x
, y
, MV_DOWN
) || blows_up_flies(x
, y
, MV_UP
)
2079 || blows_up_flies(x
, y
, MV_LEFT
) || blows_up_flies(x
, y
, MV_RIGHT
))
2081 /* otherwise move */
2083 GdDirection
const *creature_move
;
2084 bool ccw
= rotates_ccw(x
, y
); /* check if default is counterclockwise */
2085 GdElementEnum base
; /* base element number (which is like O_***_1) */
2086 int dir
, dirn
, dirp
; /* direction */
2088 if (get(x
, y
) >= O_FIREFLY_1
&& get(x
, y
) <= O_FIREFLY_4
)
2090 else if (get(x
, y
) >= O_BUTTER_1
&& get(x
, y
) <= O_BUTTER_4
)
2092 else if (get(x
, y
) >= O_STONEFLY_1
&& get(x
, y
) <= O_STONEFLY_4
)
2093 base
= O_STONEFLY_1
;
2094 else if (get(x
, y
) >= O_ALT_FIREFLY_1
&& get(x
, y
) <= O_ALT_FIREFLY_4
)
2095 base
= O_ALT_FIREFLY_1
;
2096 else if (get(x
, y
) >= O_ALT_BUTTER_1
&& get(x
, y
) <= O_ALT_BUTTER_4
)
2097 base
= O_ALT_BUTTER_1
;
2099 g_assert_not_reached();
2101 dir
= get(x
, y
) - base
; /* facing where */
2102 creature_move
= creatures_backwards
? creature_chdir
: creature_dir
;
2104 /* now change direction if backwards */
2105 if (creatures_backwards
)
2109 dirn
= (dir
+ 3) & 3; /* fast turn */
2110 dirp
= (dir
+ 1) & 3; /* slow turn */
2112 dirn
= (dir
+ 1) & 3; /* fast turn */
2113 dirp
= (dir
+ 3) & 3; /* slow turn */
2116 if (is_like_space(x
, y
, creature_move
[dirn
]))
2117 move(x
, y
, creature_move
[dirn
], (GdElementEnum
)(base
+ dirn
)); /* turn and move to preferred dir */
2118 else if (is_like_space(x
, y
, creature_move
[dir
]))
2119 move(x
, y
, creature_move
[dir
], (GdElementEnum
)(base
+ dir
)); /* go on */
2121 store(x
, y
, GdElementEnum(base
+ dirp
)); /* turn in place if nothing else possible */
2125 case O_WAITING_STONE
:
2126 if (is_like_space(x
, y
, grav_compat
)) { /* beginning to fall */
2128 move(x
, y
, grav_compat
, O_CHASING_STONE
);
2129 } else if (sloped(x
, y
, grav_compat
, opposite
[grav_compat
])) { /* rolling down a brick wall or a stone */
2130 if (sloped(x
, y
, grav_compat
, cw_fourth
[grav_compat
]) && is_like_space(x
, y
, cw_fourth
[grav_compat
]) && is_like_space(x
, y
, cw_eighth
[grav_compat
])) {
2131 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2132 move(x
, y
, cw_fourth
[grav_compat
], O_WAITING_STONE
);
2133 } else if (sloped(x
, y
, grav_compat
, ccw_fourth
[grav_compat
]) && is_like_space(x
, y
, ccw_fourth
[grav_compat
]) && is_like_space(x
, y
, ccw_eighth
[grav_compat
])) {
2134 /* or maybe right */
2135 move(x
, y
, ccw_fourth
[grav_compat
], O_WAITING_STONE
);
2140 case O_CHASING_STONE
: {
2141 int px
= player_x_mem
[0];
2142 int py
= player_y_mem
[0];
2143 bool horizontal
= random
.rand_boolean();
2144 bool dont_move
= false;
2147 /* try to move... */
2149 if (horizontal
) { /*********************************/
2150 /* check for a horizontal movement */
2152 /* if coordinates are the same */
2154 horizontal
= !horizontal
;
2158 if (px
> x
&& is_like_space(x
, y
, MV_RIGHT
)) {
2159 move(x
, y
, MV_RIGHT
, O_CHASING_STONE
);
2162 } else if (px
< x
&& is_like_space(x
, y
, MV_LEFT
)) {
2163 move(x
, y
, MV_LEFT
, O_CHASING_STONE
);
2169 horizontal
= !horizontal
;
2174 } else { /********************************/
2175 /* check for a vertical movement */
2177 /* if coordinates are the same */
2179 horizontal
= !horizontal
;
2183 if (py
> y
&& is_like_space(x
, y
, MV_DOWN
)) {
2184 move(x
, y
, MV_DOWN
, O_CHASING_STONE
);
2187 } else if (py
< y
&& is_like_space(x
, y
, MV_UP
)) {
2188 move(x
, y
, MV_UP
, O_CHASING_STONE
);
2194 horizontal
= !horizontal
;
2205 /* if we should move in both directions, but can not move in any, stop. */
2207 if (horizontal
) { /* check for horizontal */
2209 if (is_like_space(x
, y
, MV_UP
) && is_like_space(x
, y
, MV_UP_LEFT
))
2210 move(x
, y
, MV_UP
, O_CHASING_STONE
);
2211 else if (is_like_space(x
, y
, MV_DOWN
) && is_like_space(x
, y
, MV_DOWN_LEFT
))
2212 move(x
, y
, MV_DOWN
, O_CHASING_STONE
);
2214 if (is_like_space(x
, y
, MV_UP
) && is_like_space(x
, y
, MV_UP_RIGHT
))
2215 move(x
, y
, MV_UP
, O_CHASING_STONE
);
2216 else if (is_like_space(x
, y
, MV_DOWN
) && is_like_space(x
, y
, MV_DOWN_RIGHT
))
2217 move(x
, y
, MV_DOWN
, O_CHASING_STONE
);
2219 } else { /* check for vertical */
2221 if (is_like_space(x
, y
, MV_LEFT
) && is_like_space(x
, y
, MV_UP_LEFT
))
2222 move(x
, y
, MV_LEFT
, O_CHASING_STONE
);
2223 else if (is_like_space(x
, y
, MV_RIGHT
) && is_like_space(x
, y
, MV_UP_RIGHT
))
2224 move(x
, y
, MV_RIGHT
, O_CHASING_STONE
);
2226 if (is_like_space(x
, y
, MV_LEFT
) && is_like_space(x
, y
, MV_DOWN_LEFT
))
2227 move(x
, y
, MV_LEFT
, O_CHASING_STONE
);
2228 else if (is_like_space(x
, y
, MV_RIGHT
) && is_like_space(x
, y
, MV_DOWN_RIGHT
))
2229 move(x
, y
, MV_RIGHT
, O_CHASING_STONE
);
2237 if (replicators_wait_frame
== 0 && replicators_active
&& !gravity_disabled
) {
2238 /* only replicate, if space is under it. */
2239 /* do not replicate players! */
2240 /* also obeys gravity settings. */
2241 /* only replicate element if it is not a scanned one */
2242 if (is_like_space(x
, y
, gravity
) && !is_player(x
, y
, opposite
[gravity
]) && !is_scanned(x
, y
, opposite
[gravity
])) {
2243 store(x
, y
, gravity
, get(x
, y
, opposite
[gravity
]));
2244 sound_play(GD_S_REPLICATOR
, x
, y
);
2253 if (biters_wait_frame
== 0) {
2254 static GdDirectionEnum biter_move
[] = { MV_UP
, MV_RIGHT
, MV_DOWN
, MV_LEFT
};
2255 int dir
= get(x
, y
) - O_BITER_1
; /* direction, last two bits 0..3 */
2256 int dirn
= (dir
+ 3) & 3;
2257 int dirp
= (dir
+ 1) & 3;
2258 GdElementEnum made_sound_of
= O_NONE
;
2261 for (i
= 0; i
< G_N_ELEMENTS(biter_try
); i
++) {
2262 if (is_like_element(x
, y
, biter_move
[dir
], biter_try
[i
])) {
2263 GdDirectionEnum mdir
= biter_move
[dir
];
2264 play_eat_particle_of_element(get(x
, y
, mdir
), x
+ gd_dx
[mdir
], y
+ gd_dy
[mdir
]);
2265 move(x
, y
, mdir
, GdElementEnum(O_BITER_1
+ dir
));
2266 if (biter_try
[i
] != O_SPACE
)
2267 made_sound_of
= O_BITER_1
; /* sound of a biter eating */
2269 } else if (is_like_element(x
, y
, biter_move
[dirn
], biter_try
[i
])) {
2270 GdDirectionEnum mdir
= biter_move
[dirn
];
2271 play_eat_particle_of_element(get(x
, y
, mdir
), x
+ gd_dx
[mdir
], y
+ gd_dy
[mdir
]);
2272 move(x
, y
, mdir
, GdElementEnum(O_BITER_1
+ dirn
));
2273 if (biter_try
[i
] != O_SPACE
)
2274 made_sound_of
= O_BITER_1
; /* sound of a biter eating */
2276 } else if (is_like_element(x
, y
, biter_move
[dirp
], biter_try
[i
])) {
2277 GdDirectionEnum mdir
= biter_move
[dirp
];
2278 play_eat_particle_of_element(get(x
, y
, mdir
), x
+ gd_dx
[mdir
], y
+ gd_dy
[mdir
]);
2279 move(x
, y
, mdir
, GdElementEnum(O_BITER_1
+ dirp
));
2280 if (biter_try
[i
] != O_SPACE
)
2281 made_sound_of
= O_BITER_1
; /* sound of a biter eating */
2285 if (i
== G_N_ELEMENTS(biter_try
)) {
2286 /* i=number of elements in array: could not move, so just turn */
2287 store(x
, y
, GdElementEnum(O_BITER_1
+ dirp
));
2288 } else if (biter_try
[i
] == O_STONE
) {
2289 /* if there was a stone there, where we moved... do not eat stones, just throw them back */
2290 store(x
, y
, O_STONE
);
2291 made_sound_of
= O_STONE
;
2294 /* if biter did move, we had sound. play it. */
2295 if (made_sound_of
!= O_NONE
)
2296 play_effect_of_element(made_sound_of
, x
, y
);
2304 /* check if touches a voodoo */
2305 if (get(x
, y
, MV_LEFT
) == O_VOODOO
|| get(x
, y
, MV_RIGHT
) == O_VOODOO
|| get(x
, y
, MV_UP
) == O_VOODOO
|| get(x
, y
, MV_DOWN
) == O_VOODOO
)
2306 voodoo_touched
= true;
2307 /* check if touches something bad and should explode (includes voodoo by the flags) */
2308 if (blows_up_flies(x
, y
, MV_DOWN
) || blows_up_flies(x
, y
, MV_UP
)
2309 || blows_up_flies(x
, y
, MV_LEFT
) || blows_up_flies(x
, y
, MV_RIGHT
))
2311 /* otherwise move */
2313 GdDirection
const *creature_move
;
2314 bool ccw
= rotates_ccw(x
, y
); /* check if default is counterclockwise */
2315 GdElementEnum base
= O_DRAGONFLY_1
; /* base element number (which is like O_***_1) */
2316 int dir
, dirn
; /* direction */
2318 dir
= get(x
, y
) - base
; /* facing where */
2319 creature_move
= creatures_backwards
? creature_chdir
: creature_dir
;
2321 /* now change direction if backwards */
2322 if (creatures_backwards
)
2326 dirn
= (dir
+ 3) & 3; /* fast turn */
2328 dirn
= (dir
+ 1) & 3; /* fast turn */
2330 /* if can move forward, does so. */
2331 if (is_like_space(x
, y
, creature_move
[dir
]))
2332 move(x
, y
, creature_move
[dir
], GdElementEnum(base
+ dir
));
2334 /* otherwise turns 90 degrees in place. */
2335 store(x
, y
, GdElementEnum(base
+ dirn
));
2341 store(x
, y
, O_BLADDER_1
);
2352 /* bladder with any delay state: try to convert to clock. */
2353 if (is_like_element(x
, y
, opposite
[grav_compat
], bladder_converts_by
)
2354 || is_like_element(x
, y
, cw_fourth
[grav_compat
], bladder_converts_by
)
2355 || is_like_element(x
, y
, ccw_fourth
[grav_compat
], bladder_converts_by
)) {
2356 /* if touches the specified element, let it be a clock */
2357 store(x
, y
, O_PRE_CLOCK_1
);
2358 play_effect_of_element(O_PRE_CLOCK_1
, x
, y
); /* plays the bladder convert sound */
2360 /* is space over the bladder? */
2361 if (is_like_space(x
, y
, opposite
[grav_compat
])) {
2362 if (get(x
, y
) == O_BLADDER_8
) {
2363 /* if it is a bladder 8, really move up */
2364 move(x
, y
, opposite
[grav_compat
], O_BLADDER_1
);
2365 play_effect_of_element(O_BLADDER
, x
, y
);
2367 /* if smaller delay, just increase delay. */
2370 /* if not space, is something sloped over the bladder? */
2371 if (sloped_for_bladder(x
, y
, opposite
[grav_compat
]) && sloped(x
, y
, opposite
[grav_compat
], opposite
[grav_compat
])) {
2372 if (sloped(x
, y
, opposite
[grav_compat
], ccw_fourth
[opposite
[grav_compat
]])
2373 && is_like_space(x
, y
, ccw_fourth
[opposite
[grav_compat
]])
2374 && is_like_space(x
, y
, ccw_eighth
[opposite
[grav_compat
]])) {
2375 /* rolling up, to left */
2376 if (get(x
, y
) == O_BLADDER_8
) {
2377 /* if it is a bladder 8, really roll */
2378 move(x
, y
, ccw_fourth
[opposite
[grav_compat
]], O_BLADDER_8
);
2379 play_effect_of_element(O_BLADDER
, x
, y
);
2381 /* if smaller delay, just increase delay. */
2383 } else if (sloped(x
, y
, opposite
[grav_compat
], cw_fourth
[opposite
[grav_compat
]])
2384 && is_like_space(x
, y
, cw_fourth
[opposite
[grav_compat
]])
2385 && is_like_space(x
, y
, cw_eighth
[opposite
[grav_compat
]])) {
2386 /* rolling up, to left */
2387 if (get(x
, y
) == O_BLADDER_8
) {
2388 /* if it is a bladder 8, really roll */
2389 move(x
, y
, cw_fourth
[opposite
[grav_compat
]], O_BLADDER_8
);
2390 play_effect_of_element(O_BLADDER
, x
, y
);
2392 /* if smaller delay, just increase delay. */
2396 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2398 store(x
, y
, O_BLADDER_1
);
2403 if (blows_up_flies(x
, y
, MV_DOWN
) || blows_up_flies(x
, y
, MV_UP
)
2404 || blows_up_flies(x
, y
, MV_LEFT
) || blows_up_flies(x
, y
, MV_RIGHT
))
2409 /* the ghost is given four possibilities to move. */
2410 for (i
= 0; i
< 4; i
++) {
2411 static GdDirectionEnum dirs
[] = {MV_UP
, MV_DOWN
, MV_LEFT
, MV_RIGHT
};
2412 GdDirectionEnum random_dir
;
2414 random_dir
= dirs
[random
.rand_int_range(0, G_N_ELEMENTS(dirs
))];
2415 if (is_like_space(x
, y
, random_dir
)) {
2416 move(x
, y
, random_dir
, O_GHOST
);
2417 break; /* ghost did move -> exit loop */
2425 * A C T I V E E L E M E N T S
2429 if (hatched
&& amoeba_state
== GD_AM_AWAKE
)
2430 play_effect_of_element(O_AMOEBA
, x
, y
);
2431 /* emulating BD1 amoeba+magic wall bug */
2432 if (convert_amoeba_this_frame
&& amoeba_found_enclosed
) {
2433 store(x
, y
, amoeba_enclosed_effect
);
2437 switch (amoeba_state
) {
2439 store(x
, y
, amoeba_too_big_effect
);
2441 case GD_AM_ENCLOSED
:
2442 store(x
, y
, amoeba_enclosed_effect
);
2444 case GD_AM_SLEEPING
:
2446 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2447 if (amoeba_found_enclosed
)
2448 /* if still found enclosed, check all four directions, if this one is able to grow. */
2449 if (amoeba_eats(x
, y
, MV_UP
) || amoeba_eats(x
, y
, MV_DOWN
)
2450 || amoeba_eats(x
, y
, MV_LEFT
) || amoeba_eats(x
, y
, MV_RIGHT
)) {
2451 amoeba_found_enclosed
= false; /* not enclosed. this is a local (per scan) flag! */
2452 amoeba_state
= GD_AM_AWAKE
;
2455 /* if alive, check in which dir to grow (or not) */
2456 if (amoeba_state
== GD_AM_AWAKE
) {
2457 if (random
.rand_int_range(0, 1000000) < amoeba_growth_prob
) {
2458 switch (random
.rand_int_range(0, 4)) { /* decided to grow, choose a random direction. */
2459 case 0: /* let this be up. numbers indifferent. */
2460 if (amoeba_eats(x
, y
, MV_UP
))
2461 store(x
, y
, MV_UP
, O_AMOEBA
);
2464 if (amoeba_eats(x
, y
, MV_DOWN
))
2465 store(x
, y
, MV_DOWN
, O_AMOEBA
);
2468 if (amoeba_eats(x
, y
, MV_LEFT
))
2469 store(x
, y
, MV_LEFT
, O_AMOEBA
);
2472 if (amoeba_eats(x
, y
, MV_RIGHT
))
2473 store(x
, y
, MV_RIGHT
, O_AMOEBA
);
2483 if (hatched
&& amoeba_2_state
== GD_AM_AWAKE
)
2484 play_effect_of_element(O_AMOEBA
, x
, y
);
2486 /* check if it is touching an amoeba, and explosion is enabled */
2487 if (amoeba_2_explodes_by_amoeba
2488 && (is_like_element(x
, y
, MV_DOWN
, O_AMOEBA
) || is_like_element(x
, y
, MV_UP
, O_AMOEBA
)
2489 || is_like_element(x
, y
, MV_LEFT
, O_AMOEBA
) || is_like_element(x
, y
, MV_RIGHT
, O_AMOEBA
)))
2492 switch (amoeba_2_state
) {
2494 store(x
, y
, amoeba_2_too_big_effect
);
2496 case GD_AM_ENCLOSED
:
2497 store(x
, y
, amoeba_2_enclosed_effect
);
2499 case GD_AM_SLEEPING
:
2501 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2502 if (amoeba_2_found_enclosed
)
2503 if (amoeba_eats(x
, y
, MV_UP
) || amoeba_eats(x
, y
, MV_DOWN
)
2504 || amoeba_eats(x
, y
, MV_LEFT
) || amoeba_eats(x
, y
, MV_RIGHT
)) {
2505 amoeba_2_found_enclosed
= false; /* not enclosed. this is a local (per scan) flag! */
2506 amoeba_2_state
= GD_AM_AWAKE
;
2509 if (amoeba_2_state
== GD_AM_AWAKE
) /* if it is alive, decide if it attempts to grow */
2510 if (random
.rand_int_range(0, 1000000) < amoeba_2_growth_prob
) {
2511 switch (random
.rand_int_range(0, 4)) { /* decided to grow, choose a random direction. */
2512 case 0: /* let this be up. numbers indifferent. */
2513 if (amoeba_eats(x
, y
, MV_UP
))
2514 store(x
, y
, MV_UP
, O_AMOEBA_2
);
2517 if (amoeba_eats(x
, y
, MV_DOWN
))
2518 store(x
, y
, MV_DOWN
, O_AMOEBA_2
);
2521 if (amoeba_eats(x
, y
, MV_LEFT
))
2522 store(x
, y
, MV_LEFT
, O_AMOEBA_2
);
2525 if (amoeba_eats(x
, y
, MV_RIGHT
))
2526 store(x
, y
, MV_RIGHT
, O_AMOEBA_2
);
2535 /* choose randomly, if it spreads */
2536 if (random
.rand_int_range(0, 1000000) <= acid_spread_ratio
) {
2537 /* the current one explodes */
2538 store(x
, y
, acid_turns_to
);
2539 /* and if neighbours are eaten, put acid there. */
2540 if (is_like_element(x
, y
, MV_UP
, acid_eats_this
)) {
2541 store(x
, y
, MV_UP
, O_ACID
);
2542 play_effect_of_element(O_ACID
, x
, y
);
2544 if (is_like_element(x
, y
, MV_DOWN
, acid_eats_this
)) {
2545 store(x
, y
, MV_DOWN
, O_ACID
);
2546 play_effect_of_element(O_ACID
, x
, y
);
2548 if (is_like_element(x
, y
, MV_LEFT
, acid_eats_this
)) {
2549 store(x
, y
, MV_LEFT
, O_ACID
);
2550 play_effect_of_element(O_ACID
, x
, y
);
2552 if (is_like_element(x
, y
, MV_RIGHT
, acid_eats_this
)) {
2553 store(x
, y
, MV_RIGHT
, O_ACID
);
2554 play_effect_of_element(O_ACID
, x
, y
);
2560 if (!water_does_not_flow_down
&& is_like_space(x
, y
, MV_DOWN
)) /* emulating the odd behaviour in crdr */
2561 store(x
, y
, MV_DOWN
, O_WATER_1
);
2562 if (is_like_space(x
, y
, MV_UP
))
2563 store(x
, y
, MV_UP
, O_WATER_1
);
2564 if (is_like_space(x
, y
, MV_LEFT
))
2565 store(x
, y
, MV_LEFT
, O_WATER_1
);
2566 if (is_like_space(x
, y
, MV_RIGHT
))
2567 store(x
, y
, MV_RIGHT
, O_WATER_1
);
2571 store(x
, y
, O_WATER
);
2574 case O_H_EXPANDING_WALL
:
2575 case O_V_EXPANDING_WALL
:
2576 case O_H_EXPANDING_STEEL_WALL
:
2577 case O_V_EXPANDING_STEEL_WALL
:
2578 /* checks first if direction is changed. */
2579 if (((get(x
, y
) == O_H_EXPANDING_WALL
|| get(x
, y
) == O_H_EXPANDING_STEEL_WALL
) && !expanding_wall_changed
)
2580 || ((get(x
, y
) == O_V_EXPANDING_WALL
|| get(x
, y
) == O_V_EXPANDING_STEEL_WALL
) && expanding_wall_changed
)) {
2581 if (is_like_space(x
, y
, MV_LEFT
)) {
2582 store(x
, y
, MV_LEFT
, get(x
, y
));
2583 play_effect_of_element(get(x
, y
), x
, y
, MV_LEFT
);
2584 } else if (is_like_space(x
, y
, MV_RIGHT
)) {
2585 store(x
, y
, MV_RIGHT
, get(x
, y
));
2586 play_effect_of_element(get(x
, y
), x
, y
, MV_RIGHT
);
2589 if (is_like_space(x
, y
, MV_UP
)) {
2590 store(x
, y
, MV_UP
, get(x
, y
));
2591 play_effect_of_element(get(x
, y
), x
, y
, MV_UP
);
2592 } else if (is_like_space(x
, y
, MV_DOWN
)) {
2593 store(x
, y
, MV_DOWN
, get(x
, y
));
2594 play_effect_of_element(get(x
, y
), x
, y
, MV_DOWN
);
2599 case O_EXPANDING_WALL
:
2600 case O_EXPANDING_STEEL_WALL
:
2601 /* the wall which grows in all four directions. */
2602 if (is_like_space(x
, y
, MV_LEFT
)) {
2603 store(x
, y
, MV_LEFT
, get(x
, y
));
2604 play_effect_of_element(get(x
, y
), x
, y
, MV_LEFT
);
2606 if (is_like_space(x
, y
, MV_RIGHT
)) {
2607 store(x
, y
, MV_RIGHT
, get(x
, y
));
2608 play_effect_of_element(get(x
, y
), x
, y
, MV_RIGHT
);
2610 if (is_like_space(x
, y
, MV_UP
)) {
2611 store(x
, y
, MV_UP
, get(x
, y
));
2612 play_effect_of_element(get(x
, y
), x
, y
, MV_UP
);
2614 if (is_like_space(x
, y
, MV_DOWN
)) {
2615 store(x
, y
, MV_DOWN
, get(x
, y
));
2616 play_effect_of_element(get(x
, y
), x
, y
, MV_DOWN
);
2622 * unpredictable: g_rand_int
2623 * predictable: c64 predictable random generator.
2624 * for predictable, a random number is generated, whether or not it is even possible that the stone
2625 * will be able to pass.
2627 if (slime_predictable
? ((c64_rand
.random()&slime_permeability_c64
) == 0) : random
.rand_int_range(0, 1000000) < slime_permeability
) {
2628 GdDirectionEnum grav
= gravity
;
2629 GdDirectionEnum oppos
= opposite
[gravity
];
2631 /* space under the slime? elements may pass from top to bottom then. */
2632 if (is_like_space(x
, y
, grav
)) {
2633 if (get(x
, y
, oppos
) == slime_eats_1
) {
2634 store(x
, y
, grav
, slime_converts_1
); /* output a falling xy under */
2635 store(x
, y
, oppos
, O_SPACE
);
2636 play_effect_of_element(O_SLIME
, x
, y
);
2637 } else if (get(x
, y
, oppos
) == slime_eats_2
) {
2638 store(x
, y
, grav
, slime_converts_2
);
2639 store(x
, y
, oppos
, O_SPACE
);
2640 play_effect_of_element(O_SLIME
, x
, y
);
2641 } else if (get(x
, y
, oppos
) == slime_eats_3
) {
2642 store(x
, y
, grav
, slime_converts_3
);
2643 store(x
, y
, oppos
, O_SPACE
);
2644 play_effect_of_element(O_SLIME
, x
, y
);
2645 } else if (get(x
, y
, oppos
) == O_WAITING_STONE
) { /* waiting stones pass without awakening */
2646 store(x
, y
, grav
, O_WAITING_STONE
);
2647 store(x
, y
, oppos
, O_SPACE
);
2648 play_effect_of_element(O_SLIME
, x
, y
);
2649 } else if (get(x
, y
, oppos
) == O_CHASING_STONE
) { /* chasing stones pass */
2650 store(x
, y
, grav
, O_CHASING_STONE
);
2651 store(x
, y
, oppos
, O_SPACE
);
2652 play_effect_of_element(O_SLIME
, x
, y
);
2655 /* or space over the slime? elements may pass from bottom to up then. */
2656 if (is_like_space(x
, y
, oppos
)) {
2657 if (get(x
, y
, grav
) == O_BLADDER
) { /* bladders move UP the slime */
2658 store(x
, y
, grav
, O_SPACE
);
2659 store(x
, y
, oppos
, O_BLADDER_1
);
2660 play_effect_of_element(O_SLIME
, x
, y
);
2661 } else if (get(x
, y
, grav
) == O_FLYING_STONE
) {
2662 store(x
, y
, grav
, O_SPACE
);
2663 store(x
, y
, oppos
, O_FLYING_STONE_F
);
2664 play_effect_of_element(O_SLIME
, x
, y
);
2665 } else if (get(x
, y
, grav
) == O_FLYING_DIAMOND
) {
2666 store(x
, y
, grav
, O_SPACE
);
2667 store(x
, y
, oppos
, O_FLYING_DIAMOND_F
);
2668 play_effect_of_element(O_SLIME
, x
, y
);
2674 case O_FALLING_WALL
:
2675 if (is_like_space(x
, y
, grav_compat
)) {
2676 /* try falling if space under. */
2678 for (yy
= y
+ 1; yy
< y
+ h
; yy
++)
2679 /* yy<y+h is to check everything OVER the wall - since caves wrap around !! */
2680 if (!is_like_space(x
, yy
))
2681 /* stop cycle when other than space */
2683 /* if scanning stopped by a player... start falling! */
2684 if (get(x
, yy
) == O_PLAYER
|| get(x
, yy
) == O_PLAYER_GLUED
|| get(x
, yy
) == O_PLAYER_BOMB
) {
2685 move(x
, y
, grav_compat
, O_FALLING_WALL_F
);
2686 /* no sound when the falling wall starts falling! */
2691 case O_FALLING_WALL_F
:
2692 if (is_player(x
, y
, grav_compat
)) {
2693 /* if player under, it explodes - the falling wall, not the player! */
2695 } else if (is_like_space(x
, y
, grav_compat
)) {
2696 /* continue falling */
2697 move(x
, y
, grav_compat
, O_FALLING_WALL_F
);
2700 play_effect_of_element(O_FALLING_WALL_F
, x
, y
);
2701 store(x
, y
, O_FALLING_WALL
);
2706 * C O N V E Y O R B E L T S
2708 case O_CONVEYOR_RIGHT
:
2709 case O_CONVEYOR_LEFT
:
2710 /* only works if gravity is up or down!!! */
2711 /* first, check for gravity and running belts. */
2712 if (!gravity_disabled
&& conveyor_belts_active
) {
2713 GdDirection
const *dir
;
2716 /* decide direction */
2717 left
= get(x
, y
) != O_CONVEYOR_RIGHT
;
2718 if (conveyor_belts_direction_changed
)
2720 dir
= left
? ccw_eighth
: cw_eighth
;
2722 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
2723 /* if gravity is normal, and the conveyor belt has something ABOVE which can be moved
2725 the gravity is up, so anything that should float now goes DOWN and touches the conveyor */
2726 if ((gravity
== MV_DOWN
&& moved_by_conveyor_top(x
, y
, MV_UP
))
2727 || (gravity
== MV_UP
&& moved_by_conveyor_bottom(x
, y
, MV_UP
))) {
2728 if (is_like_space(x
, y
, dir
[MV_UP
])) {
2729 store(x
, y
, dir
[MV_UP
], get(x
, y
, MV_UP
)); /* move */
2730 store(x
, y
, MV_UP
, O_SPACE
); /* and place a space. */
2733 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
2734 if ((gravity
== MV_UP
&& moved_by_conveyor_top(x
, y
, MV_DOWN
))
2735 || (gravity
== MV_DOWN
&& moved_by_conveyor_bottom(x
, y
, MV_DOWN
))) {
2736 if (is_like_space(x
, y
, dir
[MV_DOWN
])) {
2737 store(x
, y
, dir
[MV_DOWN
], get(x
, y
, MV_DOWN
)); /* move */
2738 store(x
, y
, MV_DOWN
, O_SPACE
); /* and clear. */
2745 if (is_like_space(x
, y
, MV_RIGHT
)) {
2746 move(x
, y
, MV_RIGHT
, O_ROCKET_1
);
2747 add_particle_set(x
, y
, O_ROCKET_1
);
2752 if (is_like_space(x
, y
, MV_UP
)) {
2753 move(x
, y
, MV_UP
, O_ROCKET_2
);
2754 add_particle_set(x
, y
, O_ROCKET_2
);
2759 if (is_like_space(x
, y
, MV_LEFT
)) {
2760 move(x
, y
, MV_LEFT
, O_ROCKET_3
);
2761 add_particle_set(x
, y
, O_ROCKET_3
);
2766 if (is_like_space(x
, y
, MV_DOWN
)) {
2767 move(x
, y
, MV_DOWN
, O_ROCKET_4
);
2768 add_particle_set(x
, y
, O_ROCKET_4
);
2774 * S I M P L E C H A N G I N G; E X P L O S I O N S
2777 store(x
, y
, explosion_3_effect
);
2780 store(x
, y
, explosion_effect
);
2783 store(x
, y
, O_DIAMOND
);
2786 store(x
, y
, diamond_birth_effect
);
2789 store(x
, y
, O_STONE
);
2792 case O_NITRO_EXPL_4
:
2793 store(x
, y
, nitro_explosion_effect
);
2796 store(x
, y
, bomb_explosion_effect
);
2798 case O_AMOEBA_2_EXPL_4
:
2799 store(x
, y
, amoeba_2_explosion_effect
);
2802 case O_GHOST_EXPL_4
: {
2803 static GdElementEnum ghost_explode
[] = {
2804 O_SPACE
, O_SPACE
, O_DIRT
, O_DIRT
, O_CLOCK
, O_CLOCK
, O_PRE_OUTBOX
,
2805 O_BOMB
, O_BOMB
, O_PLAYER
, O_GHOST
, O_BLADDER
, O_DIAMOND
, O_SWEET
,
2806 O_WAITING_STONE
, O_BITER_1
2809 store(x
, y
, ghost_explode
[random
.rand_int_range(0, G_N_ELEMENTS(ghost_explode
))]);
2813 store(x
, y
, O_STEEL
);
2816 store(x
, y
, O_CLOCK
);
2822 case O_TRAPPED_DIAMOND
:
2823 if (diamond_key_collected
)
2824 store(x
, y
, O_DIAMOND
);
2828 if (gate_open
) /* if no more diamonds needed */
2829 store(x
, y
, O_OUTBOX
); /* open outbox */
2831 case O_PRE_INVIS_OUTBOX
:
2832 if (gate_open
) /* if no more diamonds needed */
2833 store(x
, y
, O_INVIS_OUTBOX
); /* open outbox. invisible one :P */
2836 player_seen_ago
= 0;
2837 if (hatched
&& !inbox_toggle
) /* if it is time of birth */
2838 store(x
, y
, O_PRE_PL_1
);
2839 inbox_toggle
= !inbox_toggle
;
2842 player_seen_ago
= 0;
2843 store(x
, y
, O_PRE_PL_2
);
2846 player_seen_ago
= 0;
2847 store(x
, y
, O_PRE_PL_3
);
2850 player_seen_ago
= 0;
2851 store(x
, y
, O_PLAYER
);
2876 case O_GHOST_EXPL_1
:
2877 case O_GHOST_EXPL_2
:
2878 case O_GHOST_EXPL_3
:
2881 /* explode 3 is 'effected' */
2886 case O_NITRO_EXPL_1
:
2887 case O_NITRO_EXPL_2
:
2888 case O_NITRO_EXPL_3
:
2889 case O_AMOEBA_2_EXPL_1
:
2890 case O_AMOEBA_2_EXPL_2
:
2891 case O_AMOEBA_2_EXPL_3
:
2892 /* simply the next identifier */
2910 sound_play(GD_S_WATER
, x
, y
);
2911 /* simply advance the cell the next identifier */
2915 case O_BLADDER_SPENDER
:
2916 if (is_like_space(x
, y
, opposite
[grav_compat
])) {
2917 store(x
, y
, opposite
[grav_compat
], O_BLADDER
);
2918 store(x
, y
, O_PRE_STEEL_1
);
2919 play_effect_of_element(O_BLADDER_SPENDER
, x
, y
);
2924 /* the magic wall effects are handled by the elements which fall
2925 * onto the wall. here only the sound is handled. */
2926 if (magic_wall_state
== GD_MW_ACTIVE
) {
2927 play_effect_of_element(O_MAGIC_WALL
, x
, y
);
2932 /* lava is handled by the store() routine. here only the visual effect is added. */
2933 add_particle_set(x
, y
, O_LAVA
);
2937 /* other inanimate elements that do nothing */
2941 /* after processing, check the current coordinate, if it became scanned. */
2942 /* the scanned bit can be cleared, as it will not be processed again. */
2943 /* and, it must be cleared, as it should not be scanned; for example, */
2944 /* if it is, a replicator will not replicate it! */
2948 /* POSTPROCESSING */
2950 /* forget "scanned" flags for objects. */
2951 /* also, check for time penalties. */
2952 /* these is something like an effect table, but we do not really use one. */
2953 for (int y
= 0; y
< h
; y
++)
2954 for (int x
= 0; x
< w
; x
++) {
2956 if (get(x
, y
) == O_TIME_PENALTY
) {
2957 store(x
, y
, O_GRAVESTONE
);
2958 time_decrement_sec
+= time_penalty
; /* there is time penalty for destroying the voodoo */
2962 /* another scan-like routine: */
2963 /* short explosions (for example, in bd1) started with explode_2. */
2964 /* internally we use explode_1; and change it to explode_2 if needed. */
2965 if (short_explosions
)
2966 for (int y
= 0; y
< h
; y
++)
2967 for (int x
= 0; x
< w
; x
++)
2968 if (is_first_stage_of_explosion(x
, y
)) {
2969 next(x
, y
); /* select next frame of explosion */
2970 unscan(x
, y
); /* forget scanned flag immediately */
2973 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
2974 /* but we only do this, if a living player was found. otherwise "stay" at current coordinates. */
2975 if (player_state
== GD_PL_LIVING
) {
2976 if (active_is_first_found
) {
2977 /* to be 1stb compatible, we do everything backwards. */
2978 for (int y
= h
- 1; y
>= 0; y
--)
2979 for (int x
= w
- 1; x
>= 0; x
--)
2980 if (is_player(x
, y
)) {
2981 /* here we remember the coordinates. */
2986 /* as in the original: look for the last one */
2987 for (int y
= 0; y
< h
; y
++)
2988 for (int x
= 0; x
< w
; x
++)
2989 if (is_player(x
, y
)) {
2990 /* here we remember the coordinates. */
2996 /* record coordinates of player for chasing stone */
2997 for (unsigned i
= 0; i
< PlayerMemSize
- 1; i
++) {
2998 player_x_mem
[i
] = player_x_mem
[i
+ 1];
2999 player_y_mem
[i
] = player_y_mem
[i
+ 1];
3001 player_x_mem
[PlayerMemSize
- 1] = player_x
;
3002 player_y_mem
[PlayerMemSize
- 1] = player_y
;
3005 /* updates based on the calculated explosions and per element ckdelays. */
3006 update_scheduling();
3008 /* CAVE VARIABLES */
3011 if ((player_state
== GD_PL_LIVING
&& player_seen_ago
> 15) || kill_player
) /* check if player is alive. */
3012 player_state
= GD_PL_DIED
;
3013 if (voodoo_touched
) /* check if any voodoo exploded, and kill players the next scan if that happended. */
3017 if (amoeba_state
== GD_AM_AWAKE
) {
3018 /* check flags after evaluating. */
3019 if (amoeba_count
>= amoeba_max_count
)
3020 amoeba_state
= GD_AM_TOO_BIG
;
3021 if (amoeba_found_enclosed
)
3022 amoeba_state
= GD_AM_ENCLOSED
;
3024 /* amoeba can also be turned into diamond by magic wall */
3025 if (magic_wall_stops_amoeba
&& magic_wall_state
== GD_MW_ACTIVE
)
3026 amoeba_state
= GD_AM_ENCLOSED
;
3028 if (amoeba_2_state
== GD_AM_AWAKE
) {
3029 /* check flags after evaluating. */
3030 if (amoeba_2_count
>= amoeba_2_max_count
)
3031 amoeba_2_state
= GD_AM_TOO_BIG
;
3032 if (amoeba_2_found_enclosed
)
3033 amoeba_2_state
= GD_AM_ENCLOSED
;
3035 /* amoeba 2 can also be turned into diamond by magic wall */
3036 if (magic_wall_stops_amoeba
&& magic_wall_state
== GD_MW_ACTIVE
)
3037 amoeba_2_state
= GD_AM_ENCLOSED
;
3040 /* now check times. --------------------------- */
3041 /* decrement time if a voodoo was killed. */
3042 time
-= time_decrement_sec
* timing_factor
;
3046 /* only decrement time when player is already born. */
3048 int secondsbefore
= time
/ timing_factor
;
3052 int secondsafter
= time
/ timing_factor
;
3053 if (secondsbefore
!= secondsafter
)
3054 set_seconds_sound();
3056 /* for statistics */
3057 time_elapsed
+= speed
;
3059 /* a gravity switch was activated; seconds counting down */
3060 if (gravity_will_change
> 0) {
3061 gravity_will_change
-= speed
;
3062 if (gravity_will_change
< 0)
3063 gravity_will_change
= 0;
3065 if (gravity_will_change
== 0) {
3066 gravity
= gravity_next_direction
;
3067 if (gravity_change_sound
)
3068 sound_play(GD_S_GRAVITY_CHANGE
, player_x
, player_y
); /* takes precedence over amoeba and magic wall sound */
3072 /* creatures direction automatically change */
3073 if (creatures_direction_will_change
> 0) {
3074 creatures_direction_will_change
-= speed
;
3075 if (creatures_direction_will_change
< 0)
3076 creatures_direction_will_change
= 0;
3078 if (creatures_direction_will_change
== 0) {
3079 if (creature_direction_auto_change_sound
)
3080 sound_play(GD_S_SWITCH_CREATURES
, player_x
, player_y
);
3081 creatures_backwards
= !creatures_backwards
;
3082 creatures_direction_will_change
= creatures_direction_auto_change_time
* timing_factor
;
3086 /* magic wall; if active&wait or not wait for hatching */
3087 if (magic_wall_state
== GD_MW_ACTIVE
&& (hatched
|| !magic_timer_wait_for_hatching
)) {
3088 magic_wall_time
-= speed
;
3089 if (magic_wall_time
< 0)
3090 magic_wall_time
= 0;
3091 if (magic_wall_time
== 0)
3092 magic_wall_state
= GD_MW_EXPIRED
;
3094 /* we may wait for hatching, when starting amoeba */
3095 if (amoeba_timer_started_immediately
|| (amoeba_state
== GD_AM_AWAKE
&& (hatched
|| !amoeba_timer_wait_for_hatching
))) {
3096 amoeba_time
-= speed
;
3097 if (amoeba_time
< 0)
3099 if (amoeba_time
== 0)
3100 amoeba_growth_prob
= amoeba_fast_growth_prob
;
3102 /* we may wait for hatching, when starting amoeba */
3103 if (amoeba_timer_started_immediately
|| (amoeba_2_state
== GD_AM_AWAKE
&& (hatched
|| !amoeba_timer_wait_for_hatching
))) {
3104 amoeba_2_time
-= speed
;
3105 if (amoeba_2_time
< 0)
3107 if (amoeba_2_time
== 0)
3108 amoeba_2_growth_prob
= amoeba_2_fast_growth_prob
;
3111 /* check for player hatching. */
3112 start_signal
= false;
3113 /* if not the c64 scheduling, but the correct frametime is used, hatching delay should always be decremented. */
3114 /* otherwise, the if (millisecs...) condition below will set this. */
3115 if (scheduling
== GD_SCHEDULING_MILLISECONDS
) { /* NON-C64 scheduling */
3116 if (hatching_delay_frame
> 0) {
3117 --hatching_delay_frame
; /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3118 if (hatching_delay_frame
== 0)
3119 start_signal
= true;
3121 } else { /* C64 scheduling */
3122 if (hatching_delay_time
> 0) {
3123 hatching_delay_time
-= speed
; /* for c64 schedulings, hatching delay means milliseconds. */
3124 if (hatching_delay_time
<= 0) {
3125 hatching_delay_time
= 0;
3126 start_signal
= true;
3131 /* if decremented hatching, and it became zero: */
3132 if (start_signal
) { /* THIS IS THE CAVE START SIGNAL */
3133 hatched
= true; /* record that now the cave is in its normal state */
3135 count_diamonds(); /* if diamonds needed is below zero, we count the available diamonds now. */
3137 /* setup direction auto change */
3138 if (creatures_direction_auto_change_time
) {
3139 creatures_direction_will_change
= creatures_direction_auto_change_time
* timing_factor
;
3141 if (creatures_direction_auto_change_on_start
)
3142 creatures_backwards
= !creatures_backwards
;
3145 if (player_state
== GD_PL_NOT_YET
)
3146 player_state
= GD_PL_LIVING
;
3148 sound_play(GD_S_CRACK
, player_x
, player_y
);
3152 if (biters_wait_frame
== 0)
3153 biters_wait_frame
= biter_delay_frame
;
3155 --biters_wait_frame
;
3156 /* replicators delay */
3157 if (replicators_wait_frame
== 0)
3158 replicators_wait_frame
= replicator_delay_frame
;
3160 --replicators_wait_frame
;
3165 /* check if cave failed by timeout */
3166 if (player_state
!= GD_PL_TIMEOUT
&& time
== 0) {
3167 player_state
= GD_PL_TIMEOUT
;
3169 sound_play(GD_S_TIMEOUT
, player_x
, player_y
);
3172 /* set these for drawing. */
3173 last_direction
= player_move
;
3174 /* here we remember last movements for animation. this is needed here, as animation
3175 is in sync with the game, not the keyboard directly. (for example, after exiting
3176 the cave, the player was "running" in the original, till bonus points were counted
3177 for remaining time and so on. */
3178 if (player_move
== MV_LEFT
|| player_move
== MV_UP_LEFT
|| player_move
== MV_DOWN_LEFT
)
3179 last_horizontal_direction
= MV_LEFT
;
3180 if (player_move
== MV_RIGHT
|| player_move
== MV_UP_RIGHT
|| player_move
== MV_DOWN_RIGHT
)
3181 last_horizontal_direction
= MV_RIGHT
;
3183 // return direction of movement of player, which might be changed if no diagonal movements.