2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Lava absorbs everything going into it. Everything.
24 * But it does not "pull" elements; only the things disappear which
25 * _do_ go directly into it. So if the player steps into the lava,
26 * he will die. If a dragonfly flies over it, it will not.
28 * This behavior is implemented in the is_space_dir and the store
29 * functions. is_space_dir returns true for the lava, too. The store
30 * function ignores any store requests into the lava.
31 * The player_get function will also behave for lava as it does for space.
39 #include "cavesound.h"
40 #include "caveengine.h"
46 static const GdDirection ccw_eighth
[]={ MV_STILL
, MV_UP_LEFT
, MV_UP
, MV_UP_RIGHT
, MV_RIGHT
, MV_DOWN_RIGHT
, MV_DOWN
, MV_DOWN_LEFT
};
47 static const GdDirection 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
};
48 static const GdDirection cw_eighth
[]={ MV_STILL
, MV_UP_RIGHT
, MV_RIGHT
, MV_DOWN_RIGHT
, MV_DOWN
, MV_DOWN_LEFT
, MV_LEFT
, MV_UP_LEFT
, MV_UP
};
49 static const GdDirection cw_fourth
[]={ MV_STILL
, MV_RIGHT
, MV_DOWN_RIGHT
, MV_DOWN
, MV_DOWN_LEFT
, MV_LEFT
, MV_UP_LEFT
, MV_UP
, MV_UP_RIGHT
};
51 static const GdDirection opposite
[]={ MV_STILL
, MV_DOWN
, MV_DOWN_LEFT
, MV_LEFT
, MV_UP_LEFT
, MV_UP
, MV_UP_RIGHT
, MV_RIGHT
, MV_DOWN_RIGHT
};
54 /* sets timeout sound. */
56 gd_cave_set_seconds_sound(GdCave
*cave
)
58 /* this is an integer division, so 0 seconds can be 0.5 seconds... */
59 /* also, when this reaches 8, the player still has 8.9999 seconds. so the sound is played at almost t=9s. */
60 switch(cave
->time
/cave
->timing_factor
) {
61 case 8: gd_sound_play(cave
, GD_S_TIMEOUT_1
); break;
62 case 7: gd_sound_play(cave
, GD_S_TIMEOUT_2
); break;
63 case 6: gd_sound_play(cave
, GD_S_TIMEOUT_3
); break;
64 case 5: gd_sound_play(cave
, GD_S_TIMEOUT_4
); break;
65 case 4: gd_sound_play(cave
, GD_S_TIMEOUT_5
); break;
66 case 3: gd_sound_play(cave
, GD_S_TIMEOUT_6
); break;
67 case 2: gd_sound_play(cave
, GD_S_TIMEOUT_7
); break;
68 case 1: gd_sound_play(cave
, GD_S_TIMEOUT_8
); break;
69 case 0: gd_sound_play(cave
, GD_S_TIMEOUT_9
); break;
73 /* play diamond or stone sound of given element. */
75 play_sound_of_element(GdCave
*cave
, GdElement element
)
77 /* stone and diamond fall sounds. */
82 gd_sound_play(cave
, GD_S_NUT
);
87 case O_FLYING_STONE_F
:
92 if (cave
->stone_sound
)
93 gd_sound_play(cave
, GD_S_STONE
);
98 if (cave
->nitro_sound
)
99 gd_sound_play(cave
, GD_S_NITRO
);
103 case O_FALLING_WALL_F
:
104 if (cave
->falling_wall_sound
)
105 gd_sound_play(cave
, GD_S_FALLING_WALL
);
108 case O_H_EXPANDING_WALL
:
109 case O_V_EXPANDING_WALL
:
110 case O_EXPANDING_WALL
:
111 case O_H_EXPANDING_STEEL_WALL
:
112 case O_V_EXPANDING_STEEL_WALL
:
113 case O_EXPANDING_STEEL_WALL
:
114 if (cave
->expanding_wall_sound
)
115 gd_sound_play(cave
, GD_S_EXPANDING_WALL
);
120 case O_FLYING_DIAMOND
:
121 case O_FLYING_DIAMOND_F
:
122 if (cave
->diamond_sound
)
123 gd_sound_play(cave
, GD_S_DIAMOND_RANDOM
);
126 case O_BLADDER_SPENDER
:
127 if (cave
->bladder_spender_sound
)
128 gd_sound_play(cave
, GD_S_BLADDER_SPENDER
);
132 if (cave
->bladder_convert_sound
)
133 gd_sound_play(cave
, GD_S_BLADDER_CONVERT
);
137 if (cave
->slime_sound
)
138 gd_sound_play(cave
, GD_S_SLIME
);
142 if (cave
->lava_sound
)
143 gd_sound_play(cave
, GD_S_LAVA
);
147 if (cave
->acid_spread_sound
)
148 gd_sound_play(cave
, GD_S_ACID_SPREAD
);
152 if (cave
->bladder_sound
)
153 gd_sound_play(cave
, GD_S_BLADDER_MOVE
);
160 if (cave
->biter_sound
)
161 gd_sound_play(cave
, GD_S_BITER_EAT
);
168 gd_sound_play(cave
, GD_S_DIRT_BALL
);
182 static inline GdElement
*
183 getp(const GdCave
*cave
, const int x
, const int y
)
185 return cave
->getp(cave
, x
, y
);
189 perfect (non-lineshifting) GET function. returns a pointer to a selected cave element by its coordinates.
191 static inline GdElement
*
192 getp_perfect(const GdCave
*cave
, const int x
, const int y
)
194 /* (x+n) mod n: this wors also for x>=n and -n+1<x<0 */
195 return &(cave
->map
[(y
+cave
->h
)%cave
->h
][(x
+cave
->w
)%cave
->w
]);
199 line shifting GET function; returns a pointer to the selected cave element.
200 this is used to emulate the line-shifting behaviour of original games, so that
201 the player entering one side will appear one row above or below on the other.
203 static inline GdElement
*
204 getp_shift(const GdCave
*cave
, int x
, int y
)
214 y
=(y
+cave
->h
)%cave
->h
;
215 return &(cave
->map
[y
][x
]);
219 static inline GdElement
220 get(const GdCave
*cave
, const int x
, const int y
)
222 return *getp(cave
, x
, y
);
225 /* returns an element which is somewhere near x,y */
226 static inline GdElement
227 get_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
229 return get(cave
, x
+gd_dx
[dir
], y
+gd_dy
[dir
]);
236 static inline gboolean
237 explodes_by_hit_dir(const GdCave
*cave
, const int x
, const int y
, GdDirection dir
)
239 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_EXPLODES_BY_HIT
)!=0;
242 /* returns true if the element is not explodable, for example the steel wall */
243 static inline gboolean
244 non_explodable(const GdCave
*cave
, const int x
, const int y
)
246 return (gd_elements
[get(cave
, x
,y
)&O_MASK
].properties
&P_NON_EXPLODABLE
)!=0;
249 /* returns true if the element can be eaten by the amoeba, eg. space and dirt. */
250 static inline gboolean
251 amoeba_eats_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
253 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_AMOEBA_CONSUMES
)!=0;
256 /* returns true if the element is sloped, so stones and diamonds roll down on it. for example a stone or brick wall */
257 static inline gboolean
258 sloped_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
, const GdDirection slop
)
262 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_SLOPED_LEFT
)!=0;
264 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_SLOPED_RIGHT
)!=0;
266 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_SLOPED_UP
)!=0;
268 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_SLOPED_DOWN
)!=0;
276 /* returns true if the element is sloped for bladder movement (brick=yes, diamond=no, for example) */
277 static inline gboolean
278 sloped_for_bladder_dir (const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
280 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_BLADDER_SLOPED
)!=0;
283 static inline gboolean
284 blows_up_flies_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
286 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_BLOWS_UP_FLIES
)!=0;
289 /* returns true if the element is a counter-clockwise creature */
290 static inline gboolean
291 rotates_ccw (const GdCave
*cave
, const int x
, const int y
)
293 return (gd_elements
[get(cave
, x
, y
)&O_MASK
].properties
&P_CCW
)!=0;
296 /* returns true if the element is a player */
297 static inline gboolean
298 is_player(const GdCave
*cave
, const int x
, const int y
)
300 return (gd_elements
[get(cave
, x
, y
)&O_MASK
].properties
&P_PLAYER
)!=0;
303 /* returns true if the element is a player */
304 static inline gboolean
305 is_player_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
307 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_PLAYER
)!=0;
310 static inline gboolean
311 can_be_hammered_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
313 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_CAN_BE_HAMMERED
)!=0;
317 /* returns true if the element is explodable and explodes to space, for example the player */
318 static inline gboolean
319 is_first_stage_of_explosion(const GdCave
*cave
, const int x
, const int y
)
321 return (gd_elements
[get(cave
, x
, y
)&O_MASK
].properties
&P_EXPLOSION_FIRST_STAGE
)!=0;
324 /* returns true if the element is moved by the conveyor belt */
325 static inline gboolean
326 moved_by_conveyor_top_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
328 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_MOVED_BY_CONVEYOR_TOP
)!=0;
331 /* returns true if the element is moved by the conveyor belt */
332 static inline gboolean
333 moved_by_conveyor_bottom_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
335 return (gd_elements
[get_dir(cave
, x
, y
, dir
)&O_MASK
].properties
&P_MOVED_BY_CONVEYOR_BOTTOM
)!=0;
338 static inline gboolean
339 is_scanned_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
341 return (get_dir(cave
, x
, y
, dir
)&SCANNED
) != 0;
349 /* returns true if neighbouring element is "e" */
350 /* treats dirt specially */
351 /* treats lava specially */
352 static inline gboolean
353 is_element_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
, GdElement e
)
355 GdElement examined
=get_dir(cave
, x
, y
, dir
);
357 /* if it is a dirt-like, change to dirt, so equality will evaluate to true */
358 if (gd_elements
[examined
& O_MASK
].properties
& P_DIRT
)
360 if (gd_elements
[e
& O_MASK
].properties
& P_DIRT
)
362 /* if the element on the map is a lava, it should be like space */
363 if (examined
==O_LAVA
)
368 /* returns true if neighbouring element is space */
369 static inline gboolean
370 is_space_dir(const GdCave
*cave
, const int x
, const int y
, const GdDirection dir
)
372 GdElement e
=get_dir(cave
, x
, y
, dir
)&O_MASK
;
374 return e
==O_SPACE
|| e
==O_LAVA
;
380 /* store an element at the given position */
382 store(GdCave
*cave
, const int x
, const int y
, const GdElement element
)
384 GdElement
*e
=getp(cave
, x
, y
);
387 play_sound_of_element(cave
, O_LAVA
);
393 /* store an element with SCANNED flag turned on */
395 store_sc(GdCave
*cave
, const int x
, const int y
, const GdElement element
)
397 store(cave
, x
, y
, element
|SCANNED
);
400 /* store an element to a neighbouring cell */
402 store_dir(GdCave
*cave
, const int x
, const int y
, const GdDirection dir
, const GdElement element
)
404 store(cave
, x
+gd_dx
[dir
], y
+gd_dy
[dir
], element
|SCANNED
);
407 /* store an element to a neighbouring cell */
409 store_dir_no_scanned(GdCave
*cave
, const int x
, const int y
, const GdDirection dir
, const GdElement element
)
411 store(cave
, x
+gd_dx
[dir
], y
+gd_dy
[dir
], element
);
414 /* move element to direction; then place space at x, y */
416 move(GdCave
*cave
, const int x
, const int y
, const GdDirection dir
, const GdElement e
)
418 store_dir(cave
, x
, y
, dir
, e
);
419 store(cave
, x
, y
, O_SPACE
);
422 /* increment a cave element; can be used for elements which are one after the other, for example bladder1, bladder2, bladder3... */
424 next(GdCave
*cave
, const int x
, const int y
)
426 (*getp(cave
, x
, y
))++;
445 cell_explode(GdCave
*cave
, int x
, int y
, GdElement explode_to
)
447 if (non_explodable (cave
, x
, y
))
450 if (cave
->voodoo_any_hurt_kills_player
&& get(cave
, x
, y
)==O_VOODOO
)
451 cave
->voodoo_touched
=TRUE
;
453 if (get(cave
, x
, y
)==O_VOODOO
&& !cave
->voodoo_disappear_in_explosion
)
454 /* voodoo turns into a time penalty */
455 store_sc(cave
, x
, y
, O_TIME_PENALTY
);
456 else if (get(cave
, x
, y
)==O_NITRO_PACK
|| get(cave
, x
, y
)==O_NITRO_PACK_F
)
457 /* nitro pack inside an explosion - it is now triggered */
458 store_sc(cave
, x
, y
, O_NITRO_PACK_EXPLODE
);
460 /* for everything else */
461 store_sc(cave
, x
, y
, explode_to
);
464 /* a creature explodes to a 3x3 something. */
466 creature_explode(GdCave
*cave
, int x
, int y
, GdElement explode_to
)
470 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
472 gd_sound_play(cave
, GD_S_EXPLOSION
);
474 for (yy
=y
-1; yy
<=y
+1; yy
++)
475 for (xx
=x
-1; xx
<=x
+1; xx
++)
476 cell_explode(cave
, xx
, yy
, explode_to
);
480 nitro_explode(GdCave
*cave
, int x
, int y
)
484 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
486 gd_sound_play(cave
, GD_S_NITRO_EXPLOSION
);
488 for (yy
=y
-1; yy
<=y
+1; yy
++)
489 for (xx
=x
-1; xx
<=x
+1; xx
++)
490 cell_explode(cave
, xx
, yy
, O_NITRO_EXPL_1
);
491 /* the current cell is explicitly changed into a nitro expl, as cell_explode changes it to a triggered nitro pack */
492 store_sc(cave
, x
, y
, O_NITRO_EXPL_1
);
495 /* a voodoo explodes, leaving a 3x3 steel and a time penalty behind. */
497 voodoo_explode(GdCave
*cave
, int x
, int y
)
501 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
504 gd_sound_play(cave
, GD_S_VOODOO_EXPLOSION
);
505 if (cave
->voodoo_any_hurt_kills_player
)
506 cave
->voodoo_touched
=TRUE
;
508 /* voodoo explodes to 3x3 steel */
509 for (yy
=y
-1; yy
<=y
+1; yy
++)
510 for (xx
=x
-1; xx
<=x
+1; xx
++)
511 store_sc(cave
, xx
, yy
, O_PRE_STEEL_1
);
512 /* middle is a time penalty (which will be turned into a gravestone) */
513 store_sc(cave
, x
, y
, O_TIME_PENALTY
);
516 /* a bomb does not explode the voodoo, neither does the ghost.
517 this function check this, and stores the new element or not.
518 destroying the voodoo is also controlled by the voodoo_disappear_in_explosion flag. */
520 explode_try_skip_voodoo(GdCave
*cave
, const int x
, const int y
, const GdElement expl
)
522 if (non_explodable (cave
, x
, y
))
524 /* bomb does not explode voodoo */
525 if (!cave
->voodoo_disappear_in_explosion
&& get(cave
, x
, y
)==O_VOODOO
)
527 if (cave
->voodoo_any_hurt_kills_player
&& get(cave
, x
, y
)==O_VOODOO
)
528 cave
->voodoo_touched
=TRUE
;
529 store_sc (cave
, x
, y
, expl
);
532 /* X shaped ghost explosion; does not touch voodoo! */
534 ghost_explode(GdCave
*cave
, const int x
, const int y
)
536 gd_sound_play(cave
, GD_S_GHOST_EXPLOSION
);
538 /* the processing of an explosion took pretty much time: processing 5 elements */
541 explode_try_skip_voodoo(cave
, x
, y
, O_GHOST_EXPL_1
);
542 explode_try_skip_voodoo(cave
, x
-1, y
-1, O_GHOST_EXPL_1
);
543 explode_try_skip_voodoo(cave
, x
+1, y
+1, O_GHOST_EXPL_1
);
544 explode_try_skip_voodoo(cave
, x
-1, y
+1, O_GHOST_EXPL_1
);
545 explode_try_skip_voodoo(cave
, x
+1, y
-1, O_GHOST_EXPL_1
);
548 /*+shaped bomb explosion; does not touch voodoo! */
550 bomb_explode(GdCave
*cave
, const int x
, const int y
)
552 gd_sound_play(cave
, GD_S_BOMB_EXPLOSION
);
554 /* the processing of an explosion took pretty much time: processing 5 elements */
557 explode_try_skip_voodoo(cave
, x
, y
, O_BOMB_EXPL_1
);
558 explode_try_skip_voodoo(cave
, x
-1, y
, O_BOMB_EXPL_1
);
559 explode_try_skip_voodoo(cave
, x
+1, y
, O_BOMB_EXPL_1
);
560 explode_try_skip_voodoo(cave
, x
, y
+1, O_BOMB_EXPL_1
);
561 explode_try_skip_voodoo(cave
, x
, y
-1, O_BOMB_EXPL_1
);
565 explode an element with the appropriate type of exlposion.
568 explode(GdCave
*cave
, int x
, int y
)
570 GdElement e
=get(cave
, x
, y
)&O_MASK
;
574 ghost_explode(cave
, x
, y
);
578 bomb_explode(cave
, x
, y
);
582 voodoo_explode(cave
, x
, y
);
587 case O_NITRO_PACK_EXPLODE
:
588 nitro_explode(cave
, x
, y
);
592 creature_explode(cave
, x
, y
, O_AMOEBA_2_EXPL_1
);
595 case O_FALLING_WALL_F
:
596 creature_explode(cave
, x
, y
, O_EXPLODE_1
);
603 creature_explode(cave
, x
, y
, cave
->butterfly_explode_to
);
610 creature_explode(cave
, x
, y
, cave
->alt_butterfly_explode_to
);
617 creature_explode(cave
, x
, y
, cave
->firefly_explode_to
);
620 case O_ALT_FIREFLY_1
:
621 case O_ALT_FIREFLY_2
:
622 case O_ALT_FIREFLY_3
:
623 case O_ALT_FIREFLY_4
:
624 creature_explode(cave
, x
, y
, cave
->alt_firefly_explode_to
);
630 case O_PLAYER_STIRRING
:
631 case O_PLAYER_PNEUMATIC_LEFT
:
632 case O_PLAYER_PNEUMATIC_RIGHT
:
633 creature_explode(cave
, x
, y
, O_EXPLODE_1
);
640 creature_explode(cave
, x
, y
, cave
->stonefly_explode_to
);
647 creature_explode(cave
, x
, y
, cave
->dragonfly_explode_to
);
651 g_assert_not_reached();
657 explode_dir(GdCave
*cave
, const int x
, const int y
, GdDirection dir
)
659 explode(cave
, x
+gd_dx
[dir
], y
+gd_dy
[dir
]);
664 player eats specified object.
665 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
666 returns other element if something other appears there and he can't move.
667 cave pointer is needed to know the diamond values.
670 player_get_element (GdCave
* cave
, const GdElement object
)
676 cave
->diamond_key_collected
=TRUE
;
677 gd_sound_play(cave
, GD_S_DIAMOND_KEY_COLLECT
);
682 gd_sound_play(cave
, GD_S_KEY_COLLECT
);
686 gd_sound_play(cave
, GD_S_KEY_COLLECT
);
690 gd_sound_play(cave
, GD_S_KEY_COLLECT
);
696 gd_sound_play(cave
, GD_S_DOOR_OPEN
);
702 gd_sound_play(cave
, GD_S_DOOR_OPEN
);
708 gd_sound_play(cave
, GD_S_DOOR_OPEN
);
713 case O_CREATURE_SWITCH
: /* creatures change direction. */
714 gd_sound_play(cave
, GD_S_SWITCH_CREATURES
);
715 cave
->creatures_backwards
=!cave
->creatures_backwards
;
717 case O_EXPANDING_WALL_SWITCH
: /* expanding wall change direction. */
718 gd_sound_play(cave
, GD_S_SWITCH_EXPANDING
);
719 cave
->expanding_wall_changed
=!cave
->expanding_wall_changed
;
721 case O_BITER_SWITCH
: /* biter change delay */
722 gd_sound_play(cave
, GD_S_SWITCH_BITER
);
723 cave
->biter_delay_frame
++;
724 if (cave
->biter_delay_frame
==4)
725 cave
->biter_delay_frame
=0;
727 case O_REPLICATOR_SWITCH
: /* replicator on/off switch */
728 gd_sound_play(cave
, GD_S_SWITCH_REPLICATOR
);
729 cave
->replicators_active
=!cave
->replicators_active
;
731 case O_CONVEYOR_SWITCH
: /* conveyor belts on/off */
732 gd_sound_play(cave
, GD_S_SWITCH_CONVEYOR
);
733 cave
->conveyor_belts_active
=!cave
->conveyor_belts_active
;
735 case O_CONVEYOR_DIR_SWITCH
: /* conveyor belts switch direction */
736 gd_sound_play(cave
, GD_S_SWITCH_CONVEYOR
);
737 cave
->conveyor_belts_direction_changed
=!cave
->conveyor_belts_direction_changed
;
743 case O_STEEL_EATABLE
:
744 case O_BRICK_EATABLE
:
745 case O_DIRT_SLOPED_UP_RIGHT
:
746 case O_DIRT_SLOPED_UP_LEFT
:
747 case O_DIRT_SLOPED_DOWN_LEFT
:
748 case O_DIRT_SLOPED_DOWN_RIGHT
:
751 gd_sound_play(cave
, GD_S_WALK_EARTH
);
755 gd_sound_play(cave
, GD_S_SWEET_COLLECT
);
756 cave
->sweet_eaten
=TRUE
;
759 case O_PNEUMATIC_HAMMER
:
760 gd_sound_play(cave
, GD_S_PNEUMATIC_COLLECT
);
761 cave
->got_pneumatic_hammer
=TRUE
;
766 gd_sound_play(cave
, GD_S_CLOCK_COLLECT
);
767 cave
->time
+=cave
->time_bonus
*cave
->timing_factor
;
768 if (cave
->time
>cave
->max_time
*cave
->timing_factor
)
769 cave
->time
-=cave
->max_time
*cave
->timing_factor
;
770 /* no space, rather a dirt remains there... */
773 case O_FLYING_DIAMOND
:
774 gd_sound_play(cave
, GD_S_DIAMOND_COLLECT
);
775 cave
->score
+=cave
->diamond_value
;
776 cave
->diamonds_collected
++;
777 if (cave
->diamonds_needed
==cave
->diamonds_collected
) {
778 cave
->gate_open
=TRUE
;
779 cave
->diamond_value
=cave
->extra_diamond_value
; /* extra is worth more points. */
780 cave
->gate_open_flash
=1;
781 cave
->sound3
=GD_S_CRACK
;
785 cave
->skeletons_collected
++;
786 for (i
=0; i
<cave
->skeletons_worth_diamonds
; i
++)
787 player_get_element(cave
, O_DIAMOND
); /* as if player got a diamond */
788 gd_sound_play(cave
, GD_S_SKELETON_COLLECT
); /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
792 cave
->player_state
=GD_PL_EXITED
; /* player now exits the cave! */
795 case O_LAVA
: /* player goes into lava, as if it was space */
796 gd_sound_play(cave
, GD_S_WALK_EMPTY
);
800 /* the object will remain there. */
806 process a crazy dream-style teleporter.
807 called from gd_cave_iterate, for a player or a player_bomb.
808 player is standing at px, py, and trying to move in the direction player_move,
809 where there is a teleporter.
810 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
811 around). the first teleporter we find, and which is suitable, will be the destination.
812 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
815 do_teleporter(GdCave
*cave
, int px
, int py
, GdDirection player_move
)
823 /* jump to next element; wrap around columns and rows. */
831 /* if we found a teleporter... */
832 if (get(cave
, tx
, ty
)==O_TELEPORTER
&& is_space_dir(cave
, tx
, ty
, player_move
)) {
833 store_dir(cave
, tx
, ty
, player_move
, get(cave
, px
, py
)); /* new player appears near teleporter found */
834 store(cave
, px
, py
, O_SPACE
); /* current player disappears */
835 gd_sound_play(cave
, GD_S_TELEPORTER
);
836 return TRUE
; /* return true as teleporter worked */
838 } while (tx
!=px
|| ty
!=py
); /* loop until we get back to original coordinates */
839 return FALSE
; /* return false as we did not find any usable teleporter */
843 try to push an element.
844 returns true if the push is possible; also does move the specified _element_.
845 up to the caller to move the _player_itself_.
849 do_push(GdCave
*cave
, int x
, int y
, GdDirection player_move
, gboolean player_fire
)
852 GdElement what
=get_dir(cave
, x
, y
, player_move
);
853 GdDirection grav_compat
=cave
->gravity_affects_all
?cave
->gravity
:MV_DOWN
; /* gravity for falling wall, bladder, ... */
858 case O_WAITING_STONE
:
861 case O_CHASING_STONE
:
865 /* pushing some kind of stone or nut */
866 /* directions possible: 90degrees cw or ccw to current gravity. */
867 /* only push if player dir is orthogonal to gravity, ie. gravity down, pushing left&right possible */
868 if (player_move
==ccw_fourth
[cave
->gravity
] || player_move
==cw_fourth
[cave
->gravity
]) {
872 /* different probabilities for different elements. */
874 case O_WAITING_STONE
:
875 prob
=1000000; /* waiting stones are light, can always push */
877 case O_CHASING_STONE
:
878 if (cave
->sweet_eaten
) /* chasing can be pushed if player is turbo */
882 if (cave
->mega_stones_pushable_with_sweet
&& cave
->sweet_eaten
) /* mega may(!) be pushed if player is turbo */
889 if (cave
->sweet_eaten
)
890 prob
=cave
->pushing_stone_prob_sweet
; /* probability with sweet */
892 prob
=cave
->pushing_stone_prob
; /* probability without sweet. */
895 g_assert_not_reached();
899 if (is_space_dir(cave
, x
, y
, MV_TWICE
+player_move
) && g_rand_int_range(cave
->random
, 0, 1000000)<prob
) {
900 /* if decided that he will be able to push, */
901 store_dir(cave
, x
, y
, MV_TWICE
+player_move
, what
);
902 play_sound_of_element(cave
, what
);
917 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
918 * not an O_BLADDER_x. */
919 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
921 /* first check: we cannot push a bladder "up" */
922 if (player_move
!=opposite
[grav_compat
]) {
923 /* pushing a bladder "down". p=player, o=bladder, 1, 2, 3=directions to check. */
924 /* player moving in the direction of gravity. */
928 if (player_move
==grav_compat
) {
929 if (is_space_dir(cave
, x
, y
, MV_TWICE
+player_move
)) /* pushing bladder down */
930 store_dir_no_scanned(cave
, x
, y
, MV_TWICE
+player_move
, O_BLADDER
), result
=TRUE
;
931 else if (is_space_dir(cave
, x
, y
, cw_eighth
[grav_compat
])) /* if no space to push down, maybe left (down-left to player) */
932 /* left is "down, turned right (cw)" */
933 store_dir_no_scanned(cave
, x
, y
, cw_eighth
[grav_compat
], O_BLADDER
), result
=TRUE
;
934 else if (is_space_dir(cave
, x
, y
, ccw_eighth
[grav_compat
])) /* if not, maybe right (down-right to player) */
935 store_dir_no_scanned(cave
, x
, y
, ccw_eighth
[grav_compat
], O_BLADDER
), result
=TRUE
;
937 /* pushing a bladder "left". p=player, o=bladder, 1, 2, 3=directions to check. */
941 else if (player_move
==cw_fourth
[grav_compat
]) {
942 if (is_space_dir(cave
, x
, y
, MV_TWICE
+cw_fourth
[grav_compat
])) /* pushing it left */
943 store_dir_no_scanned(cave
, x
, y
, MV_TWICE
+cw_fourth
[grav_compat
], O_BLADDER
), result
=TRUE
;
944 else if (is_space_dir(cave
, x
, y
, cw_eighth
[grav_compat
])) /* maybe down, and player will move left */
945 store_dir_no_scanned(cave
, x
, y
, cw_eighth
[grav_compat
], O_BLADDER
), result
=TRUE
;
946 else if (is_space_dir(cave
, x
, y
, cw_eighth
[player_move
])) /* maybe up, and player will move left */
947 store_dir_no_scanned(cave
, x
, y
, cw_eighth
[player_move
], O_BLADDER
), result
=TRUE
;
949 /* pushing a bladder "right". p=player, o=bladder, 1, 2, 3=directions to check. */
953 else if (player_move
==ccw_fourth
[grav_compat
]) {
954 if (is_space_dir(cave
, x
, y
, MV_TWICE
+player_move
)) /* pushing it right */
955 store_dir_no_scanned(cave
, x
, y
, MV_TWICE
+player_move
, O_BLADDER
), result
=TRUE
;
956 else if (is_space_dir(cave
, x
, y
, ccw_eighth
[grav_compat
])) /* maybe down, and player will move right */
957 store_dir_no_scanned(cave
, x
, y
, ccw_eighth
[grav_compat
], O_BLADDER
), result
=TRUE
;
958 else if (is_space_dir(cave
, x
, y
, ccw_eighth
[player_move
])) /* maybe up, and player will move right */
959 store_dir_no_scanned(cave
, x
, y
, ccw_eighth
[player_move
], O_BLADDER
), result
=TRUE
;
963 play_sound_of_element(cave
, O_BLADDER
);
968 /* a box is only pushed with the fire pressed */
970 /* but always with 100% probability */
971 switch (player_move
) {
976 /* pushing in some dir, two steps in that dir - is there space? */
977 if (is_space_dir(cave
, x
, y
, player_move
+MV_TWICE
)) {
979 store_dir(cave
, x
, y
, player_move
+MV_TWICE
, O_BOX
);
981 gd_sound_play(cave
, GD_S_BOX_PUSH
);
985 /* push in no other directions possible */
991 /* pushing of other elements not possible */
999 /* from the key press booleans, create a direction */
1001 gd_direction_from_keypress(gboolean up
, gboolean down
, gboolean left
, gboolean right
)
1003 GdDirection player_move
;
1005 /* from the key press booleans, create a direction */
1007 player_move
=MV_UP_RIGHT
;
1008 else if (down
&& right
)
1009 player_move
=MV_DOWN_RIGHT
;
1010 else if (down
&& left
)
1011 player_move
=MV_DOWN_LEFT
;
1012 else if (up
&& left
)
1013 player_move
=MV_UP_LEFT
;
1017 player_move
=MV_DOWN
;
1019 player_move
=MV_LEFT
;
1021 player_move
=MV_RIGHT
;
1023 player_move
=MV_STILL
;
1029 /* clear these to no sound; and they will be set during iteration. */
1031 gd_cave_clear_sounds(GdCave
*cave
)
1033 cave
->sound1
=GD_S_NONE
;
1034 cave
->sound2
=GD_S_NONE
;
1035 cave
->sound3
=GD_S_NONE
;
1041 do_start_fall(GdCave
*cave
, int x
, int y
, GdDirection falling_direction
, GdElement falling_element
)
1043 if (cave
->gravity_disabled
)
1046 if (is_space_dir(cave
, x
, y
, falling_direction
)) { /* beginning to fall */
1047 play_sound_of_element(cave
, get(cave
, x
, y
));
1048 move(cave
, x
, y
, falling_direction
, falling_element
);
1050 /* check if it is on a sloped element, and it can roll. */
1051 /* for example, sloped wall looks like: */
1054 /* this is tagged as sloped up&left. */
1055 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1056 /* then check the direction to roll (left or right) */
1057 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1058 else if (sloped_dir(cave
, x
, y
, falling_direction
, opposite
[falling_direction
])) { /* rolling down, if sitting on a sloped object */
1059 if (sloped_dir(cave
, x
, y
, falling_direction
, cw_fourth
[falling_direction
]) && is_space_dir(cave
, x
, y
, cw_fourth
[falling_direction
]) && is_space_dir(cave
, x
, y
, cw_eighth
[falling_direction
])) {
1060 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw, so here we use cw_fourth */
1061 play_sound_of_element(cave
, get(cave
, x
, y
));
1062 move(cave
, x
, y
, cw_fourth
[falling_direction
], falling_element
);
1064 else if (sloped_dir(cave
, x
, y
, falling_direction
, ccw_fourth
[falling_direction
]) && is_space_dir(cave
, x
, y
, ccw_fourth
[falling_direction
]) && is_space_dir(cave
, x
, y
, ccw_eighth
[falling_direction
])) {
1065 /* rolling right? */
1066 play_sound_of_element(cave
, get(cave
, x
, y
));
1067 move(cave
, x
, y
, ccw_fourth
[falling_direction
], falling_element
);
1073 do_fall_try_crush_voodoo(GdCave
*cave
, int x
, int y
, GdDirection fall_dir
)
1075 if (get_dir(cave
, x
, y
, fall_dir
)==O_VOODOO
&& cave
->voodoo_dies_by_stone
) {
1076 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1077 explode_dir(cave
, x
, y
, fall_dir
);
1085 do_fall_try_eat_voodoo(GdCave
*cave
, int x
, int y
, GdDirection fall_dir
)
1087 if (get_dir(cave
, x
, y
, fall_dir
)==O_VOODOO
&& cave
->voodoo_collects_diamonds
) {
1088 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1089 player_get_element(cave
, O_DIAMOND
); /* as if player got diamond */
1090 store(cave
, x
, y
, O_SPACE
); /* diamond disappears */
1099 do_fall_try_crack_nut(GdCave
*cave
, int x
, int y
, GdDirection fall_dir
, GdElement bouncing
)
1101 if (get_dir(cave
, x
, y
, fall_dir
)==O_NUT
|| get_dir(cave
, x
, y
, fall_dir
)==O_NUT_F
) {
1103 store(cave
, x
, y
, bouncing
);
1104 store_dir(cave
, x
, y
, fall_dir
, cave
->nut_turns_to_when_crushed
);
1105 if (cave
->nut_sound
)
1106 gd_sound_play(cave
, GD_S_NUT_CRACK
);
1114 do_fall_try_magic(GdCave
*cave
, int x
, int y
, GdDirection fall_dir
, GdElement magic
)
1116 if (get_dir(cave
, x
, y
, fall_dir
)==O_MAGIC_WALL
) {
1117 play_sound_of_element(cave
, O_DIAMOND
); /* always play diamond sound */
1118 if (cave
->magic_wall_state
==GD_MW_DORMANT
)
1119 cave
->magic_wall_state
=GD_MW_ACTIVE
;
1120 if (cave
->magic_wall_state
==GD_MW_ACTIVE
&& is_space_dir(cave
, x
, y
, MV_TWICE
+fall_dir
)) {
1121 /* if magic wall active and place underneath, it turns element into anything the effect says to do. */
1122 store_dir(cave
, x
, y
, MV_TWICE
+fall_dir
, magic
);
1124 store(cave
, x
, y
, O_SPACE
); /* active or non-active or anything, element falling in will always disappear */
1132 do_fall_try_crush(GdCave
*cave
, int x
, int y
, GdDirection fall_dir
)
1134 if (explodes_by_hit_dir(cave
, x
, y
, fall_dir
)) {
1135 explode_dir(cave
, x
, y
, fall_dir
);
1143 do_fall_roll_or_stop(GdCave
*cave
, int x
, int y
, GdDirection fall_dir
, GdElement bouncing
)
1145 if (is_space_dir(cave
, x
, y
, fall_dir
)) { /* falling further */
1146 move(cave
, x
, y
, fall_dir
, get(cave
, x
, y
));
1149 /* check if it is on a sloped element, and it can roll. */
1150 /* for example, sloped wall looks like: */
1153 /* this is tagged as sloped up&left. */
1154 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1155 /* then check the direction to roll (left or right) */
1156 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1157 if (sloped_dir(cave
, x
, y
, fall_dir
, opposite
[fall_dir
])) { /* sloped element, falling to left or right */
1158 if (sloped_dir(cave
, x
, y
, fall_dir
, cw_fourth
[fall_dir
]) && is_space_dir(cave
, x
, y
, cw_eighth
[fall_dir
]) && is_space_dir(cave
, x
, y
, cw_fourth
[fall_dir
])) {
1159 play_sound_of_element(cave
, get(cave
, x
, y
));
1160 move(cave
, x
, y
, cw_fourth
[fall_dir
], get(cave
, x
, y
)); /* try to roll left first - see O_STONE to understand why cw_fourth */
1162 else if (sloped_dir(cave
, x
, y
, fall_dir
, ccw_fourth
[fall_dir
]) && is_space_dir(cave
, x
, y
, ccw_eighth
[fall_dir
]) && is_space_dir(cave
, x
, y
, ccw_fourth
[fall_dir
])) {
1163 play_sound_of_element(cave
, get(cave
, x
, y
));
1164 move(cave
, x
, y
, ccw_fourth
[fall_dir
], get(cave
, x
, y
)); /* if not, try to roll right */
1167 /* cannot roll in any direction, so it stops */
1168 play_sound_of_element(cave
, get(cave
, x
, y
));
1169 store(cave
, x
, y
, bouncing
);
1174 /* any other element, stops */
1175 play_sound_of_element(cave
, get(cave
, x
, y
));
1176 store(cave
, x
, y
, bouncing
);
1190 /* process a cave. */
1192 gd_cave_iterate(GdCave
*cave
, GdDirection player_move
, gboolean player_fire
, gboolean suicide
)
1195 int ymin
, ymax
; /* for border scan */
1196 gboolean amoeba_found_enclosed
, amoeba_2_found_enclosed
; /* amoeba found to be enclosed. if not, this is cleared */
1197 int amoeba_count
, amoeba_2_count
; /* counting the number of amoebas. after scan, check if too much */
1198 gboolean found_water
; /* cave scan found water - for sound */
1199 gboolean inbox_toggle
;
1200 gboolean start_signal
;
1201 GdDirection grav_compat
=cave
->gravity_affects_all
?cave
->gravity
:MV_DOWN
; /* gravity for falling wall, bladder, ... */
1202 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1203 static const GdDirection creature_dir
[]={ MV_LEFT
, MV_UP
, MV_RIGHT
, MV_DOWN
};
1204 static const GdDirection creature_chdir
[]={ MV_RIGHT
, MV_DOWN
, MV_LEFT
, MV_UP
};
1205 int time_decrement_sec
;
1206 GdElement biter_try
[]={ O_DIRT
, cave
->biter_eat
, O_SPACE
, O_STONE
}; /* biters eating elements preference, they try to go in this order */
1207 gboolean amoeba_sound
, magic_sound
;
1209 gd_cave_clear_sounds(cave
);
1211 /* if diagonal movements not allowed, */
1212 /* horizontal movements have precedence. [BROADRIBB] */
1213 if (!cave
->diagonal_movements
) {
1214 switch (player_move
) {
1217 player_move
=MV_RIGHT
;
1221 player_move
=MV_LEFT
;
1224 /* no correction needed */
1229 /* set cave get function; to implement perfect or lineshifting borders */
1230 if (cave
->lineshift
)
1231 cave
->getp
=getp_shift
;
1233 cave
->getp
=getp_perfect
;
1235 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1236 if (cave
->player_seen_ago
<100)
1237 cave
->player_seen_ago
++;
1239 if (cave
->pneumatic_hammer_active_delay
>0)
1240 cave
->pneumatic_hammer_active_delay
--;
1242 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1243 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1244 cave
->inbox_flash_toggle
=!cave
->inbox_flash_toggle
;
1245 inbox_toggle
=cave
->inbox_flash_toggle
;
1247 if (cave
->gate_open_flash
>0)
1248 cave
->gate_open_flash
--;
1250 /* score collected this frame */
1253 /* suicide only kills the active player */
1254 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1255 /* we must check if there is a player or not - he may have exploded or something like that */
1256 if (suicide
&& cave
->player_state
==GD_PL_LIVING
&& is_player(cave
, cave
->player_x
, cave
->player_y
))
1257 store(cave
, cave
->player_x
, cave
->player_y
, O_EXPLODE_1
);
1259 /* check for walls reappearing */
1260 if (cave
->hammered_reappear
)
1261 for (y
=0; y
<cave
->h
; y
++)
1262 for (x
=0; x
<cave
->w
; x
++) {
1263 /* timer for the cell > 0? */
1264 if (cave
->hammered_reappear
[y
][x
]>0) {
1265 /* decrease timer */
1266 cave
->hammered_reappear
[y
][x
]--;
1267 /* check if it became zero */
1268 if (cave
->hammered_reappear
[y
][x
]==0) {
1269 store(cave
, x
, y
, O_BRICK
);
1270 gd_sound_play(cave
, GD_S_WALL_REAPPEAR
);
1275 /* variables to check during the scan */
1276 amoeba_found_enclosed
=TRUE
; /* will be set to false if any of the amoeba is found free. */
1277 amoeba_2_found_enclosed
=TRUE
;
1282 time_decrement_sec
=0;
1284 /* check whether to scan the first and last line */
1285 if (cave
->border_scan_first_and_last
) {
1292 /* the cave scan routine */
1293 for (y
=ymin
; y
<=ymax
; y
++)
1294 for (x
=0; x
<cave
->w
; x
++) {
1295 /* if we find a scanned element, change it to the normal one, and that's all. */
1296 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1297 if (get(cave
, x
, y
)&SCANNED
) {
1298 store(cave
, x
, y
, get(cave
, x
, y
)& ~SCANNED
);
1302 /* add the ckdelay correction value for every element seen. */
1303 cave
->ckdelay
+=gd_elements
[get(cave
, x
, y
)].ckdelay
;
1305 switch (get(cave
, x
, y
)) {
1310 if (cave
->kill_player
) {
1311 explode (cave
, x
, y
);
1314 cave
->player_seen_ago
=0;
1315 /* bd4 intermission caves have many players. so if one of them has exited,
1316 * do not change the flag anymore. so this if () is needed */
1317 if (cave
->player_state
!=GD_PL_EXITED
)
1318 cave
->player_state
=GD_PL_LIVING
;
1320 /* check for pneumatic hammer things */
1321 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right for hammer 5) stand on something */
1322 if (player_fire
&& cave
->got_pneumatic_hammer
&& is_space_dir(cave
, x
, y
, player_move
)
1323 && !is_space_dir(cave
, x
, y
, MV_DOWN
)) {
1324 if (player_move
==MV_LEFT
&& can_be_hammered_dir(cave
, x
, y
, MV_DOWN_LEFT
)) {
1325 cave
->pneumatic_hammer_active_delay
=cave
->pneumatic_hammer_frame
;
1326 store_dir(cave
, x
, y
, MV_LEFT
, O_PNEUMATIC_ACTIVE_LEFT
);
1327 store(cave
, x
, y
, O_PLAYER_PNEUMATIC_LEFT
);
1328 break; /* finished. */
1330 if (player_move
==MV_RIGHT
&& can_be_hammered_dir(cave
, x
, y
, MV_DOWN_RIGHT
)) {
1331 cave
->pneumatic_hammer_active_delay
=cave
->pneumatic_hammer_frame
;
1332 store_dir(cave
, x
, y
, MV_RIGHT
, O_PNEUMATIC_ACTIVE_RIGHT
);
1333 store(cave
, x
, y
, O_PLAYER_PNEUMATIC_RIGHT
);
1334 break; /* finished. */
1338 if (player_move
!=MV_STILL
) {
1339 /* only do every check if he is not moving */
1340 GdElement what
=get_dir(cave
, x
, y
, player_move
);
1341 GdElement remains
=what
;
1344 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1345 if (what
==O_TELEPORTER
&& do_teleporter(cave
, x
, y
, player_move
))
1348 /* try to push element; if successful, break */
1349 push
=do_push(cave
, x
, y
, player_move
, player_fire
);
1355 /* if its a bomb, remember he now has one. */
1356 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1357 gd_sound_play(cave
, GD_S_BOMB_COLLECT
);
1358 store_dir(cave
, x
, y
, player_move
, O_SPACE
);
1360 store(cave
, x
, y
, O_PLAYER_BOMB
);
1362 move(cave
, x
, y
, player_move
, O_PLAYER_BOMB
);
1366 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1367 if (!player_fire
&& !cave
->gravity_switch_active
&& cave
->skeletons_collected
>=cave
->skeletons_needed_for_pot
) {
1368 cave
->skeletons_collected
-=cave
->skeletons_needed_for_pot
;
1369 move(cave
, x
, y
, player_move
, O_PLAYER_STIRRING
);
1370 cave
->gravity_disabled
=TRUE
;
1374 case O_GRAVITY_SWITCH
:
1375 /* (we cannot use player_get for this as it does not have player_move parameter) */
1376 /* only allow changing direction if the new dir is not diagonal */
1377 if (cave
->gravity_switch_active
&& (player_move
==MV_LEFT
|| player_move
==MV_RIGHT
|| player_move
==MV_UP
|| player_move
==MV_DOWN
)) {
1378 gd_sound_play(cave
, GD_S_SWITCH_GRAVITY
);
1379 cave
->gravity_will_change
=cave
->gravity_change_time
*cave
->timing_factor
;
1380 cave
->gravity_next_direction
=player_move
;
1381 cave
->gravity_switch_active
=FALSE
;
1386 /* get element - process others. if cannot get, player_get_element will return the same */
1387 remains
=player_get_element (cave
, what
);
1391 if (remains
!=what
|| remains
==O_SPACE
) {
1392 /* if anything changed, apply the change. */
1394 /* if snapping anything and we have snapping explosions set. but these is not true for pushing. */
1395 if (remains
==O_SPACE
&& player_fire
&& !push
)
1396 remains
=cave
->snap_element
;
1397 if (remains
!=O_SPACE
|| player_fire
)
1398 /* if any other element than space, player cannot move. also if pressing fire, will not move. */
1399 store_dir(cave
, x
, y
, player_move
, remains
);
1401 /* if space remains there, the player moves. */
1402 move(cave
, x
, y
, player_move
, O_PLAYER
);
1409 /* much simpler; cannot steal stones */
1410 if (cave
->kill_player
) {
1411 explode (cave
, x
, y
);
1414 cave
->player_seen_ago
=0;
1415 /* bd4 intermission caves have many players. so if one of them has exited,
1416 * do not change the flag anymore. so this if () is needed */
1417 if (cave
->player_state
!=GD_PL_EXITED
)
1418 cave
->player_state
=GD_PL_LIVING
;
1420 if (player_move
!=MV_STILL
) { /* if the player does not move, nothing to do */
1421 GdElement what
=get_dir(cave
, x
, y
, player_move
);
1422 GdElement remains
=what
;
1425 /* placing a bomb into empty space or dirt */
1426 if (is_space_dir(cave
, x
, y
, player_move
) || is_element_dir(cave
, x
, y
, player_move
, O_DIRT
)) {
1427 store_dir(cave
, x
, y
, player_move
, O_BOMB_TICK_1
);
1428 /* placed bomb, he is normal player again */
1429 store(cave
, x
, y
, O_PLAYER
);
1430 gd_sound_play(cave
, GD_S_BOMB_PLACE
);
1435 /* pushing and collecting */
1436 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1437 if (what
==O_TELEPORTER
&& do_teleporter(cave
, x
, y
, player_move
))
1440 if (do_push(cave
, x
, y
, player_move
, FALSE
)) /* player fire is false... */
1444 case O_GRAVITY_SWITCH
:
1445 /* (we cannot use player_get for this as it does not have player_move parameter) */
1446 /* only allow changing direction if the new dir is not diagonal */
1447 if (cave
->gravity_switch_active
&& (player_move
==MV_LEFT
|| player_move
==MV_RIGHT
|| player_move
==MV_UP
|| player_move
==MV_DOWN
)) {
1448 gd_sound_play(cave
, GD_S_SWITCH_GRAVITY
);
1449 cave
->gravity_will_change
=cave
->gravity_change_time
*cave
->timing_factor
;
1450 cave
->gravity_next_direction
=player_move
;
1451 cave
->gravity_switch_active
=FALSE
;
1455 /* get element. if cannot get, player_get_element will return the same */
1456 remains
=player_get_element (cave
, what
);
1461 /* if element changed, OR there is space, move. */
1462 if (remains
!=what
|| remains
==O_SPACE
) {
1463 /* if anything changed, apply the change. */
1464 move(cave
, x
, y
, player_move
, O_PLAYER_BOMB
);
1469 case O_PLAYER_STIRRING
:
1470 if (cave
->kill_player
) {
1471 explode (cave
, x
, y
);
1474 gd_sound_play(cave
, GD_S_STIRRING
); /* stirring sound, if no other walking sound or explosion */
1475 cave
->player_seen_ago
=0;
1476 /* bd4 intermission caves have many players. so if one of them has exited,
1477 * do not change the flag anymore. so this if () is needed */
1478 if (cave
->player_state
!=GD_PL_EXITED
)
1479 cave
->player_state
=GD_PL_LIVING
;
1481 /* player "exits" stirring the pot by pressing fire */
1482 cave
->gravity_disabled
=FALSE
;
1483 store(cave
, x
, y
, O_PLAYER
);
1484 cave
->gravity_switch_active
=TRUE
;
1488 /* player holding pneumatic hammer */
1489 case O_PLAYER_PNEUMATIC_LEFT
:
1490 case O_PLAYER_PNEUMATIC_RIGHT
:
1491 /* usual player stuff */
1492 if (cave
->kill_player
) {
1493 explode (cave
, x
, y
);
1496 cave
->player_seen_ago
=0;
1497 if (cave
->player_state
!=GD_PL_EXITED
)
1498 cave
->player_state
=GD_PL_LIVING
;
1499 if (cave
->pneumatic_hammer_active_delay
==0) /* if hammering time is up, becomes a normal player again. */
1500 store(cave
, x
, y
, O_PLAYER
);
1503 /* the active pneumatic hammer itself */
1504 case O_PNEUMATIC_ACTIVE_RIGHT
:
1505 case O_PNEUMATIC_ACTIVE_LEFT
:
1506 if (cave
->pneumatic_hammer_active_delay
==0) {
1509 store(cave
, x
, y
, O_SPACE
); /* pneumatic hammer element disappears */
1510 /* which is the new element which appears after that one is hammered? */
1511 new_elem
=gd_element_get_hammered(get_dir(cave
, x
, y
, MV_DOWN
));
1512 /* if there is a new element, display it */
1513 /* O_NONE might be returned, for example if the element being hammered explodes during hammering (by a nearby explosion) */
1514 if (new_elem
!=O_NONE
) {
1515 store_dir(cave
, x
, y
, MV_DOWN
, new_elem
);
1517 /* and if walls reappear, remember it in array */
1518 if (cave
->hammered_walls_reappear
) {
1521 wall_y
=(y
+1)%cave
->h
;
1522 cave
->hammered_reappear
[wall_y
][x
]=cave
->hammered_wall_reappear_frame
;
1530 * S T O N E S, D I A M O N D S
1532 case O_STONE
: /* standing stone */
1533 do_start_fall(cave
, x
, y
, cave
->gravity
, cave
->stone_falling_effect
);
1536 case O_MEGA_STONE
: /* standing mega_stone */
1537 do_start_fall(cave
, x
, y
, cave
->gravity
, O_MEGA_STONE_F
);
1540 case O_DIAMOND
: /* standing diamond */
1541 do_start_fall(cave
, x
, y
, cave
->gravity
, cave
->diamond_falling_effect
);
1544 case O_NUT
: /* standing nut */
1545 do_start_fall(cave
, x
, y
, cave
->gravity
, O_NUT_F
);
1548 case O_DIRT_BALL
: /* standing dirt ball */
1549 do_start_fall(cave
, x
, y
, cave
->gravity
, O_DIRT_BALL_F
);
1552 case O_DIRT_LOOSE
: /* standing loose dirt */
1553 do_start_fall(cave
, x
, y
, cave
->gravity
, O_DIRT_LOOSE_F
);
1556 case O_FLYING_STONE
: /* standing stone */
1557 do_start_fall(cave
, x
, y
, opposite
[cave
->gravity
], O_FLYING_STONE_F
);
1560 case O_FLYING_DIAMOND
: /* standing diamond */
1561 do_start_fall(cave
, x
, y
, opposite
[cave
->gravity
], O_FLYING_DIAMOND_F
);
1565 * 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
1567 case O_DIRT_BALL_F
: /* falling dirt ball */
1568 if (!cave
->gravity_disabled
)
1569 do_fall_roll_or_stop(cave
, x
, y
, cave
->gravity
, O_DIRT_BALL
);
1572 case O_DIRT_LOOSE_F
: /* falling loose dirt */
1573 if (!cave
->gravity_disabled
)
1574 do_fall_roll_or_stop(cave
, x
, y
, cave
->gravity
, O_DIRT_LOOSE
);
1577 case O_STONE_F
: /* falling stone */
1578 if (!cave
->gravity_disabled
) {
1579 if (do_fall_try_crush_voodoo(cave
, x
, y
, cave
->gravity
)) break;
1580 if (do_fall_try_crack_nut(cave
, x
, y
, cave
->gravity
, cave
->stone_bouncing_effect
)) break;
1581 if (do_fall_try_magic(cave
, x
, y
, cave
->gravity
, cave
->magic_stone_to
)) break;
1582 if (do_fall_try_crush(cave
, x
, y
, cave
->gravity
)) break;
1583 do_fall_roll_or_stop(cave
, x
, y
, cave
->gravity
, cave
->stone_bouncing_effect
);
1587 case O_MEGA_STONE_F
: /* falling mega */
1588 if (!cave
->gravity_disabled
) {
1589 if (do_fall_try_crush_voodoo(cave
, x
, y
, cave
->gravity
)) break;
1590 if (do_fall_try_crack_nut(cave
, x
, y
, cave
->gravity
, O_MEGA_STONE
)) break;
1591 if (do_fall_try_magic(cave
, x
, y
, cave
->gravity
, cave
->magic_mega_stone_to
)) break;
1592 if (do_fall_try_crush(cave
, x
, y
, cave
->gravity
)) break;
1593 do_fall_roll_or_stop(cave
, x
, y
, cave
->gravity
, O_MEGA_STONE
);
1597 case O_DIAMOND_F
: /* falling diamond */
1598 if (!cave
->gravity_disabled
) {
1599 if (do_fall_try_eat_voodoo(cave
, x
, y
, cave
->gravity
)) break;
1600 if (do_fall_try_magic(cave
, x
, y
, cave
->gravity
, cave
->magic_diamond_to
)) break;
1601 if (do_fall_try_crush(cave
, x
, y
, cave
->gravity
)) break;
1602 do_fall_roll_or_stop(cave
, x
, y
, cave
->gravity
, cave
->diamond_bouncing_effect
);
1606 case O_NUT_F
: /* falling nut */
1607 if (!cave
->gravity_disabled
) {
1608 if (do_fall_try_magic(cave
, x
, y
, cave
->gravity
, cave
->magic_nut_to
)) break;
1609 if (do_fall_try_crush(cave
, x
, y
, cave
->gravity
)) break;
1610 do_fall_roll_or_stop(cave
, x
, y
, cave
->gravity
, O_NUT
);
1614 case O_FLYING_STONE_F
: /* falling stone */
1615 if (!cave
->gravity_disabled
) {
1616 GdDirection fall_dir
=opposite
[cave
->gravity
];
1618 if (do_fall_try_crush_voodoo(cave
, x
, y
, fall_dir
)) break;
1619 if (do_fall_try_crack_nut(cave
, x
, y
, fall_dir
, O_FLYING_STONE
)) break;
1620 if (do_fall_try_magic(cave
, x
, y
, fall_dir
, cave
->magic_flying_stone_to
)) break;
1621 if (do_fall_try_crush(cave
, x
, y
, fall_dir
)) break;
1622 do_fall_roll_or_stop(cave
, x
, y
, fall_dir
, O_FLYING_STONE
);
1626 case O_FLYING_DIAMOND_F
: /* falling diamond */
1627 if (!cave
->gravity_disabled
) {
1628 GdDirection fall_dir
=opposite
[cave
->gravity
];
1630 if (do_fall_try_eat_voodoo(cave
, x
, y
, fall_dir
)) break;
1631 if (do_fall_try_magic(cave
, x
, y
, fall_dir
, cave
->magic_flying_diamond_to
)) break;
1632 if (do_fall_try_crush(cave
, x
, y
, fall_dir
)) break;
1633 do_fall_roll_or_stop(cave
, x
, y
, fall_dir
, O_FLYING_DIAMOND
);
1642 case O_NITRO_PACK
: /* standing nitro pack */
1643 do_start_fall(cave
, x
, y
, cave
->gravity
, O_NITRO_PACK_F
);
1646 case O_NITRO_PACK_F
: /* falling nitro pack */
1647 if (!cave
->gravity_disabled
) {
1648 if (is_space_dir(cave
, x
, y
, cave
->gravity
)) /* if space, falling further */
1649 move(cave
, x
, y
, cave
->gravity
, get(cave
, x
, y
));
1650 else if (do_fall_try_magic(cave
, x
, y
, cave
->gravity
, cave
->magic_nitro_pack_to
)) {
1651 /* try magic wall; if true, function did the work */
1653 else if (is_element_dir(cave
, x
, y
, cave
->gravity
, O_DIRT
)) {
1654 /* falling on a dirt, it does NOT explode - just stops at its place. */
1655 play_sound_of_element(cave
, O_NITRO_PACK
);
1656 store(cave
, x
, y
, O_NITRO_PACK
);
1659 /* falling on any other element it explodes */
1660 explode(cave
, x
, y
);
1663 case O_NITRO_PACK_EXPLODE
: /* a triggered nitro pack */
1664 explode(cave
, x
, y
);
1677 /* if cannot move in any direction, becomes an enclosed cow */
1678 if (!is_space_dir(cave
, x
, y
, MV_UP
) && !is_space_dir(cave
, x
, y
, MV_DOWN
)
1679 && !is_space_dir(cave
, x
, y
, MV_LEFT
) && !is_space_dir(cave
, x
, y
, MV_RIGHT
))
1680 store(cave
, x
, y
, O_COW_ENCLOSED_1
);
1682 /* THIS IS THE CREATURE MOVE thing copied. */
1683 const GdDirection
*creature_move
;
1684 gboolean ccw
=rotates_ccw(cave
, x
, y
); /* check if default is counterclockwise */
1685 GdElement base
; /* base element number (which is like O_***_1) */
1686 int dir
, dirn
, dirp
; /* direction */
1690 dir
=get(cave
, x
, y
)-base
; /* facing where */
1691 creature_move
=cave
->creatures_backwards
? creature_chdir
:creature_dir
;
1693 /* now change direction if backwards */
1694 if (cave
->creatures_backwards
)
1698 dirn
=(dir
+3)&3; /* fast turn */
1699 dirp
=(dir
+1)&3; /* slow turn */
1701 dirn
=(dir
+1)&3; /* fast turn */
1702 dirp
=(dir
+3)&3; /* slow turn */
1705 if (is_space_dir(cave
, x
, y
, creature_move
[dirn
]))
1706 move(cave
, x
, y
, creature_move
[dirn
], base
+dirn
); /* turn and move to preferred dir */
1707 else if (is_space_dir(cave
, x
, y
, creature_move
[dir
]))
1708 move(cave
, x
, y
, creature_move
[dir
], base
+dir
); /* go on */
1710 store(cave
, x
, y
, base
+dirp
); /* turn in place if nothing else possible */
1713 /* enclosed cows wait some time before turning to a skeleton */
1714 case O_COW_ENCLOSED_1
:
1715 case O_COW_ENCLOSED_2
:
1716 case O_COW_ENCLOSED_3
:
1717 case O_COW_ENCLOSED_4
:
1718 case O_COW_ENCLOSED_5
:
1719 case O_COW_ENCLOSED_6
:
1720 if (is_space_dir(cave
, x
, y
, MV_UP
) || is_space_dir(cave
, x
, y
, MV_LEFT
) || is_space_dir(cave
, x
, y
, MV_RIGHT
) || is_space_dir(cave
, x
, y
, MV_DOWN
))
1721 store(cave
, x
, y
, O_COW_1
);
1725 case O_COW_ENCLOSED_7
:
1726 if (is_space_dir(cave
, x
, y
, MV_UP
) || is_space_dir(cave
, x
, y
, MV_LEFT
) || is_space_dir(cave
, x
, y
, MV_RIGHT
) || is_space_dir(cave
, x
, y
, MV_DOWN
))
1727 store(cave
, x
, y
, O_COW_1
);
1729 store(cave
, x
, y
, O_SKELETON
);
1736 case O_ALT_FIREFLY_1
:
1737 case O_ALT_FIREFLY_2
:
1738 case O_ALT_FIREFLY_3
:
1739 case O_ALT_FIREFLY_4
:
1744 case O_ALT_BUTTER_1
:
1745 case O_ALT_BUTTER_2
:
1746 case O_ALT_BUTTER_3
:
1747 case O_ALT_BUTTER_4
:
1752 /* check if touches a voodoo */
1753 if (get_dir(cave
, x
, y
, MV_LEFT
)==O_VOODOO
|| get_dir(cave
, x
, y
, MV_RIGHT
)==O_VOODOO
|| get_dir(cave
, x
, y
, MV_UP
)==O_VOODOO
|| get_dir(cave
, x
, y
, MV_DOWN
)==O_VOODOO
)
1754 cave
->voodoo_touched
=TRUE
;
1755 /* check if touches something bad and should explode (includes voodoo by the flags) */
1756 if (blows_up_flies_dir(cave
, x
, y
, MV_DOWN
) || blows_up_flies_dir(cave
, x
, y
, MV_UP
)
1757 || blows_up_flies_dir(cave
, x
, y
, MV_LEFT
) || blows_up_flies_dir(cave
, x
, y
, MV_RIGHT
))
1758 explode (cave
, x
, y
);
1759 /* otherwise move */
1761 const GdDirection
*creature_move
;
1762 gboolean ccw
=rotates_ccw(cave
, x
, y
); /* check if default is counterclockwise */
1763 GdElement base
; /* base element number (which is like O_***_1) */
1764 int dir
, dirn
, dirp
; /* direction */
1766 if (get(cave
, x
, y
)>=O_FIREFLY_1
&& get(cave
, x
, y
)<=O_FIREFLY_4
)
1768 else if (get(cave
, x
, y
)>=O_BUTTER_1
&& get(cave
, x
, y
)<=O_BUTTER_4
)
1770 else if (get(cave
, x
, y
)>=O_STONEFLY_1
&& get(cave
, x
, y
)<=O_STONEFLY_4
)
1772 else if (get(cave
, x
, y
)>=O_ALT_FIREFLY_1
&& get(cave
, x
, y
)<=O_ALT_FIREFLY_4
)
1773 base
=O_ALT_FIREFLY_1
;
1774 else if (get(cave
, x
, y
)>=O_ALT_BUTTER_1
&& get(cave
, x
, y
)<=O_ALT_BUTTER_4
)
1775 base
=O_ALT_BUTTER_1
;
1777 g_assert_not_reached();
1779 dir
=get(cave
, x
, y
)-base
; /* facing where */
1780 creature_move
=cave
->creatures_backwards
? creature_chdir
:creature_dir
;
1782 /* now change direction if backwards */
1783 if (cave
->creatures_backwards
)
1787 dirn
=(dir
+3)&3; /* fast turn */
1788 dirp
=(dir
+1)&3; /* slow turn */
1790 dirn
=(dir
+1)&3; /* fast turn */
1791 dirp
=(dir
+3)&3; /* slow turn */
1794 if (is_space_dir(cave
, x
, y
, creature_move
[dirn
]))
1795 move(cave
, x
, y
, creature_move
[dirn
], base
+dirn
); /* turn and move to preferred dir */
1796 else if (is_space_dir(cave
, x
, y
, creature_move
[dir
]))
1797 move(cave
, x
, y
, creature_move
[dir
], base
+dir
); /* go on */
1799 store(cave
, x
, y
, base
+dirp
); /* turn in place if nothing else possible */
1803 case O_WAITING_STONE
:
1804 if (is_space_dir(cave
, x
, y
, grav_compat
)) { /* beginning to fall */
1806 move(cave
, x
, y
, grav_compat
, O_CHASING_STONE
);
1808 else if (sloped_dir(cave
, x
, y
, grav_compat
, opposite
[grav_compat
])) { /* rolling down a brick wall or a stone */
1809 if (sloped_dir(cave
, x
, y
, grav_compat
, cw_fourth
[grav_compat
]) && is_space_dir(cave
, x
, y
, cw_fourth
[grav_compat
]) && is_space_dir(cave
, x
, y
, cw_eighth
[grav_compat
])) {
1810 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
1811 move(cave
, x
, y
, cw_fourth
[grav_compat
], O_WAITING_STONE
);
1813 else if (sloped_dir(cave
, x
, y
, grav_compat
, ccw_fourth
[grav_compat
]) && is_space_dir(cave
, x
, y
, ccw_fourth
[grav_compat
]) && is_space_dir(cave
, x
, y
, ccw_eighth
[grav_compat
])) {
1814 /* or maybe right */
1815 move(cave
, x
, y
, ccw_fourth
[grav_compat
], O_WAITING_STONE
);
1820 case O_CHASING_STONE
:
1824 gboolean horizontal
=g_rand_boolean(cave
->random
);
1825 gboolean dont_move
=FALSE
;
1828 /* try to move... */
1830 if (horizontal
) { /*********************************/
1831 /* check for a horizontal movement */
1833 /* if coordinates are the same */
1835 horizontal
=!horizontal
;
1839 if (px
>x
&& is_space_dir(cave
, x
, y
, MV_RIGHT
)) {
1840 move(cave
, x
, y
, MV_RIGHT
, O_CHASING_STONE
);
1844 if (px
<x
&& is_space_dir(cave
, x
, y
, MV_LEFT
)) {
1845 move(cave
, x
, y
, MV_LEFT
, O_CHASING_STONE
);
1851 horizontal
=!horizontal
;
1856 } else { /********************************/
1857 /* check for a vertical movement */
1859 /* if coordinates are the same */
1861 horizontal
=!horizontal
;
1865 if (py
>y
&& is_space_dir(cave
, x
, y
, MV_DOWN
)) {
1866 move(cave
, x
, y
, MV_DOWN
, O_CHASING_STONE
);
1870 if (py
<y
&& is_space_dir(cave
, x
, y
, MV_UP
)) {
1871 move(cave
, x
, y
, MV_UP
, O_CHASING_STONE
);
1877 horizontal
=!horizontal
;
1888 /* if we should move in both directions, but can not move in any, stop. */
1890 if (horizontal
) { /* check for horizontal */
1892 if (is_space_dir(cave
, x
, y
, MV_UP
) && is_space_dir(cave
, x
, y
, MV_UP_LEFT
))
1893 move(cave
, x
, y
, MV_UP
, O_CHASING_STONE
);
1895 if (is_space_dir(cave
, x
, y
, MV_DOWN
) && is_space_dir(cave
, x
, y
, MV_DOWN_LEFT
))
1896 move(cave
, x
, y
, MV_DOWN
, O_CHASING_STONE
);
1898 if (is_space_dir(cave
, x
, y
, MV_UP
) && is_space_dir(cave
, x
, y
, MV_UP_RIGHT
))
1899 move(cave
, x
, y
, MV_UP
, O_CHASING_STONE
);
1901 if (is_space_dir(cave
, x
, y
, MV_DOWN
) && is_space_dir(cave
, x
, y
, MV_DOWN_RIGHT
))
1902 move(cave
, x
, y
, MV_DOWN
, O_CHASING_STONE
);
1904 } else { /* check for vertical */
1906 if (is_space_dir(cave
, x
, y
, MV_LEFT
) && is_space_dir(cave
, x
, y
, MV_UP_LEFT
))
1907 move(cave
, x
, y
, MV_LEFT
, O_CHASING_STONE
);
1909 if (is_space_dir(cave
, x
, y
, MV_RIGHT
) && is_space_dir(cave
, x
, y
, MV_UP_RIGHT
))
1910 move(cave
, x
, y
, MV_RIGHT
, O_CHASING_STONE
);
1912 if (is_space_dir(cave
, x
, y
, MV_LEFT
) && is_space_dir(cave
, x
, y
, MV_DOWN_LEFT
))
1913 move(cave
, x
, y
, MV_LEFT
, O_CHASING_STONE
);
1915 if (is_space_dir(cave
, x
, y
, MV_RIGHT
) && is_space_dir(cave
, x
, y
, MV_DOWN_RIGHT
))
1916 move(cave
, x
, y
, MV_RIGHT
, O_CHASING_STONE
);
1924 if (cave
->replicators_wait_frame
==0 && cave
->replicators_active
&& !cave
->gravity_disabled
) {
1925 /* only replicate, if space is under it. */
1926 /* do not replicate players! */
1927 /* also obeys gravity settings. */
1928 /* only replicate element if it is not a scanned one */
1929 /* do not replicate space... that condition looks like it makes no sense,
1930 but otherwise it generates SCANNED spaces, which cannot be "collected" by the player, so he cannot run under a replicator */
1931 if (is_space_dir(cave
, x
, y
, cave
->gravity
) && !is_player_dir(cave
, x
, y
, opposite
[cave
->gravity
])
1932 && !is_space_dir(cave
, x
, y
, opposite
[cave
->gravity
])) {
1933 store_dir(cave
, x
, y
, cave
->gravity
, get_dir(cave
, x
, y
, opposite
[cave
->gravity
]));
1934 gd_sound_play(cave
, GD_S_REPLICATOR
);
1943 if (cave
->biters_wait_frame
==0) {
1944 static GdDirection biter_move
[]={ MV_UP
, MV_RIGHT
, MV_DOWN
, MV_LEFT
};
1945 int dir
=get(cave
, x
, y
)-O_BITER_1
; /* direction, last two bits 0..3 */
1949 GdElement made_sound_of
=O_NONE
;
1951 for (i
=0; i
<G_N_ELEMENTS (biter_try
); i
++) {
1952 if (is_element_dir(cave
, x
, y
, biter_move
[dir
], biter_try
[i
])) {
1953 move(cave
, x
, y
, biter_move
[dir
], O_BITER_1
+dir
);
1954 if (biter_try
[i
]!=O_SPACE
)
1955 made_sound_of
=O_BITER_1
; /* sound of a biter eating */
1958 else if (is_element_dir(cave
, x
, y
, biter_move
[dirn
], biter_try
[i
])) {
1959 move(cave
, x
, y
, biter_move
[dirn
], O_BITER_1
+dirn
);
1960 if (biter_try
[i
]!=O_SPACE
)
1961 made_sound_of
=O_BITER_1
; /* sound of a biter eating */
1964 else if (is_element_dir(cave
, x
, y
, biter_move
[dirp
], biter_try
[i
])) {
1965 move(cave
, x
, y
, biter_move
[dirp
], O_BITER_1
+dirp
);
1966 if (biter_try
[i
]!=O_SPACE
)
1967 made_sound_of
=O_BITER_1
; /* sound of a biter eating */
1971 if (i
==G_N_ELEMENTS (biter_try
))
1972 /* i=number of elements in array: could not move, so just turn */
1973 store(cave
, x
, y
, O_BITER_1
+dirp
);
1974 else if (biter_try
[i
]==O_STONE
) {
1975 /* if there was a stone there, where we moved... do not eat stones, just throw them back */
1976 store(cave
, x
, y
, O_STONE
);
1977 made_sound_of
=O_STONE
;
1980 /* if biter did move, we had sound. play it. */
1981 if (made_sound_of
!=O_NONE
)
1982 play_sound_of_element(cave
, made_sound_of
);
1990 /* check if touches a voodoo */
1991 if (get_dir(cave
, x
, y
, MV_LEFT
)==O_VOODOO
|| get_dir(cave
, x
, y
, MV_RIGHT
)==O_VOODOO
|| get_dir(cave
, x
, y
, MV_UP
)==O_VOODOO
|| get_dir(cave
, x
, y
, MV_DOWN
)==O_VOODOO
)
1992 cave
->voodoo_touched
=TRUE
;
1993 /* check if touches something bad and should explode (includes voodoo by the flags) */
1994 if (blows_up_flies_dir(cave
, x
, y
, MV_DOWN
) || blows_up_flies_dir(cave
, x
, y
, MV_UP
)
1995 || blows_up_flies_dir(cave
, x
, y
, MV_LEFT
) || blows_up_flies_dir(cave
, x
, y
, MV_RIGHT
))
1996 explode (cave
, x
, y
);
1997 /* otherwise move */
1999 const GdDirection
*creature_move
;
2000 gboolean ccw
=rotates_ccw(cave
, x
, y
); /* check if default is counterclockwise */
2001 GdElement base
=O_DRAGONFLY_1
; /* base element number (which is like O_***_1) */
2002 int dir
, dirn
; /* direction */
2004 dir
=get(cave
, x
, y
)-base
; /* facing where */
2005 creature_move
=cave
->creatures_backwards
? creature_chdir
:creature_dir
;
2007 /* now change direction if backwards */
2008 if (cave
->creatures_backwards
)
2012 dirn
=(dir
+3)&3; /* fast turn */
2014 dirn
=(dir
+1)&3; /* fast turn */
2016 /* if can move forward, does so. */
2017 if (is_space_dir(cave
, x
, y
, creature_move
[dir
]))
2018 move(cave
, x
, y
, creature_move
[dir
], base
+dir
);
2020 /* otherwise turns 90 degrees in place. */
2021 store(cave
, x
, y
, base
+dirn
);
2027 store(cave
, x
, y
, O_BLADDER_1
);
2038 /* bladder with any delay state: try to convert to clock. */
2039 if (is_element_dir(cave
, x
, y
, opposite
[grav_compat
], cave
->bladder_converts_by
)
2040 || is_element_dir(cave
, x
, y
, cw_fourth
[grav_compat
], cave
->bladder_converts_by
)
2041 || is_element_dir(cave
, x
, y
, ccw_fourth
[grav_compat
], cave
->bladder_converts_by
)) {
2042 /* if touches the specified element, let it be a clock */
2043 store(cave
, x
, y
, O_PRE_CLOCK_1
);
2044 play_sound_of_element(cave
, O_PRE_CLOCK_1
); /* plays the bladder convert sound */
2046 /* is space over the bladder? */
2047 if (is_space_dir(cave
, x
, y
, opposite
[grav_compat
])) {
2048 if (get(cave
, x
, y
)==O_BLADDER_8
) {
2049 /* if it is a bladder 8, really move up */
2050 move(cave
, x
, y
, opposite
[grav_compat
], O_BLADDER_1
);
2051 play_sound_of_element(cave
, O_BLADDER
);
2054 /* if smaller delay, just increase delay. */
2058 /* if not space, is something sloped over the bladder? */
2059 if (sloped_for_bladder_dir(cave
, x
, y
, opposite
[grav_compat
]) && sloped_dir(cave
, x
, y
, opposite
[grav_compat
], opposite
[grav_compat
])) {
2060 if (sloped_dir(cave
, x
, y
, opposite
[grav_compat
], ccw_fourth
[opposite
[grav_compat
]])
2061 && is_space_dir(cave
, x
, y
, ccw_fourth
[opposite
[grav_compat
]])
2062 && is_space_dir(cave
, x
, y
, ccw_eighth
[opposite
[grav_compat
]])) {
2063 /* rolling up, to left */
2064 if (get(cave
, x
, y
)==O_BLADDER_8
) {
2065 /* if it is a bladder 8, really roll */
2066 move(cave
, x
, y
, ccw_fourth
[opposite
[grav_compat
]], O_BLADDER_8
);
2067 play_sound_of_element(cave
, O_BLADDER
);
2069 /* if smaller delay, just increase delay. */
2073 if (sloped_dir(cave
, x
, y
, opposite
[grav_compat
], cw_fourth
[opposite
[grav_compat
]])
2074 && is_space_dir(cave
, x
, y
, cw_fourth
[opposite
[grav_compat
]])
2075 && is_space_dir(cave
, x
, y
, cw_eighth
[opposite
[grav_compat
]])) {
2076 /* rolling up, to left */
2077 if (get(cave
, x
, y
)==O_BLADDER_8
) {
2078 /* if it is a bladder 8, really roll */
2079 move(cave
, x
, y
, cw_fourth
[opposite
[grav_compat
]], O_BLADDER_8
);
2080 play_sound_of_element(cave
, O_BLADDER
);
2082 /* if smaller delay, just increase delay. */
2086 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2088 store(cave
, x
, y
, O_BLADDER_1
);
2093 if (blows_up_flies_dir(cave
, x
, y
, MV_DOWN
) || blows_up_flies_dir(cave
, x
, y
, MV_UP
)
2094 || blows_up_flies_dir(cave
, x
, y
, MV_LEFT
) || blows_up_flies_dir(cave
, x
, y
, MV_RIGHT
))
2095 explode (cave
, x
, y
);
2099 /* the ghost is given four possibilities to move. */
2100 for (i
=0; i
<4; i
++) {
2101 static GdDirection dirs
[]={MV_UP
, MV_DOWN
, MV_LEFT
, MV_RIGHT
};
2102 GdDirection random_dir
;
2104 random_dir
=dirs
[g_rand_int_range(cave
->random
, 0, G_N_ELEMENTS(dirs
))];
2105 if (is_space_dir(cave
, x
, y
, random_dir
)) {
2106 move(cave
, x
, y
, random_dir
, O_GHOST
);
2107 break; /* ghost did move -> exit loop */
2116 * A C T I V E E L E M E N T S
2121 switch (cave
->amoeba_state
) {
2123 store(cave
, x
, y
, cave
->amoeba_too_big_effect
);
2125 case GD_AM_ENCLOSED
:
2126 store(cave
, x
, y
, cave
->amoeba_enclosed_effect
);
2128 case GD_AM_SLEEPING
:
2130 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2131 if (amoeba_found_enclosed
)
2132 /* if still found enclosed, check all four directions, if this one is able to grow. */
2133 if (amoeba_eats_dir(cave
, x
, y
, MV_UP
) || amoeba_eats_dir(cave
, x
, y
, MV_DOWN
)
2134 || amoeba_eats_dir(cave
, x
, y
, MV_LEFT
) || amoeba_eats_dir(cave
, x
, y
, MV_RIGHT
)) {
2135 amoeba_found_enclosed
=FALSE
; /* not enclosed. this is a local (per scan) flag! */
2136 cave
->amoeba_state
=GD_AM_AWAKE
;
2139 /* if alive, check in which dir to grow (or not) */
2140 if (cave
->amoeba_state
==GD_AM_AWAKE
) {
2141 if (g_rand_int_range(cave
->random
, 0, 1000000)<cave
->amoeba_growth_prob
) {
2142 switch (g_rand_int_range(cave
->random
, 0, 4)) { /* decided to grow, choose a random direction. */
2143 case 0: /* let this be up. numbers indifferent. */
2144 if (amoeba_eats_dir(cave
, x
, y
, MV_UP
))
2145 store_dir(cave
, x
, y
, MV_UP
, O_AMOEBA
);
2148 if (amoeba_eats_dir(cave
, x
, y
, MV_DOWN
))
2149 store_dir(cave
, x
, y
, MV_DOWN
, O_AMOEBA
);
2152 if (amoeba_eats_dir(cave
, x
, y
, MV_LEFT
))
2153 store_dir(cave
, x
, y
, MV_LEFT
, O_AMOEBA
);
2156 if (amoeba_eats_dir(cave
, x
, y
, MV_RIGHT
))
2157 store_dir(cave
, x
, y
, MV_RIGHT
, O_AMOEBA
);
2168 /* check if it is touching an amoeba, and explosion is enabled */
2169 if (cave
->amoeba_2_explodes_by_amoeba
2170 && (is_element_dir(cave
, x
, y
, MV_DOWN
, O_AMOEBA
) || is_element_dir(cave
, x
, y
, MV_UP
, O_AMOEBA
)
2171 || is_element_dir(cave
, x
, y
, MV_LEFT
, O_AMOEBA
) || is_element_dir(cave
, x
, y
, MV_RIGHT
, O_AMOEBA
)))
2172 explode (cave
, x
, y
);
2174 switch (cave
->amoeba_2_state
) {
2176 store(cave
, x
, y
, cave
->amoeba_2_too_big_effect
);
2178 case GD_AM_ENCLOSED
:
2179 store(cave
, x
, y
, cave
->amoeba_2_enclosed_effect
);
2181 case GD_AM_SLEEPING
:
2183 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2184 if (amoeba_2_found_enclosed
)
2185 if (amoeba_eats_dir(cave
, x
, y
, MV_UP
) || amoeba_eats_dir(cave
, x
, y
, MV_DOWN
)
2186 || amoeba_eats_dir(cave
, x
, y
, MV_LEFT
) || amoeba_eats_dir(cave
, x
, y
, MV_RIGHT
)) {
2187 amoeba_2_found_enclosed
=FALSE
; /* not enclosed. this is a local (per scan) flag! */
2188 cave
->amoeba_2_state
=GD_AM_AWAKE
;
2191 if (cave
->amoeba_2_state
==GD_AM_AWAKE
) /* if it is alive, decide if it attempts to grow */
2192 if (g_rand_int_range(cave
->random
, 0, 1000000)<cave
->amoeba_2_growth_prob
) {
2193 switch (g_rand_int_range(cave
->random
, 0, 4)) { /* decided to grow, choose a random direction. */
2194 case 0: /* let this be up. numbers indifferent. */
2195 if (amoeba_eats_dir(cave
, x
, y
, MV_UP
))
2196 store_dir(cave
, x
, y
, MV_UP
, O_AMOEBA_2
);
2199 if (amoeba_eats_dir(cave
, x
, y
, MV_DOWN
))
2200 store_dir(cave
, x
, y
, MV_DOWN
, O_AMOEBA_2
);
2203 if (amoeba_eats_dir(cave
, x
, y
, MV_LEFT
))
2204 store_dir(cave
, x
, y
, MV_LEFT
, O_AMOEBA_2
);
2207 if (amoeba_eats_dir(cave
, x
, y
, MV_RIGHT
))
2208 store_dir(cave
, x
, y
, MV_RIGHT
, O_AMOEBA_2
);
2217 /* choose randomly, if it spreads */
2218 if (g_rand_int_range(cave
->random
, 0, 1000000)<=cave
->acid_spread_ratio
) {
2219 /* the current one explodes */
2220 store(cave
, x
, y
, cave
->acid_turns_to
);
2221 /* and if neighbours are eaten, put acid there. */
2222 if (is_element_dir(cave
, x
, y
, MV_UP
, cave
->acid_eats_this
)) {
2223 play_sound_of_element(cave
, O_ACID
);
2224 store_dir(cave
, x
, y
, MV_UP
, O_ACID
);
2226 if (is_element_dir(cave
, x
, y
, MV_DOWN
, cave
->acid_eats_this
)) {
2227 play_sound_of_element(cave
, O_ACID
);
2228 store_dir(cave
, x
, y
, MV_DOWN
, O_ACID
);
2230 if (is_element_dir(cave
, x
, y
, MV_LEFT
, cave
->acid_eats_this
)) {
2231 play_sound_of_element(cave
, O_ACID
);
2232 store_dir(cave
, x
, y
, MV_LEFT
, O_ACID
);
2234 if (is_element_dir(cave
, x
, y
, MV_RIGHT
, cave
->acid_eats_this
)) {
2235 play_sound_of_element(cave
, O_ACID
);
2236 store_dir(cave
, x
, y
, MV_RIGHT
, O_ACID
);
2243 if (!cave
->water_does_not_flow_down
&& is_space_dir(cave
, x
, y
, MV_DOWN
)) /* emulating the odd behaviour in crdr */
2244 store_dir(cave
, x
, y
, MV_DOWN
, O_WATER_1
);
2245 if (is_space_dir(cave
, x
, y
, MV_UP
))
2246 store_dir(cave
, x
, y
, MV_UP
, O_WATER_1
);
2247 if (is_space_dir(cave
, x
, y
, MV_LEFT
))
2248 store_dir(cave
, x
, y
, MV_LEFT
, O_WATER_1
);
2249 if (is_space_dir(cave
, x
, y
, MV_RIGHT
))
2250 store_dir(cave
, x
, y
, MV_RIGHT
, O_WATER_1
);
2254 store(cave
, x
, y
, O_WATER
);
2257 case O_H_EXPANDING_WALL
:
2258 case O_V_EXPANDING_WALL
:
2259 case O_H_EXPANDING_STEEL_WALL
:
2260 case O_V_EXPANDING_STEEL_WALL
:
2261 /* checks first if direction is changed. */
2262 if (((get(cave
, x
, y
)==O_H_EXPANDING_WALL
|| get(cave
, x
, y
)==O_H_EXPANDING_STEEL_WALL
) && !cave
->expanding_wall_changed
)
2263 || ((get(cave
, x
, y
)==O_V_EXPANDING_WALL
|| get(cave
, x
, y
)==O_V_EXPANDING_STEEL_WALL
) && cave
->expanding_wall_changed
)) {
2264 if (is_space_dir(cave
, x
, y
, MV_LEFT
)) {
2265 store_dir(cave
, x
, y
, MV_LEFT
, get(cave
, x
, y
));
2266 play_sound_of_element(cave
, get(cave
, x
, y
));
2268 if (is_space_dir(cave
, x
, y
, MV_RIGHT
)) {
2269 store_dir(cave
, x
, y
, MV_RIGHT
, get(cave
, x
, y
));
2270 play_sound_of_element(cave
, get(cave
, x
, y
));
2274 if (is_space_dir(cave
, x
, y
, MV_UP
)) {
2275 store_dir(cave
, x
, y
, MV_UP
, get(cave
, x
, y
));
2276 play_sound_of_element(cave
, get(cave
, x
, y
));
2278 if (is_space_dir(cave
, x
, y
, MV_DOWN
)) {
2279 store_dir(cave
, x
, y
, MV_DOWN
, get(cave
, x
, y
));
2280 play_sound_of_element(cave
, get(cave
, x
, y
));
2285 case O_EXPANDING_WALL
:
2286 case O_EXPANDING_STEEL_WALL
:
2287 /* the wall which grows in all four directions. */
2288 if (is_space_dir(cave
, x
, y
, MV_LEFT
)) {
2289 store_dir(cave
, x
, y
, MV_LEFT
, get(cave
, x
, y
));
2290 play_sound_of_element(cave
, get(cave
, x
, y
));
2292 if (is_space_dir(cave
, x
, y
, MV_RIGHT
)) {
2293 store_dir(cave
, x
, y
, MV_RIGHT
, get(cave
, x
, y
));
2294 play_sound_of_element(cave
, get(cave
, x
, y
));
2296 if (is_space_dir(cave
, x
, y
, MV_UP
)) {
2297 store_dir(cave
, x
, y
, MV_UP
, get(cave
, x
, y
));
2298 play_sound_of_element(cave
, get(cave
, x
, y
));
2300 if (is_space_dir(cave
, x
, y
, MV_DOWN
)) {
2301 store_dir(cave
, x
, y
, MV_DOWN
, get(cave
, x
, y
));
2302 play_sound_of_element(cave
, get(cave
, x
, y
));
2307 g_print("Step[%03d]", cave
->frame
); /* XXX */
2308 int rrr
=gd_cave_c64_random(cave
);
2309 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr
, cave
->slime_permeability_c64
, (rrr
&cave
->slime_permeability_c64
)==0);
2311 * unpredictable: g_rand_int
2312 * predictable: c64 predictable random generator.
2313 * for predictable, a random number is generated, whether or not it is even possible that the stone
2314 * will be able to pass.
2316 if (cave
->slime_predictable
? ((rrr
/* XXX */ &cave
->slime_permeability_c64
)==0) : g_rand_int_range(cave
->random
, 0, 1000000)<cave
->slime_permeability
) {
2317 GdDirection grav
=cave
->gravity
;
2318 GdDirection oppos
=opposite
[cave
->gravity
];
2320 /* space under the slime? elements may pass from top to bottom then. */
2321 if (is_space_dir(cave
, x
, y
, grav
)) {
2322 if (get_dir(cave
, x
, y
, oppos
)==cave
->slime_eats_1
) {
2323 store_dir(cave
, x
, y
, grav
, cave
->slime_converts_1
); /* output a falling xy under */
2324 store_dir(cave
, x
, y
, oppos
, O_SPACE
);
2325 play_sound_of_element(cave
, O_SLIME
);
2327 else if (get_dir(cave
, x
, y
, oppos
)==cave
->slime_eats_2
) {
2328 store_dir(cave
, x
, y
, grav
, cave
->slime_converts_2
);
2329 store_dir(cave
, x
, y
, oppos
, O_SPACE
);
2330 play_sound_of_element(cave
, O_SLIME
);
2332 else if (get_dir(cave
, x
, y
, oppos
)==cave
->slime_eats_3
) {
2333 store_dir(cave
, x
, y
, grav
, cave
->slime_converts_3
);
2334 store_dir(cave
, x
, y
, oppos
, O_SPACE
);
2335 play_sound_of_element(cave
, O_SLIME
);
2337 else if (get_dir(cave
, x
, y
, oppos
)==O_WAITING_STONE
) { /* waiting stones pass without awakening */
2338 store_dir(cave
, x
, y
, grav
, O_WAITING_STONE
);
2339 store_dir(cave
, x
, y
, oppos
, O_SPACE
);
2340 play_sound_of_element(cave
, O_SLIME
);
2342 else if (get_dir(cave
, x
, y
, oppos
)==O_CHASING_STONE
) { /* chasing stones pass */
2343 store_dir(cave
, x
, y
, grav
, O_CHASING_STONE
);
2344 store_dir(cave
, x
, y
, oppos
, O_SPACE
);
2345 play_sound_of_element(cave
, O_SLIME
);
2348 /* or space over the slime? elements may pass from bottom to up then. */
2349 if (is_space_dir(cave
, x
, y
, oppos
)) {
2350 if (get_dir(cave
, x
, y
, grav
)==O_BLADDER
) { /* bladders move UP the slime */
2351 store_dir(cave
, x
, y
, grav
, O_SPACE
);
2352 store_dir(cave
, x
, y
, oppos
, O_BLADDER_1
);
2353 play_sound_of_element(cave
, O_SLIME
);
2355 if (get_dir(cave
, x
, y
, grav
)==O_FLYING_STONE
) {
2356 store_dir(cave
, x
, y
, grav
, O_SPACE
);
2357 store_dir(cave
, x
, y
, oppos
, O_FLYING_STONE_F
);
2358 play_sound_of_element(cave
, O_SLIME
);
2360 if (get_dir(cave
, x
, y
, grav
)==O_FLYING_DIAMOND
) {
2361 store_dir(cave
, x
, y
, grav
, O_SPACE
);
2362 store_dir(cave
, x
, y
, oppos
, O_FLYING_DIAMOND_F
);
2363 play_sound_of_element(cave
, O_SLIME
);
2369 case O_FALLING_WALL
:
2370 if (is_space_dir(cave
, x
, y
, grav_compat
)) {
2371 /* try falling if space under. */
2373 for (yy
=y
+1; yy
<y
+cave
->h
; yy
++)
2374 /* yy<y+cave->h is to check everything OVER the wall - since caves wrap around !! */
2375 if (get(cave
, x
, yy
)!=O_SPACE
)
2376 /* stop cycle when other than space */
2378 /* if scanning stopped by a player... start falling! */
2379 if (get(cave
, x
, yy
)==O_PLAYER
|| get(cave
, x
, yy
)==O_PLAYER_GLUED
|| get(cave
, x
, yy
)==O_PLAYER_BOMB
) {
2380 move(cave
, x
, y
, grav_compat
, O_FALLING_WALL_F
);
2381 /* no sound when the falling wall starts falling! */
2386 case O_FALLING_WALL_F
:
2387 switch (get_dir(cave
, x
, y
, grav_compat
)) {
2389 case O_PLAYER_GLUED
:
2391 /* if player under, it explodes - the falling wall, not the player! */
2392 explode (cave
, x
, y
);
2395 /* continue falling */
2396 move(cave
, x
, y
, grav_compat
, O_FALLING_WALL_F
);
2400 play_sound_of_element(cave
, get(cave
, x
, y
));
2401 store(cave
, x
, y
, O_FALLING_WALL
);
2408 * C O N V E Y O R B E L T S
2410 case O_CONVEYOR_RIGHT
:
2411 case O_CONVEYOR_LEFT
:
2412 /* only works if gravity is up or down!!! */
2413 /* first, check for gravity and running belts. */
2414 if (!cave
->gravity_disabled
&& cave
->conveyor_belts_active
) {
2415 const GdDirection
*dir
;
2418 /* decide direction */
2419 left
=get(cave
, x
, y
)!=O_CONVEYOR_RIGHT
;
2420 if (cave
->conveyor_belts_direction_changed
)
2422 dir
=left
?ccw_eighth
:cw_eighth
;
2424 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
2425 /* if gravity is normal, and the conveyor belt has something ABOVE which can be moved
2427 the gravity is up, so anything that should float now goes DOWN and touches the conveyor */
2428 if ((cave
->gravity
==MV_DOWN
&& moved_by_conveyor_top_dir(cave
, x
, y
, MV_UP
))
2429 || (cave
->gravity
==MV_UP
&& moved_by_conveyor_bottom_dir(cave
, x
, y
, MV_UP
))) {
2430 if (!is_scanned_dir(cave
, x
, y
, MV_UP
) && is_space_dir(cave
, x
, y
, dir
[MV_UP
]))
2432 store_dir(cave
, x
, y
, dir
[MV_UP
], get_dir(cave
, x
, y
, MV_UP
)); /* move */
2433 store_dir(cave
, x
, y
, MV_UP
, O_SPACE
); /* and place a space. */
2436 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
2437 if ((cave
->gravity
==MV_UP
&& moved_by_conveyor_top_dir(cave
, x
, y
, MV_DOWN
))
2438 || (cave
->gravity
==MV_DOWN
&& moved_by_conveyor_bottom_dir(cave
, x
, y
, MV_DOWN
))) {
2439 if (!is_scanned_dir(cave
, x
, y
, MV_DOWN
) && is_space_dir(cave
, x
, y
, dir
[MV_DOWN
]))
2441 store_dir(cave
, x
, y
, dir
[MV_DOWN
], get_dir(cave
, x
, y
, MV_DOWN
)); /* move */
2442 store_dir(cave
, x
, y
, MV_DOWN
, O_SPACE
); /* and clear. */
2449 * S I M P L E C H A N G I N G; E X P L O S I O N S
2452 store(cave
, x
, y
, cave
->explosion_effect
);
2455 store(cave
, x
, y
, O_DIAMOND
);
2458 store(cave
, x
, y
, cave
->diamond_birth_effect
);
2461 store(cave
, x
, y
, O_STONE
);
2464 case O_NITRO_EXPL_4
:
2465 store(cave
, x
, y
, cave
->nitro_explosion_effect
);
2468 store(cave
, x
, y
, cave
->bomb_explosion_effect
);
2470 case O_AMOEBA_2_EXPL_4
:
2471 store(cave
, x
, y
, cave
->amoeba_2_explosion_effect
);
2474 case O_GHOST_EXPL_4
:
2476 static GdElement ghost_explode
[]={
2477 O_SPACE
, O_SPACE
, O_DIRT
, O_DIRT
, O_CLOCK
, O_CLOCK
, O_PRE_OUTBOX
,
2478 O_BOMB
, O_BOMB
, O_PLAYER
, O_GHOST
, O_BLADDER
, O_DIAMOND
, O_SWEET
,
2479 O_WAITING_STONE
, O_BITER_1
2482 store(cave
, x
, y
, ghost_explode
[g_rand_int_range(cave
->random
, 0, G_N_ELEMENTS(ghost_explode
))]);
2486 store(cave
, x
, y
, O_STEEL
);
2489 store(cave
, x
, y
, O_CLOCK
);
2492 explode(cave
, x
, y
);
2495 case O_TRAPPED_DIAMOND
:
2496 if (cave
->diamond_key_collected
)
2497 store(cave
, x
, y
, O_DIAMOND
);
2501 if (cave
->gate_open
) /* if no more diamonds needed */
2502 store(cave
, x
, y
, O_OUTBOX
); /* open outbox */
2504 case O_PRE_INVIS_OUTBOX
:
2505 if (cave
->gate_open
) /* if no more diamonds needed */
2506 store(cave
, x
, y
, O_INVIS_OUTBOX
); /* open outbox. invisible one :P */
2509 if (cave
->hatched
&& !inbox_toggle
) /* if it is time of birth */
2510 store(cave
, x
, y
, O_PRE_PL_1
);
2511 inbox_toggle
=!inbox_toggle
;
2514 store(cave
, x
, y
, O_PLAYER
);
2539 case O_GHOST_EXPL_1
:
2540 case O_GHOST_EXPL_2
:
2541 case O_GHOST_EXPL_3
:
2551 case O_NITRO_EXPL_1
:
2552 case O_NITRO_EXPL_2
:
2553 case O_NITRO_EXPL_3
:
2554 case O_AMOEBA_2_EXPL_1
:
2555 case O_AMOEBA_2_EXPL_2
:
2556 case O_AMOEBA_2_EXPL_3
:
2557 /* simply the next identifier */
2575 found_water
=TRUE
; /* for sound */
2576 /* simply the next identifier */
2580 case O_BLADDER_SPENDER
:
2581 if (is_space_dir(cave
, x
, y
, opposite
[grav_compat
])) {
2582 store_dir(cave
, x
, y
, opposite
[grav_compat
], O_BLADDER
);
2583 store(cave
, x
, y
, O_PRE_STEEL_1
);
2584 play_sound_of_element(cave
, O_BLADDER_SPENDER
);
2591 /* other inanimate elements that do nothing */
2596 /* POSTPROCESSING */
2598 /* another scan-like routine: */
2599 /* short explosions (for example, in bd1) started with explode_2. */
2600 /* internally we use explode_1; and change it to explode_2 if needed. */
2601 if (cave
->short_explosions
)
2602 for (y
=0; y
<cave
->h
; y
++)
2603 for (x
=0; x
<cave
->w
; x
++)
2604 if (is_first_stage_of_explosion(cave
, x
, y
)) {
2605 next(cave
, x
, y
); /* select next frame of explosion */
2606 store(cave
, x
, y
, get(cave
, x
, y
)&~SCANNED
); /* forget scanned flag immediately */
2609 /* finally: forget "scanned" flags for objects. */
2610 /* also, check for time penalties. */
2611 /* these is something like an effect table, but we do not really use one. */
2612 for (y
=0; y
<cave
->h
; y
++)
2613 for (x
=0; x
<cave
->w
; x
++) {
2614 if (get(cave
, x
, y
)&SCANNED
)
2615 store(cave
, x
, y
, get(cave
, x
, y
)&~SCANNED
);
2616 if (get(cave
, x
, y
)==O_TIME_PENALTY
) {
2617 store(cave
, x
, y
, O_GRAVESTONE
);
2618 time_decrement_sec
+=cave
->time_penalty
; /* there is time penalty for destroying the voodoo */
2623 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
2624 /* but we only do this, if a living player was found. if not yet, the setup routine coordinates are used */
2625 if (cave
->player_state
==GD_PL_LIVING
) {
2626 if (cave
->active_is_first_found
) {
2627 /* to be 1stb compatible, we do everything backwards. */
2628 for (y
=cave
->h
-1; y
>=0; y
--)
2629 for (x
=cave
->w
-1; x
>=0; x
--)
2630 if (is_player(cave
, x
, y
)) {
2631 /* here we remember the coordinates. */
2638 /* as in the original: look for the last one */
2639 for (y
=0; y
<cave
->h
; y
++)
2640 for (x
=0; x
<cave
->w
; x
++)
2641 if (is_player(cave
, x
, y
)) {
2642 /* here we remember the coordinates. */
2649 /* record coordinates of player for chasing stone */
2650 for (i
=0; i
<G_N_ELEMENTS(cave
->px
)-1; i
++) {
2651 cave
->px
[i
]=cave
->px
[i
+1];
2652 cave
->py
[i
]=cave
->py
[i
+1];
2654 cave
->px
[G_N_ELEMENTS(cave
->px
)-1]=cave
->player_x
;
2655 cave
->py
[G_N_ELEMENTS(cave
->py
)-1]=cave
->player_y
;
2659 /* update timing calculated by iterating and counting elements which
2660 were slow to process on c64 */
2661 switch (cave
->scheduling
) {
2662 case GD_SCHEDULING_MILLISECONDS
:
2663 /* cave->speed already contains the milliseconds value, do not touch it */
2666 case GD_SCHEDULING_BD1
:
2667 if (!cave
->intermission
)
2668 /* non-intermissions */
2669 cave
->speed
=(88+3.66*cave
->c64_timing
+(cave
->ckdelay
+cave
->ckdelay_extra_for_animation
)/1000);
2671 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
2672 cave
->speed
=(60+3.66*cave
->c64_timing
+(cave
->ckdelay
+cave
->ckdelay_extra_for_animation
)/1000);
2675 case GD_SCHEDULING_BD1_ATARI
:
2676 /* about 20ms/frame faster than c64 version */
2677 if (!cave
->intermission
)
2678 cave
->speed
=(74+3.2*cave
->c64_timing
+(cave
->ckdelay
)/1000); /* non-intermissions */
2680 cave
->speed
=(65+2.88*cave
->c64_timing
+(cave
->ckdelay
)/1000); /* for intermissions */
2683 case GD_SCHEDULING_BD2
:
2684 /* 60 is a guess. */
2685 cave
->speed
=MAX(60+(cave
->ckdelay
+cave
->ckdelay_extra_for_animation
)/1000, cave
->c64_timing
*20);
2688 case GD_SCHEDULING_PLCK
:
2689 /* 65 is totally empty cave in construction kit, with delay=0) */
2690 cave
->speed
=MAX(65+cave
->ckdelay
/1000, cave
->c64_timing
*20);
2693 case GD_SCHEDULING_BD2_PLCK_ATARI
:
2694 /* a really fast engine; timing works like c64 plck. */
2695 /* 40 ms was measured in the construction kit, with delay=0 */
2696 cave
->speed
=MAX(40+cave
->ckdelay
/1000, cave
->c64_timing
*20);
2699 case GD_SCHEDULING_CRDR
:
2700 if (cave
->hammered_walls_reappear
) /* this made the engine very slow. */
2701 cave
->ckdelay
+=60000;
2702 cave
->speed
=MAX(130+cave
->ckdelay
/1000, cave
->c64_timing
*20);
2705 case GD_SCHEDULING_MAX
:
2706 /* to avoid compiler warning */
2707 g_assert_not_reached();
2711 /* cave 3 sounds. precedence is controlled by the sound_play function. */
2712 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
2713 if (found_water
&& cave
->water_sound
)
2714 gd_sound_play(cave
, GD_S_WATER
);
2715 magic_sound
=cave
->magic_wall_state
==GD_MW_ACTIVE
&& cave
->magic_wall_sound
;
2716 amoeba_sound
=cave
->hatched
&& cave
->amoeba_sound
&& ((amoeba_count
>0 && cave
->amoeba_state
==GD_AM_AWAKE
) || (amoeba_2_count
>0 && cave
->amoeba_2_state
==GD_AM_AWAKE
));
2717 if (amoeba_sound
&& magic_sound
)
2718 gd_sound_play(cave
, GD_S_AMOEBA_MAGIC
);
2721 gd_sound_play(cave
, GD_S_AMOEBA
);
2724 gd_sound_play(cave
, GD_S_MAGIC_WALL
);
2726 if ((amoeba_count
>0 && cave
->amoeba_state
==GD_AM_AWAKE
)
2727 || (amoeba_2_count
>0 && cave
->amoeba_2_state
==GD_AM_AWAKE
))
2728 play_sound_of_element(cave
, O_AMOEBA
);
2729 /* pneumatic hammer sound - overrides everything. */
2730 if (cave
->pneumatic_hammer_active_delay
>0 && cave
->pneumatic_hammer_sound
)
2731 gd_sound_play(cave
, GD_S_PNEUMATIC_HAMMER
);
2733 /* CAVE VARIABLES */
2736 if ((cave
->player_state
==GD_PL_LIVING
&& cave
->player_seen_ago
>15) || cave
->kill_player
) /* check if player is alive. */
2737 cave
->player_state
=GD_PL_DIED
;
2738 if (cave
->voodoo_touched
) /* check if any voodoo exploded, and kill players the next scan if that happended. */
2739 cave
->kill_player
=TRUE
;
2742 if (cave
->amoeba_state
==GD_AM_AWAKE
) {
2743 /* check flags after evaluating. */
2744 if (amoeba_count
>=cave
->amoeba_max_count
)
2745 cave
->amoeba_state
=GD_AM_TOO_BIG
;
2746 if (amoeba_found_enclosed
)
2747 cave
->amoeba_state
=GD_AM_ENCLOSED
;
2749 /* amoeba can also be turned into diamond by magic wall */
2750 if (cave
->magic_wall_stops_amoeba
&& cave
->magic_wall_state
==GD_MW_ACTIVE
)
2751 cave
->amoeba_state
=GD_AM_ENCLOSED
;
2753 if (cave
->amoeba_2_state
==GD_AM_AWAKE
) {
2754 /* check flags after evaluating. */
2755 if (amoeba_2_count
>=cave
->amoeba_2_max_count
)
2756 cave
->amoeba_2_state
=GD_AM_TOO_BIG
;
2757 if (amoeba_2_found_enclosed
)
2758 cave
->amoeba_2_state
=GD_AM_ENCLOSED
;
2760 /* amoeba 2 can also be turned into diamond by magic wall */
2761 if (cave
->magic_wall_stops_amoeba
&& cave
->magic_wall_state
==GD_MW_ACTIVE
)
2762 cave
->amoeba_2_state
=GD_AM_ENCLOSED
;
2765 /* now check times. --------------------------- */
2766 /* decrement time if a voodoo was killed. */
2767 cave
->time
-=time_decrement_sec
*cave
->timing_factor
;
2771 /* only decrement time when player is already born. */
2772 if (cave
->hatched
) {
2773 int secondsbefore
, secondsafter
;
2775 secondsbefore
=cave
->time
/cave
->timing_factor
;
2776 cave
->time
-=cave
->speed
;
2779 secondsafter
=cave
->time
/cave
->timing_factor
;
2780 if (cave
->time
/cave
->timing_factor
<10)
2781 /* if less than 10 seconds, no walking sound, but play explosion sound */
2782 gd_sound_play(cave
, GD_S_NONE
);
2783 if (secondsbefore
!=secondsafter
)
2784 gd_cave_set_seconds_sound(cave
);
2786 /* a gravity switch was activated; seconds counting down */
2787 if (cave
->gravity_will_change
>0) {
2788 cave
->gravity_will_change
-=cave
->speed
;
2789 if (cave
->gravity_will_change
<0)
2790 cave
->gravity_will_change
=0;
2792 if (cave
->gravity_will_change
==0) {
2793 cave
->gravity
=cave
->gravity_next_direction
;
2794 if (cave
->gravity_change_sound
)
2795 gd_sound_play(cave
, GD_S_GRAVITY_CHANGE
); /* takes precedence over amoeba and magic wall sound */
2799 /* creatures direction automatically change */
2800 if (cave
->creatures_direction_will_change
>0) {
2801 cave
->creatures_direction_will_change
-=cave
->speed
;
2802 if (cave
->creatures_direction_will_change
<0)
2803 cave
->creatures_direction_will_change
=0;
2805 if (cave
->creatures_direction_will_change
==0) {
2806 if (cave
->creature_direction_auto_change_sound
)
2807 gd_sound_play(cave
, GD_S_SWITCH_CREATURES
);
2808 cave
->creatures_backwards
=!cave
->creatures_backwards
;
2809 cave
->creatures_direction_will_change
=cave
->creatures_direction_auto_change_time
*cave
->timing_factor
;
2813 /* magic wall; if active&wait or not wait for hatching */
2814 if (cave
->magic_wall_state
==GD_MW_ACTIVE
&& (cave
->hatched
|| !cave
->magic_timer_wait_for_hatching
)) {
2815 cave
->magic_wall_time
-=cave
->speed
;
2816 if (cave
->magic_wall_time
<0)
2817 cave
->magic_wall_time
=0;
2818 if (cave
->magic_wall_time
==0)
2819 cave
->magic_wall_state
=GD_MW_EXPIRED
;
2821 /* we may wait for hatching, when starting amoeba */
2822 if (cave
->amoeba_timer_started_immediately
|| (cave
->amoeba_state
==GD_AM_AWAKE
&& (cave
->hatched
|| !cave
->amoeba_timer_wait_for_hatching
))) {
2823 cave
->amoeba_time
-=cave
->speed
;
2824 if (cave
->amoeba_time
<0)
2825 cave
->amoeba_time
=0;
2826 if (cave
->amoeba_time
==0)
2827 cave
->amoeba_growth_prob
=cave
->amoeba_fast_growth_prob
;
2829 /* we may wait for hatching, when starting amoeba */
2830 if (cave
->amoeba_timer_started_immediately
|| (cave
->amoeba_2_state
==GD_AM_AWAKE
&& (cave
->hatched
|| !cave
->amoeba_timer_wait_for_hatching
))) {
2831 cave
->amoeba_2_time
-=cave
->speed
;
2832 if (cave
->amoeba_2_time
<0)
2833 cave
->amoeba_2_time
=0;
2834 if (cave
->amoeba_2_time
==0)
2835 cave
->amoeba_2_growth_prob
=cave
->amoeba_2_fast_growth_prob
;
2838 /* check for player hatching. */
2840 /* if not the c64 scheduling, but the correct frametime is used, hatching delay should always be decremented. */
2841 /* otherwise, the if (millisecs...) condition below will set this. */
2842 if (cave
->scheduling
==GD_SCHEDULING_MILLISECONDS
) { /* NON-C64 scheduling */
2843 if (cave
->hatching_delay_frame
>0) {
2844 cave
->hatching_delay_frame
--; /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
2845 if (cave
->hatching_delay_frame
==0)
2849 else { /* C64 scheduling */
2850 if (cave
->hatching_delay_time
>0) {
2851 cave
->hatching_delay_time
-=cave
->speed
; /* for c64 schedulings, hatching delay means milliseconds. */
2852 if (cave
->hatching_delay_time
<=0) {
2853 cave
->hatching_delay_time
=0;
2859 /* if decremented hatching, and it became zero: */
2860 if (start_signal
) { /* THIS IS THE CAVE START SIGNAL */
2861 cave
->hatched
=TRUE
; /* record that now the cave is in its normal state */
2863 gd_cave_count_diamonds(cave
); /* if diamonds needed is below zero, we count the available diamonds now. */
2865 /* setup direction auto change */
2866 if (cave
->creatures_direction_auto_change_time
) {
2867 cave
->creatures_direction_will_change
=cave
->creatures_direction_auto_change_time
*cave
->timing_factor
;
2869 if (cave
->creatures_direction_auto_change_on_start
)
2870 cave
->creatures_backwards
=!cave
->creatures_backwards
;
2873 gd_sound_play(cave
, GD_S_CRACK
);
2877 if (cave
->biters_wait_frame
==0)
2878 cave
->biters_wait_frame
=cave
->biter_delay_frame
;
2880 cave
->biters_wait_frame
--;
2881 /* replicators delay */
2882 if (cave
->replicators_wait_frame
==0)
2883 cave
->replicators_wait_frame
=cave
->replicator_delay_frame
;
2885 cave
->replicators_wait_frame
--;
2890 /* check if cave failed by timeout */
2891 if (cave
->player_state
==GD_PL_LIVING
&& cave
->time
==0) {
2892 gd_cave_clear_sounds(cave
);
2893 cave
->player_state
=GD_PL_TIMEOUT
;
2894 gd_sound_play(cave
, GD_S_TIMEOUT
);
2897 /* set these for drawing. */
2898 cave
->last_direction
=player_move
;
2899 /* here we remember last movements for animation. this is needed here, as animation
2900 is in sync with the game, not the keyboard directly. (for example, after exiting
2901 the cave, the player was "running" in the original, till bonus points were counted
2902 for remaining time and so on. */
2903 if (player_move
==MV_LEFT
|| player_move
==MV_UP_LEFT
|| player_move
==MV_DOWN_LEFT
)
2904 cave
->last_horizontal_direction
=MV_LEFT
;
2905 if (player_move
==MV_RIGHT
|| player_move
==MV_UP_RIGHT
|| player_move
==MV_DOWN_RIGHT
)
2906 cave
->last_horizontal_direction
=MV_RIGHT
;
2910 cave
->frame
++; /* XXX */