2009050
[gdash.git] / src / caveengine.c
blob720f43eb4cc239567d6d6de1c25fd004834afcd3
1 /*
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.
18 /* IMPORTANT NOTES */
21 * LAVA.
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.
36 #include <glib.h>
37 #include "cave.h"
38 #include "cavedb.h"
39 #include "cavesound.h"
40 #include "caveengine.h"
45 /* for gravity */
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. */
55 void
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. */
74 static void
75 play_sound_of_element(GdCave *cave, GdElement element)
77 /* stone and diamond fall sounds. */
78 switch(element) {
79 case O_NUT:
80 case O_NUT_F:
81 if (cave->nut_sound)
82 gd_sound_play(cave, GD_S_NUT);
83 break;
84 case O_STONE:
85 case O_STONE_F:
86 case O_FLYING_STONE:
87 case O_FLYING_STONE_F:
88 case O_MEGA_STONE:
89 case O_MEGA_STONE_F:
90 case O_WAITING_STONE:
91 case O_CHASING_STONE:
92 if (cave->stone_sound)
93 gd_sound_play(cave, GD_S_STONE);
94 break;
96 case O_NITRO_PACK:
97 case O_NITRO_PACK_F:
98 if (cave->nitro_sound)
99 gd_sound_play(cave, GD_S_NITRO);
100 break;
102 case O_FALLING_WALL:
103 case O_FALLING_WALL_F:
104 if (cave->falling_wall_sound)
105 gd_sound_play(cave, GD_S_FALLING_WALL);
106 break;
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);
116 break;
118 case O_DIAMOND:
119 case O_DIAMOND_F:
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);
124 break;
126 case O_BLADDER_SPENDER:
127 if (cave->bladder_spender_sound)
128 gd_sound_play(cave, GD_S_BLADDER_SPENDER);
129 break;
131 case O_PRE_CLOCK_1:
132 if (cave->bladder_convert_sound)
133 gd_sound_play(cave, GD_S_BLADDER_CONVERT);
134 break;
136 case O_SLIME:
137 if (cave->slime_sound)
138 gd_sound_play(cave, GD_S_SLIME);
139 break;
141 case O_LAVA:
142 if (cave->lava_sound)
143 gd_sound_play(cave, GD_S_LAVA);
144 break;
146 case O_ACID:
147 if (cave->acid_spread_sound)
148 gd_sound_play(cave, GD_S_ACID_SPREAD);
149 break;
151 case O_BLADDER:
152 if (cave->bladder_sound)
153 gd_sound_play(cave, GD_S_BLADDER_MOVE);
154 break;
156 case O_BITER_1:
157 case O_BITER_2:
158 case O_BITER_3:
159 case O_BITER_4:
160 if (cave->biter_sound)
161 gd_sound_play(cave, GD_S_BITER_EAT);
162 break;
164 case O_DIRT_BALL:
165 case O_DIRT_BALL_F:
166 case O_DIRT_LOOSE:
167 case O_DIRT_LOOSE_F:
168 gd_sound_play(cave, GD_S_DIRT_BALL);
169 break;
171 default:
172 /* do nothing. */
173 break;
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)
206 if (x>=cave->w) {
207 y++;
208 x-=cave->w;
209 } else
210 if (x<0) {
211 y--;
212 x+=cave->w;
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)
260 switch (slop) {
261 case MV_LEFT:
262 return (gd_elements[get_dir(cave, x, y, dir)&O_MASK].properties&P_SLOPED_LEFT)!=0;
263 case MV_RIGHT:
264 return (gd_elements[get_dir(cave, x, y, dir)&O_MASK].properties&P_SLOPED_RIGHT)!=0;
265 case MV_UP:
266 return (gd_elements[get_dir(cave, x, y, dir)&O_MASK].properties&P_SLOPED_UP)!=0;
267 case MV_DOWN:
268 return (gd_elements[get_dir(cave, x, y, dir)&O_MASK].properties&P_SLOPED_DOWN)!=0;
269 default:
270 break;
273 return FALSE;
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)
359 examined=O_DIRT;
360 if (gd_elements[e & O_MASK].properties & P_DIRT)
361 e=O_DIRT;
362 /* if the element on the map is a lava, it should be like space */
363 if (examined==O_LAVA)
364 examined=O_SPACE;
365 return e==examined;
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 */
381 static inline void
382 store(GdCave *cave, const int x, const int y, const GdElement element)
384 GdElement *e=getp(cave, x, y);
386 if (*e==O_LAVA) {
387 play_sound_of_element(cave, O_LAVA);
388 return;
390 *e=element;
393 /* store an element with SCANNED flag turned on */
394 static inline void
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 */
401 static inline void
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 */
408 static inline void
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 */
415 static inline void
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... */
423 static inline void
424 next(GdCave *cave, const int x, const int y)
426 (*getp(cave, x, y))++;
444 static void
445 cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
447 if (non_explodable (cave, x, y))
448 return;
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);
459 else
460 /* for everything else */
461 store_sc(cave, x, y, explode_to);
464 /* a creature explodes to a 3x3 something. */
465 static void
466 creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
468 int xx, yy;
470 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
471 cave->ckdelay+=1200;
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);
479 static void
480 nitro_explode(GdCave *cave, int x, int y)
482 int xx, yy;
484 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
485 cave->ckdelay+=1200;
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. */
496 static void
497 voodoo_explode(GdCave *cave, int x, int y)
499 int xx, yy;
501 /* the processing of an explosion took pretty much time: processing 3x3=9 elements */
502 cave->ckdelay+=1000;
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. */
519 static void
520 explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
522 if (non_explodable (cave, x, y))
523 return;
524 /* bomb does not explode voodoo */
525 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y)==O_VOODOO)
526 return;
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! */
533 static void
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 */
539 cave->ckdelay+=650;
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! */
549 static void
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 */
555 cave->ckdelay+=650;
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.
567 static void
568 explode(GdCave *cave, int x, int y)
570 GdElement e=get(cave, x, y)&O_MASK;
572 switch (e) {
573 case O_GHOST:
574 ghost_explode(cave, x, y);
575 break;
577 case O_BOMB_TICK_7:
578 bomb_explode(cave, x, y);
579 break;
581 case O_VOODOO:
582 voodoo_explode(cave, x, y);
583 break;
585 case O_NITRO_PACK:
586 case O_NITRO_PACK_F:
587 case O_NITRO_PACK_EXPLODE:
588 nitro_explode(cave, x, y);
589 break;
591 case O_AMOEBA_2:
592 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
593 break;
595 case O_FALLING_WALL_F:
596 creature_explode(cave, x, y, O_EXPLODE_1);
597 break;
599 case O_BUTTER_1:
600 case O_BUTTER_2:
601 case O_BUTTER_3:
602 case O_BUTTER_4:
603 creature_explode(cave, x, y, cave->butterfly_explode_to);
604 break;
606 case O_ALT_BUTTER_1:
607 case O_ALT_BUTTER_2:
608 case O_ALT_BUTTER_3:
609 case O_ALT_BUTTER_4:
610 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
611 break;
613 case O_FIREFLY_1:
614 case O_FIREFLY_2:
615 case O_FIREFLY_3:
616 case O_FIREFLY_4:
617 creature_explode(cave, x, y, cave->firefly_explode_to);
618 break;
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);
625 break;
627 case O_PLAYER:
628 case O_PLAYER_BOMB:
629 case O_PLAYER_GLUED:
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);
634 break;
636 case O_STONEFLY_1:
637 case O_STONEFLY_2:
638 case O_STONEFLY_3:
639 case O_STONEFLY_4:
640 creature_explode(cave, x, y, cave->stonefly_explode_to);
641 break;
643 case O_DRAGONFLY_1:
644 case O_DRAGONFLY_2:
645 case O_DRAGONFLY_3:
646 case O_DRAGONFLY_4:
647 creature_explode(cave, x, y, cave->dragonfly_explode_to);
648 break;
650 default:
651 g_assert_not_reached();
652 break;
656 static void inline
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.
669 static GdElement
670 player_get_element (GdCave* cave, const GdElement object)
672 int i;
674 switch (object) {
675 case O_DIAMOND_KEY:
676 cave->diamond_key_collected=TRUE;
677 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECT);
678 return O_SPACE;
680 /* KEYS AND DOORS */
681 case O_KEY_1:
682 gd_sound_play(cave, GD_S_KEY_COLLECT);
683 cave->key1++;
684 return O_SPACE;
685 case O_KEY_2:
686 gd_sound_play(cave, GD_S_KEY_COLLECT);
687 cave->key2++;
688 return O_SPACE;
689 case O_KEY_3:
690 gd_sound_play(cave, GD_S_KEY_COLLECT);
691 cave->key3++;
692 return O_SPACE;
693 case O_DOOR_1:
694 if (cave->key1==0)
695 return object;
696 gd_sound_play(cave, GD_S_DOOR_OPEN);
697 cave->key1--;
698 return O_SPACE;
699 case O_DOOR_2:
700 if (cave->key2==0)
701 return object;
702 gd_sound_play(cave, GD_S_DOOR_OPEN);
703 cave->key2--;
704 return O_SPACE;
705 case O_DOOR_3:
706 if (cave->key3==0)
707 return object;
708 gd_sound_play(cave, GD_S_DOOR_OPEN);
709 cave->key3--;
710 return O_SPACE;
712 /* SWITCHES */
713 case O_CREATURE_SWITCH: /* creatures change direction. */
714 gd_sound_play(cave, GD_S_SWITCH_CREATURES);
715 cave->creatures_backwards=!cave->creatures_backwards;
716 return object;
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;
720 return object;
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;
726 return object;
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;
730 return object;
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;
734 return object;
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;
738 return object;
740 /* USUAL STUFF */
741 case O_DIRT:
742 case O_DIRT2:
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:
749 case O_DIRT_BALL:
750 case O_DIRT_LOOSE:
751 gd_sound_play(cave, GD_S_WALK_EARTH);
752 return O_SPACE;
754 case O_SWEET:
755 gd_sound_play(cave, GD_S_SWEET_COLLECT);
756 cave->sweet_eaten=TRUE;
757 return O_SPACE;
759 case O_PNEUMATIC_HAMMER:
760 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECT);
761 cave->got_pneumatic_hammer=TRUE;
762 return O_SPACE;
764 case O_CLOCK:
765 /* bonus time */
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... */
771 return O_DIRT;
772 case O_DIAMOND:
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;
783 return O_SPACE;
784 case O_SKELETON:
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 */
789 return O_SPACE;
790 case O_OUTBOX:
791 case O_INVIS_OUTBOX:
792 cave->player_state=GD_PL_EXITED; /* player now exits the cave! */
793 return O_SPACE;
794 case O_SPACE:
795 case O_LAVA: /* player goes into lava, as if it was space */
796 gd_sound_play(cave, GD_S_WALK_EMPTY);
797 return O_SPACE;
799 default:
800 /* the object will remain there. */
801 return object;
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.
814 static gboolean
815 do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
817 int tx, ty;
819 tx=px;
820 ty=py;
822 do {
823 /* jump to next element; wrap around columns and rows. */
824 tx++;
825 if (tx>=cave->w) {
826 tx=0;
827 ty++;
828 if (ty>=cave->h)
829 ty=0;
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_.
848 static gboolean
849 do_push(GdCave *cave, int x, int y, GdDirection player_move, gboolean player_fire)
851 gboolean result;
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, ... */
855 result=FALSE;
857 switch(what) {
858 case O_WAITING_STONE:
859 case O_STONE:
860 case O_NITRO_PACK:
861 case O_CHASING_STONE:
862 case O_MEGA_STONE:
863 case O_FLYING_STONE:
864 case O_NUT:
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]) {
869 int prob;
871 prob=0;
872 /* different probabilities for different elements. */
873 switch(what) {
874 case O_WAITING_STONE:
875 prob=1000000; /* waiting stones are light, can always push */
876 break;
877 case O_CHASING_STONE:
878 if (cave->sweet_eaten) /* chasing can be pushed if player is turbo */
879 prob=1000000;
880 break;
881 case O_MEGA_STONE:
882 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten) /* mega may(!) be pushed if player is turbo */
883 prob=1000000;
884 break;
885 case O_STONE:
886 case O_NUT:
887 case O_FLYING_STONE:
888 case O_NITRO_PACK:
889 if (cave->sweet_eaten)
890 prob=cave->pushing_stone_prob_sweet*1000000; /* probability with sweet */
891 else
892 prob=cave->pushing_stone_prob*1000000; /* probability without sweet. */
893 break;
894 default:
895 g_assert_not_reached();
896 break;
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);
903 result=TRUE;
906 break;
908 case O_BLADDER:
909 case O_BLADDER_1:
910 case O_BLADDER_2:
911 case O_BLADDER_3:
912 case O_BLADDER_4:
913 case O_BLADDER_5:
914 case O_BLADDER_6:
915 case O_BLADDER_7:
916 case O_BLADDER_8:
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. */
925 /* p p g */
926 /* 2o3 | | */
927 /* 1 v v */
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. */
938 /* 3 g */
939 /* 1op <-p | */
940 /* 2 v */
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. */
950 /* 3 g */
951 /* po1 p-< | */
952 /* 2 v */
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;
962 if (result)
963 play_sound_of_element(cave, O_BLADDER);
965 break;
967 case O_BOX:
968 /* a box is only pushed with the fire pressed */
969 if (player_fire) {
970 /* but always with 100% probability */
971 switch (player_move) {
972 case MV_LEFT:
973 case MV_RIGHT:
974 case MV_UP:
975 case MV_DOWN:
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)) {
978 /* yes, so push. */
979 store_dir(cave, x, y, player_move+MV_TWICE, O_BOX);
980 result=TRUE;
981 gd_sound_play(cave, GD_S_BOX_PUSH);
983 break;
984 default:
985 /* push in no other directions possible */
986 break;
989 break;
991 /* pushing of other elements not possible */
992 default:
993 break;
996 return result;
999 /* from the key press booleans, create a direction */
1000 GdDirection
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 */
1006 if (up && right)
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;
1014 else if (up)
1015 player_move=MV_UP;
1016 else if (down)
1017 player_move=MV_DOWN;
1018 else if (left)
1019 player_move=MV_LEFT;
1020 else if (right)
1021 player_move=MV_RIGHT;
1022 else
1023 player_move=MV_STILL;
1025 return player_move;
1029 /* clear these to no sound; and they will be set during iteration. */
1030 void
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;
1038 /* process a cave. */
1039 void
1040 gd_cave_iterate(GdCave *cave, GdDirection player_move, gboolean player_fire, gboolean suicide)
1042 int x, y, i;
1043 int ymin, ymax; /* for border scan */
1044 gboolean amoeba_found_enclosed, amoeba_2_found_enclosed; /* amoeba found to be enclosed. if not, this is cleared */
1045 int amoeba_count, amoeba_2_count; /* counting the number of amoebas. after scan, check if too much */
1046 gboolean found_water; /* cave scan found water - for sound */
1047 gboolean inbox_toggle;
1048 gboolean start_signal;
1049 GdDirection grav_compat=cave->gravity_affects_all?cave->gravity:MV_DOWN; /* gravity for falling wall, bladder, ... */
1050 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1051 static const GdDirection creature_dir[]={ MV_LEFT, MV_UP, MV_RIGHT, MV_DOWN };
1052 static const GdDirection creature_chdir[]={ MV_RIGHT, MV_DOWN, MV_LEFT, MV_UP };
1053 int time_decrement_sec;
1054 GdElement biter_try[]={ O_DIRT, cave->biter_eat, O_SPACE, O_STONE }; /* biters eating elements preference, they try to go in this order */
1055 gboolean amoeba_sound, magic_sound;
1057 gd_cave_clear_sounds(cave);
1059 /* if diagonal movements not allowed, */
1060 /* horizontal movements have precedence. [BROADRIBB] */
1061 if (!cave->diagonal_movements) {
1062 switch (player_move) {
1063 case MV_UP_RIGHT:
1064 case MV_DOWN_RIGHT:
1065 player_move=MV_RIGHT;
1066 break;
1067 case MV_UP_LEFT:
1068 case MV_DOWN_LEFT:
1069 player_move=MV_LEFT;
1070 break;
1071 default:
1072 /* no correction needed */
1073 break;
1077 /* set cave get function; to implement perfect or lineshifting borders */
1078 if (cave->lineshift)
1079 cave->getp=getp_shift;
1080 else
1081 cave->getp=getp_perfect;
1083 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1084 if (cave->player_seen_ago<100)
1085 cave->player_seen_ago++;
1087 if (cave->pneumatic_hammer_active_delay>0)
1088 cave->pneumatic_hammer_active_delay--;
1090 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1091 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1092 cave->inbox_flash_toggle=!cave->inbox_flash_toggle;
1093 inbox_toggle=cave->inbox_flash_toggle;
1095 if (cave->gate_open_flash>0)
1096 cave->gate_open_flash--;
1098 /* score collected this frame */
1099 cave->score=0;
1101 /* suicide only kills the active player */
1102 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1103 /* we must check if there is a player or not - he may have exploded or something like that */
1104 if (suicide && cave->player_state==GD_PL_LIVING && is_player(cave, cave->player_x, cave->player_y))
1105 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1107 /* check for walls reappearing */
1108 if (cave->hammered_reappear)
1109 for (y=0; y<cave->h; y++)
1110 for (x=0; x<cave->w; x++) {
1111 /* timer for the cell > 0? */
1112 if (cave->hammered_reappear[y][x]>0) {
1113 /* decrease timer */
1114 cave->hammered_reappear[y][x]--;
1115 /* check if it became zero */
1116 if (cave->hammered_reappear[y][x]==0) {
1117 store(cave, x, y, O_BRICK);
1118 gd_sound_play(cave, GD_S_WALL_REAPPEAR);
1123 /* variables to check during the scan */
1124 amoeba_found_enclosed=TRUE; /* will be set to false if any of the amoeba is found free. */
1125 amoeba_2_found_enclosed=TRUE;
1126 amoeba_count=0;
1127 amoeba_2_count=0;
1128 found_water=FALSE;
1129 cave->ckdelay=0;
1130 time_decrement_sec=0;
1132 /* check whether to scan the first and last line */
1133 if (cave->border_scan_first_and_last) {
1134 ymin=0;
1135 ymax=cave->h-1;
1136 } else {
1137 ymin=1;
1138 ymax=cave->h-2;
1140 /* the cave scan routine */
1141 for (y=ymin; y<=ymax; y++)
1142 for (x=0; x<cave->w; x++) {
1143 /* if we find a scanned element, change it to the normal one, and that's all. */
1144 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1145 if (get(cave, x, y)&SCANNED) {
1146 store(cave, x, y, get(cave, x, y)& ~SCANNED);
1147 continue;
1150 /* add the ckdelay correction value for every element seen. */
1151 cave->ckdelay+=gd_elements[get(cave, x, y)].ckdelay;
1153 switch (get(cave, x, y)) {
1155 * P L A Y E R S
1157 case O_PLAYER:
1158 if (cave->kill_player) {
1159 explode (cave, x, y);
1160 break;
1162 cave->player_seen_ago=0;
1163 /* bd4 intermission caves have many players. so if one of them has exited,
1164 * do not change the flag anymore. so this if () is needed */
1165 if (cave->player_state!=GD_PL_EXITED)
1166 cave->player_state=GD_PL_LIVING;
1168 /* check for pneumatic hammer things */
1169 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right for hammer 5) stand on something */
1170 if (player_fire && cave->got_pneumatic_hammer && is_space_dir(cave, x, y, player_move)
1171 && !is_space_dir(cave, x, y, MV_DOWN)) {
1172 if (player_move==MV_LEFT && can_be_hammered_dir(cave, x, y, MV_DOWN_LEFT)) {
1173 cave->pneumatic_hammer_active_delay=cave->pneumatic_hammer_frame;
1174 store_dir(cave, x, y, MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1175 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1176 break; /* finished. */
1178 if (player_move==MV_RIGHT && can_be_hammered_dir(cave, x, y, MV_DOWN_RIGHT)) {
1179 cave->pneumatic_hammer_active_delay=cave->pneumatic_hammer_frame;
1180 store_dir(cave, x, y, MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1181 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1182 break; /* finished. */
1186 if (player_move!=MV_STILL) {
1187 /* only do every check if he is not moving */
1188 GdElement what=get_dir(cave, x, y, player_move);
1189 GdElement remains=what;
1190 gboolean push;
1192 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1193 if (what==O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1194 break;
1196 /* try to push element; if successful, break */
1197 push=do_push(cave, x, y, player_move, player_fire);
1198 if (push)
1199 remains=O_SPACE;
1200 else
1201 switch (what) {
1202 case O_BOMB:
1203 /* if its a bomb, remember he now has one. */
1204 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1205 gd_sound_play(cave, GD_S_BOMB_COLLECT);
1206 store_dir(cave, x, y, player_move, O_SPACE);
1207 if (player_fire)
1208 store(cave, x, y, O_PLAYER_BOMB);
1209 else
1210 move(cave, x, y, player_move, O_PLAYER_BOMB);
1211 break;
1213 case O_POT:
1214 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1215 if (!player_fire && !cave->gravity_switch_active && cave->skeletons_collected>=cave->skeletons_needed_for_pot) {
1216 cave->skeletons_collected-=cave->skeletons_needed_for_pot;
1217 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1218 cave->gravity_disabled=TRUE;
1220 break;
1222 case O_GRAVITY_SWITCH:
1223 /* (we cannot use player_get for this as it does not have player_move parameter) */
1224 /* only allow changing direction if the new dir is not diagonal */
1225 if (cave->gravity_switch_active && (player_move==MV_LEFT || player_move==MV_RIGHT || player_move==MV_UP || player_move==MV_DOWN)) {
1226 gd_sound_play(cave, GD_S_SWITCH_GRAVITY);
1227 cave->gravity_will_change=cave->gravity_change_time*cave->timing_factor;
1228 cave->gravity_next_direction=player_move;
1229 cave->gravity_switch_active=FALSE;
1231 break;
1233 default:
1234 /* get element - process others. if cannot get, player_get_element will return the same */
1235 remains=player_get_element (cave, what);
1236 break;
1239 if (remains!=what || remains==O_SPACE) {
1240 /* if anything changed, apply the change. */
1242 /* if snapping anything and we have snapping explosions set. but these is not true for pushing. */
1243 if (remains==O_SPACE && player_fire && !push)
1244 remains=cave->snap_element;
1245 if (remains!=O_SPACE || player_fire)
1246 /* if any other element than space, player cannot move. also if pressing fire, will not move. */
1247 store_dir(cave, x, y, player_move, remains);
1248 else
1249 /* if space remains there, the player moves. */
1250 move(cave, x, y, player_move, O_PLAYER);
1254 break;
1256 case O_PLAYER_BOMB:
1257 /* much simpler; cannot steal stones */
1258 if (cave->kill_player) {
1259 explode (cave, x, y);
1260 break;
1262 cave->player_seen_ago=0;
1263 /* bd4 intermission caves have many players. so if one of them has exited,
1264 * do not change the flag anymore. so this if () is needed */
1265 if (cave->player_state!=GD_PL_EXITED)
1266 cave->player_state=GD_PL_LIVING;
1268 if (player_move!=MV_STILL) { /* if the player does not move, nothing to do */
1269 GdElement what=get_dir(cave, x, y, player_move);
1270 GdElement remains=what;
1272 if (player_fire) {
1273 /* placing a bomb into empty space or dirt */
1274 if (is_space_dir(cave, x, y, player_move) || is_element_dir(cave, x, y, player_move, O_DIRT)) {
1275 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1276 /* placed bomb, he is normal player again */
1277 store(cave, x, y, O_PLAYER);
1278 gd_sound_play(cave, GD_S_BOMB_PLACE);
1280 break;
1283 /* pushing and collecting */
1284 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1285 if (what==O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1286 break;
1288 if (do_push(cave, x, y, player_move, FALSE)) /* player fire is false... */
1289 remains=O_SPACE;
1290 else {
1291 switch (what) {
1292 case O_GRAVITY_SWITCH:
1293 /* (we cannot use player_get for this as it does not have player_move parameter) */
1294 /* only allow changing direction if the new dir is not diagonal */
1295 if (cave->gravity_switch_active && (player_move==MV_LEFT || player_move==MV_RIGHT || player_move==MV_UP || player_move==MV_DOWN)) {
1296 gd_sound_play(cave, GD_S_SWITCH_GRAVITY);
1297 cave->gravity_will_change=cave->gravity_change_time*cave->timing_factor;
1298 cave->gravity_next_direction=player_move;
1299 cave->gravity_switch_active=FALSE;
1301 break;
1302 default:
1303 /* get element. if cannot get, player_get_element will return the same */
1304 remains=player_get_element (cave, what);
1305 break;
1309 /* if element changed, OR there is space, move. */
1310 if (remains!=what || remains==O_SPACE) {
1311 /* if anything changed, apply the change. */
1312 move(cave, x, y, player_move, O_PLAYER_BOMB);
1315 break;
1317 case O_PLAYER_STIRRING:
1318 if (cave->kill_player) {
1319 explode (cave, x, y);
1320 break;
1322 gd_sound_play(cave, GD_S_STIRRING); /* stirring sound, if no other walking sound or explosion */
1323 cave->player_seen_ago=0;
1324 /* bd4 intermission caves have many players. so if one of them has exited,
1325 * do not change the flag anymore. so this if () is needed */
1326 if (cave->player_state!=GD_PL_EXITED)
1327 cave->player_state=GD_PL_LIVING;
1328 if (player_fire) {
1329 /* player "exits" stirring the pot by pressing fire */
1330 cave->gravity_disabled=FALSE;
1331 store(cave, x, y, O_PLAYER);
1332 cave->gravity_switch_active=TRUE;
1334 break;
1336 /* player holding pneumatic hammer */
1337 case O_PLAYER_PNEUMATIC_LEFT:
1338 case O_PLAYER_PNEUMATIC_RIGHT:
1339 /* usual player stuff */
1340 if (cave->kill_player) {
1341 explode (cave, x, y);
1342 break;
1344 cave->player_seen_ago=0;
1345 if (cave->player_state!=GD_PL_EXITED)
1346 cave->player_state=GD_PL_LIVING;
1347 if (cave->pneumatic_hammer_active_delay==0) /* if hammering time is up, becomes a normal player again. */
1348 store(cave, x, y, O_PLAYER);
1349 break;
1351 /* the active pneumatic hammer itself */
1352 case O_PNEUMATIC_ACTIVE_RIGHT:
1353 case O_PNEUMATIC_ACTIVE_LEFT:
1354 if (cave->pneumatic_hammer_active_delay==0) {
1355 GdElement new_elem;
1357 store(cave, x, y, O_SPACE); /* pneumatic hammer element disappears */
1358 /* which is the new element which appears after that one is hammered? */
1359 new_elem=gd_element_get_hammered(get_dir(cave, x, y, MV_DOWN));
1360 /* if there is a new element, display it */
1361 /* O_NONE might be returned, for example if the element being hammered explodes during hammering (by a nearby explosion) */
1362 if (new_elem!=O_NONE) {
1363 store_dir(cave, x, y, MV_DOWN, new_elem);
1365 /* and if walls reappear, remember it in array */
1366 if (cave->hammered_walls_reappear) {
1367 int wall_y;
1369 wall_y=(y+1)%cave->h;
1370 cave->hammered_reappear[wall_y][x]=cave->hammered_wall_reappear_frame;
1374 break;
1378 * S T O N E S, D I A M O N D S
1380 case O_STONE: /* standing stone */
1381 case O_MEGA_STONE: /* standing mega_stone */
1382 case O_DIAMOND: /* standing diamond */
1383 case O_NUT: /* standing nut */
1384 if (!cave->gravity_disabled) {
1385 /* if gravity is enabled, the stone might fall. */
1386 GdElement falling;
1388 switch (get(cave, x, y)) {
1389 case O_STONE:
1390 falling=cave->stone_falling_effect;
1391 break;
1392 case O_MEGA_STONE:
1393 falling=O_MEGA_STONE_F;
1394 break;
1395 case O_DIAMOND:
1396 falling=cave->diamond_falling_effect;
1397 break;
1398 case O_NUT:
1399 falling=O_NUT_F;
1400 break;
1401 default:
1402 g_assert_not_reached();
1405 if (is_space_dir(cave, x, y, cave->gravity)) { /* beginning to fall */
1406 play_sound_of_element(cave, get(cave, x, y));
1407 move(cave, x, y, cave->gravity, falling);
1409 /* check if it is on a sloped element, and it can roll. */
1410 /* for example, sloped wall looks like: */
1411 /* /| */
1412 /* /_| */
1413 /* this is tagged as sloped up&left. */
1414 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1415 /* then check the direction to roll (left or right) */
1416 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1417 else if (sloped_dir(cave, x, y, cave->gravity, opposite[cave->gravity])) { /* rolling down, if sitting on a sloped object */
1418 if (sloped_dir(cave, x, y, cave->gravity, cw_fourth[cave->gravity]) && is_space_dir(cave, x, y, cw_fourth[cave->gravity]) && is_space_dir(cave, x, y, cw_eighth[cave->gravity])) {
1419 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw, so here we use cw_fourth */
1420 play_sound_of_element(cave, get(cave, x, y));
1421 move(cave, x, y, cw_fourth[cave->gravity], falling);
1423 else if (sloped_dir(cave, x, y, cave->gravity, ccw_fourth[cave->gravity]) && is_space_dir(cave, x, y, ccw_fourth[cave->gravity]) && is_space_dir(cave, x, y, ccw_eighth[cave->gravity])) {
1424 /* rolling right? */
1425 play_sound_of_element(cave, get(cave, x, y));
1426 move(cave, x, y, ccw_fourth[cave->gravity], falling);
1430 break;
1431 case O_STONE_F: /* falling stone */
1432 case O_MEGA_STONE_F: /* falling mega stone */
1433 case O_DIAMOND_F: /* falling diamond */
1434 case O_NUT_F: /* falling nut */
1435 if (!cave->gravity_disabled) {
1436 GdElement bouncing;
1438 switch (get(cave, x, y)) {
1439 case O_STONE_F: bouncing=cave->stone_bouncing_effect; break;
1440 case O_MEGA_STONE_F: bouncing=O_MEGA_STONE; break;
1441 case O_DIAMOND_F: bouncing=cave->diamond_bouncing_effect; break;
1442 case O_NUT_F: bouncing=O_NUT; break;
1443 default: g_assert_not_reached();
1446 if (is_space_dir(cave, x, y, cave->gravity)) /* falling further */
1447 move(cave, x, y, cave->gravity, get(cave, x, y));
1448 else if (get(cave, x, y)==O_DIAMOND_F && get_dir(cave, x, y, cave->gravity)==O_VOODOO && cave->voodoo_collects_diamonds) {
1449 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1450 player_get_element (cave, O_DIAMOND); /* as if player got diamond */
1451 store(cave, x, y, O_SPACE); /* diamond disappears */
1453 else if ((get(cave, x, y)==O_STONE_F || get(cave, x, y)==O_MEGA_STONE_F) && get_dir(cave, x, y, cave->gravity)==O_NUT) {
1454 /* mega stones and normal stones crack nuts */
1455 store(cave, x, y, bouncing);
1456 store_dir(cave, x, y, cave->gravity, O_NUT_EXPL_1);
1457 if (cave->nut_sound)
1458 gd_sound_play(cave, GD_S_NUT_CRACK);
1460 else if ((get(cave, x, y)==O_STONE_F || get(cave, x, y)==O_MEGA_STONE_F) && get_dir(cave, x, y, cave->gravity)==O_VOODOO && cave->voodoo_dies_by_stone) {
1461 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1462 explode_dir (cave, x, y, cave->gravity);
1464 else if (get_dir(cave, x, y, cave->gravity)==O_MAGIC_WALL) {
1465 play_sound_of_element(cave, O_DIAMOND); /* always play diamond sound */
1466 if (cave->magic_wall_state==GD_MW_DORMANT)
1467 cave->magic_wall_state=GD_MW_ACTIVE;
1468 if (cave->magic_wall_state==GD_MW_ACTIVE && is_space_dir(cave, x, y, MV_TWICE+cave->gravity)) {
1469 /* if magic wall active and place underneath, */
1470 /* it turns boulder into diamond and vice versa. or anything the effect says to do. */
1471 GdElement magic;
1473 switch (get(cave, x, y)) {
1474 case O_STONE_F:
1475 magic=cave->magic_stone_to;
1476 break;
1477 case O_MEGA_STONE_F:
1478 magic=cave->magic_mega_stone_to;
1479 break;
1480 case O_DIAMOND_F:
1481 magic=cave->magic_diamond_to;
1482 break;
1483 case O_NUT_F:
1484 magic=O_NUT_F;
1485 break;
1486 default:
1487 g_assert_not_reached();
1490 store_dir(cave, x, y, MV_TWICE+cave->gravity, magic);
1492 store(cave, x, y, O_SPACE); /* active or non-active or anything, element falling in will always disappear */
1494 else if (explodes_by_hit_dir(cave, x, y, cave->gravity))
1495 explode_dir(cave, x, y, cave->gravity);
1496 else if (sloped_dir(cave, x, y, cave->gravity, opposite[cave->gravity])) { /* sloped element, falling to left or right */
1497 if (sloped_dir(cave, x, y, cave->gravity, cw_fourth[cave->gravity]) && is_space_dir(cave, x, y, cw_eighth[cave->gravity]) && is_space_dir(cave, x, y, cw_fourth[cave->gravity])) {
1498 play_sound_of_element(cave, get(cave, x, y));
1499 move(cave, x, y, cw_fourth[cave->gravity], get(cave, x, y)); /* try to roll left first - see O_STONE to understand why cw_fourth */
1501 else if (sloped_dir(cave, x, y, cave->gravity, ccw_fourth[cave->gravity]) && is_space_dir(cave, x, y, ccw_eighth[cave->gravity]) && is_space_dir(cave, x, y, ccw_fourth[cave->gravity])) {
1502 play_sound_of_element(cave, get(cave, x, y));
1503 move(cave, x, y, ccw_fourth[cave->gravity], get(cave, x, y)); /* if not, try to roll right */
1505 else {
1506 /* cannot roll in any direction, so it stops */
1507 play_sound_of_element(cave, get(cave, x, y));
1508 store(cave, x, y, bouncing);
1511 else {
1512 /* any other element, stops */
1513 play_sound_of_element(cave, get(cave, x, y));
1514 store(cave, x, y, bouncing);
1517 break;
1519 /* DIRT BALL AND LOOSE DIRT */
1520 case O_DIRT_BALL: /* standing dirt ball */
1521 case O_DIRT_LOOSE: /* standing loose dirt */
1522 if (!cave->gravity_disabled) {
1523 /* if gravity is enabled, the stone might fall. */
1524 GdElement falling;
1526 switch (get(cave, x, y)) {
1527 case O_DIRT_BALL: falling=O_DIRT_BALL_F; break;
1528 case O_DIRT_LOOSE: falling=O_DIRT_LOOSE_F; break;
1529 default: g_assert_not_reached();
1532 if (is_space_dir(cave, x, y, cave->gravity)) { /* beginning to fall */
1533 play_sound_of_element(cave, get(cave, x, y));
1534 move(cave, x, y, cave->gravity, falling);
1536 /* check if it is on a sloped element, and it can roll. */
1537 /* for example, sloped wall looks like: */
1538 /* /| */
1539 /* /_| */
1540 /* this is tagged as sloped up&left. */
1541 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1542 /* then check the direction to roll (left or right) */
1543 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1544 else if (sloped_dir(cave, x, y, cave->gravity, opposite[cave->gravity])) { /* rolling down, if sitting on a sloped object */
1545 if (sloped_dir(cave, x, y, cave->gravity, cw_fourth[cave->gravity]) && is_space_dir(cave, x, y, cw_fourth[cave->gravity]) && is_space_dir(cave, x, y, cw_eighth[cave->gravity])) {
1546 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw, so here we use cw_fourth */
1547 play_sound_of_element(cave, get(cave, x, y));
1548 move(cave, x, y, cw_fourth[cave->gravity], falling);
1550 else if (sloped_dir(cave, x, y, cave->gravity, ccw_fourth[cave->gravity]) && is_space_dir(cave, x, y, ccw_fourth[cave->gravity]) && is_space_dir(cave, x, y, ccw_eighth[cave->gravity])) {
1551 /* rolling right? */
1552 play_sound_of_element(cave, get(cave, x, y));
1553 move(cave, x, y, ccw_fourth[cave->gravity], falling);
1557 break;
1558 case O_DIRT_BALL_F: /* falling stone */
1559 case O_DIRT_LOOSE_F: /* falling mega stone */
1560 if (!cave->gravity_disabled) {
1561 GdElement bouncing;
1563 switch (get(cave, x, y)) {
1564 case O_DIRT_BALL_F: bouncing=O_DIRT_BALL; break;
1565 case O_DIRT_LOOSE_F: bouncing=O_DIRT_LOOSE; break;
1566 default: g_assert_not_reached();
1569 if (is_space_dir(cave, x, y, cave->gravity)) /* falling further */
1570 move(cave, x, y, cave->gravity, get(cave, x, y));
1571 else if (sloped_dir(cave, x, y, cave->gravity, opposite[cave->gravity])) { /* sloped element, falling to left or right */
1572 if (sloped_dir(cave, x, y, cave->gravity, cw_fourth[cave->gravity]) && is_space_dir(cave, x, y, cw_eighth[cave->gravity]) && is_space_dir(cave, x, y, cw_fourth[cave->gravity])) {
1573 play_sound_of_element(cave, get(cave, x, y));
1574 move(cave, x, y, cw_fourth[cave->gravity], get(cave, x, y)); /* try to roll left first - see O_STONE to understand why cw_fourth */
1576 else if (sloped_dir(cave, x, y, cave->gravity, ccw_fourth[cave->gravity]) && is_space_dir(cave, x, y, ccw_eighth[cave->gravity]) && is_space_dir(cave, x, y, ccw_fourth[cave->gravity])) {
1577 play_sound_of_element(cave, get(cave, x, y));
1578 move(cave, x, y, ccw_fourth[cave->gravity], get(cave, x, y)); /* if not, try to roll right */
1580 else {
1581 /* cannot roll in any direction, so it stops */
1582 play_sound_of_element(cave, get(cave, x, y));
1583 store(cave, x, y, bouncing);
1586 else {
1587 /* any other element, stops */
1588 play_sound_of_element(cave, get(cave, x, y));
1589 store(cave, x, y, bouncing);
1592 break;
1596 * F L Y I N G S T O N E S, D I A M O N D S
1598 case O_FLYING_STONE: /* standing stone */
1599 case O_FLYING_DIAMOND: /* standing diamond */
1600 if (!cave->gravity_disabled) {
1601 GdDirection fall_dir=opposite[cave->gravity]; /* these elements fall "up" */
1602 GdElement falling; /* if gravity is enabled, the stone might fall. */
1604 switch (get(cave, x, y)) {
1605 case O_FLYING_STONE: falling=O_FLYING_STONE_F; break;
1606 case O_FLYING_DIAMOND: falling=O_FLYING_DIAMOND_F; break;
1607 default: g_assert_not_reached();
1610 if (is_space_dir(cave, x, y, fall_dir)) { /* beginning to fall */
1611 play_sound_of_element(cave, get(cave, x, y));
1612 move(cave, x, y, fall_dir, falling);
1614 /* check if it is on a sloped element, and it can roll. */
1615 /* for example, sloped wall looks like: */
1616 /* /| */
1617 /* /_| */
1618 /* this is tagged as sloped up&left. */
1619 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1620 /* then check the direction to roll (left or right) */
1621 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1622 else if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir])) { /* rolling down, if sitting on a sloped object */
1623 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) && is_space_dir(cave, x, y, cw_fourth[fall_dir]) && is_space_dir(cave, x, y, cw_eighth[fall_dir])) {
1624 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw, so here we use cw_fourth */
1625 play_sound_of_element(cave, get(cave, x, y));
1626 move(cave, x, y, cw_fourth[fall_dir], falling);
1628 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) && is_space_dir(cave, x, y, ccw_fourth[fall_dir]) && is_space_dir(cave, x, y, ccw_eighth[fall_dir])) {
1629 /* rolling right? */
1630 play_sound_of_element(cave, get(cave, x, y));
1631 move(cave, x, y, ccw_fourth[fall_dir], falling);
1635 break;
1636 case O_FLYING_STONE_F: /* falling stone */
1637 case O_FLYING_DIAMOND_F: /* falling diamond */
1638 if (!cave->gravity_disabled) {
1639 GdElement bouncing;
1640 GdDirection fall_dir=opposite[cave->gravity]; /* these elements fall "up" */
1642 switch (get(cave, x, y)) {
1643 case O_FLYING_STONE_F: bouncing=O_FLYING_STONE; break;
1644 case O_FLYING_DIAMOND_F: bouncing=O_FLYING_DIAMOND; break;
1645 default: g_assert_not_reached();
1648 if (is_space_dir(cave, x, y, fall_dir)) /* falling further */
1649 move(cave, x, y, fall_dir, get(cave, x, y));
1650 else if (get(cave, x, y)==O_FLYING_DIAMOND_F && get_dir(cave, x, y, fall_dir)==O_VOODOO && cave->voodoo_collects_diamonds) {
1651 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1652 player_get_element(cave, O_FLYING_DIAMOND); /* as if player got diamond */
1653 store(cave, x, y, O_SPACE); /* diamond disappears */
1655 else if (get(cave, x, y)==O_FLYING_STONE_F && get_dir(cave, x, y, fall_dir)==O_VOODOO && cave->voodoo_dies_by_stone) {
1656 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1657 explode_dir (cave, x, y, fall_dir);
1659 else if (get_dir(cave, x, y, fall_dir)==O_MAGIC_WALL) {
1660 play_sound_of_element(cave, O_DIAMOND); /* always play diamond sound */
1661 if (cave->magic_wall_state==GD_MW_DORMANT)
1662 cave->magic_wall_state=GD_MW_ACTIVE;
1663 if (cave->magic_wall_state==GD_MW_ACTIVE && is_space_dir(cave, x, y, MV_TWICE+fall_dir)) {
1664 /* if magic wall active and place underneath, */
1665 /* it turns boulder into diamond and vice versa. or anything the effect says to do. */
1666 GdElement magic;
1668 switch (get(cave, x, y)) {
1669 case O_FLYING_STONE_F: magic=cave->magic_flying_stone_to; break;
1670 case O_FLYING_DIAMOND_F: magic=cave->magic_flying_diamond_to; break;
1671 default: g_assert_not_reached();
1674 store_dir(cave, x, y, MV_TWICE+fall_dir, magic);
1676 store(cave, x, y, O_SPACE); /* active or non-active or anything, element falling in will always disappear */
1678 else if (explodes_by_hit_dir(cave, x, y, fall_dir))
1679 explode_dir(cave, x, y, fall_dir);
1680 else if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir])) { /* sloped element, falling to left or right */
1681 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])) {
1682 play_sound_of_element(cave, get(cave, x, y));
1683 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 */
1685 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])) {
1686 play_sound_of_element(cave, get(cave, x, y));
1687 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y)); /* if not, try to roll right */
1689 else {
1690 /* cannot roll in any direction, so it stops */
1691 play_sound_of_element(cave, get(cave, x, y));
1692 store(cave, x, y, bouncing);
1695 else {
1696 /* any other element, stops */
1697 play_sound_of_element(cave, get(cave, x, y));
1698 store(cave, x, y, bouncing);
1701 break;
1706 * N I T R O P A C K
1708 case O_NITRO_PACK: /* standing nitro pack */
1709 if (!cave->gravity_disabled) {
1710 /* if gravity is enabled, the stone might fall. */
1711 GdElement falling;
1713 falling=O_NITRO_PACK_F;
1714 if (is_space_dir(cave, x, y, cave->gravity)) { /* beginning to fall */
1715 play_sound_of_element(cave, get(cave, x, y));
1716 move(cave, x, y, cave->gravity, falling);
1718 /* check if it is on a sloped element, and it can roll. */
1719 /* for example, sloped wall looks like: */
1720 /* /| */
1721 /* /_| */
1722 /* this is tagged as sloped up&left. */
1723 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1724 /* then check the direction to roll (left or right) */
1725 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1726 else if (sloped_dir(cave, x, y, cave->gravity, opposite[cave->gravity])) { /* rolling down, if sitting on a sloped object */
1727 if (sloped_dir(cave, x, y, cave->gravity, cw_fourth[cave->gravity]) && is_space_dir(cave, x, y, cw_fourth[cave->gravity]) && is_space_dir(cave, x, y, cw_eighth[cave->gravity])) {
1728 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw, so here we use cw_fourth */
1729 play_sound_of_element(cave, get(cave, x, y));
1730 move(cave, x, y, cw_fourth[cave->gravity], falling);
1732 else if (sloped_dir(cave, x, y, cave->gravity, ccw_fourth[cave->gravity]) && is_space_dir(cave, x, y, ccw_fourth[cave->gravity]) && is_space_dir(cave, x, y, ccw_eighth[cave->gravity])) {
1733 /* rolling right? */
1734 play_sound_of_element(cave, get(cave, x, y));
1735 move(cave, x, y, ccw_fourth[cave->gravity], falling);
1739 break;
1740 case O_NITRO_PACK_F: /* falling nitro pack */
1741 if (!cave->gravity_disabled) {
1742 GdElement bouncing;
1744 bouncing=O_NITRO_PACK;
1745 if (is_space_dir(cave, x, y, cave->gravity))
1746 /* if space, falling further */
1747 move(cave, x, y, cave->gravity, get(cave, x, y));
1748 else if (is_element_dir(cave, x, y, cave->gravity, O_MAGIC_WALL)) {
1749 /* if magic wall, it transforms it. */
1750 play_sound_of_element(cave, O_DIAMOND); /* always play diamond sound */
1751 if (cave->magic_wall_state==GD_MW_DORMANT)
1752 cave->magic_wall_state=GD_MW_ACTIVE;
1753 if (cave->magic_wall_state==GD_MW_ACTIVE && is_space_dir(cave, x, y, MV_TWICE+cave->gravity))
1754 store_dir(cave, x, y, MV_TWICE+cave->gravity, cave->magic_nitro_pack_to);
1755 store(cave, x, y, O_SPACE); /* active or non-active or anything, element falling in will always disappear */
1757 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT)) {
1758 /* falling on a dirt, it does NOT explode - just stops at its place. */
1759 play_sound_of_element(cave, bouncing);
1760 store(cave, x, y, bouncing);
1762 else
1763 /* falling on any other element it explodes */
1764 explode(cave, x, y);
1766 break;
1767 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
1768 explode(cave, x, y);
1769 break;
1774 * C R E A T U R E S
1777 case O_COW_1:
1778 case O_COW_2:
1779 case O_COW_3:
1780 case O_COW_4:
1781 /* if cannot move in any direction, becomes an enclosed cow */
1782 if (!is_space_dir(cave, x, y, MV_UP) && !is_space_dir(cave, x, y, MV_DOWN)
1783 && !is_space_dir(cave, x, y, MV_LEFT) && !is_space_dir(cave, x, y, MV_RIGHT))
1784 store(cave, x, y, O_COW_ENCLOSED_1);
1785 else {
1786 /* THIS IS THE CREATURE MOVE thing copied. */
1787 const GdDirection *creature_move;
1788 gboolean ccw=rotates_ccw(cave, x, y); /* check if default is counterclockwise */
1789 GdElement base; /* base element number (which is like O_***_1) */
1790 int dir, dirn, dirp; /* direction */
1792 base=O_COW_1;
1794 dir=get(cave, x, y)-base; /* facing where */
1795 creature_move=cave->creatures_backwards? creature_chdir:creature_dir;
1797 /* now change direction if backwards */
1798 if (cave->creatures_backwards)
1799 ccw=!ccw;
1801 if (ccw) {
1802 dirn=(dir+3)&3; /* fast turn */
1803 dirp=(dir+1)&3; /* slow turn */
1804 } else {
1805 dirn=(dir+1)&3; /* fast turn */
1806 dirp=(dir+3)&3; /* slow turn */
1809 if (is_space_dir(cave, x, y, creature_move[dirn]))
1810 move(cave, x, y, creature_move[dirn], base+dirn); /* turn and move to preferred dir */
1811 else if (is_space_dir(cave, x, y, creature_move[dir]))
1812 move(cave, x, y, creature_move[dir], base+dir); /* go on */
1813 else
1814 store(cave, x, y, base+dirp); /* turn in place if nothing else possible */
1816 break;
1817 /* enclosed cows wait some time before turning to a skeleton */
1818 case O_COW_ENCLOSED_1:
1819 case O_COW_ENCLOSED_2:
1820 case O_COW_ENCLOSED_3:
1821 case O_COW_ENCLOSED_4:
1822 case O_COW_ENCLOSED_5:
1823 case O_COW_ENCLOSED_6:
1824 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))
1825 store(cave, x, y, O_COW_1);
1826 else
1827 next(cave, x, y);
1828 break;
1829 case O_COW_ENCLOSED_7:
1830 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))
1831 store(cave, x, y, O_COW_1);
1832 else
1833 store(cave, x, y, O_SKELETON);
1834 break;
1836 case O_FIREFLY_1:
1837 case O_FIREFLY_2:
1838 case O_FIREFLY_3:
1839 case O_FIREFLY_4:
1840 case O_ALT_FIREFLY_1:
1841 case O_ALT_FIREFLY_2:
1842 case O_ALT_FIREFLY_3:
1843 case O_ALT_FIREFLY_4:
1844 case O_BUTTER_1:
1845 case O_BUTTER_2:
1846 case O_BUTTER_3:
1847 case O_BUTTER_4:
1848 case O_ALT_BUTTER_1:
1849 case O_ALT_BUTTER_2:
1850 case O_ALT_BUTTER_3:
1851 case O_ALT_BUTTER_4:
1852 case O_STONEFLY_1:
1853 case O_STONEFLY_2:
1854 case O_STONEFLY_3:
1855 case O_STONEFLY_4:
1856 /* check if touches a voodoo */
1857 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)
1858 cave->voodoo_touched=TRUE;
1859 /* check if touches something bad and should explode (includes voodoo by the flags) */
1860 if (blows_up_flies_dir(cave, x, y, MV_DOWN) || blows_up_flies_dir(cave, x, y, MV_UP)
1861 || blows_up_flies_dir(cave, x, y, MV_LEFT) || blows_up_flies_dir(cave, x, y, MV_RIGHT))
1862 explode (cave, x, y);
1863 /* otherwise move */
1864 else {
1865 const GdDirection *creature_move;
1866 gboolean ccw=rotates_ccw(cave, x, y); /* check if default is counterclockwise */
1867 GdElement base; /* base element number (which is like O_***_1) */
1868 int dir, dirn, dirp; /* direction */
1870 if (get(cave, x, y)>=O_FIREFLY_1 && get(cave, x, y)<=O_FIREFLY_4)
1871 base=O_FIREFLY_1;
1872 else if (get(cave, x, y)>=O_BUTTER_1 && get(cave, x, y)<=O_BUTTER_4)
1873 base=O_BUTTER_1;
1874 else if (get(cave, x, y)>=O_STONEFLY_1 && get(cave, x, y)<=O_STONEFLY_4)
1875 base=O_STONEFLY_1;
1876 else if (get(cave, x, y)>=O_ALT_FIREFLY_1 && get(cave, x, y)<=O_ALT_FIREFLY_4)
1877 base=O_ALT_FIREFLY_1;
1878 else if (get(cave, x, y)>=O_ALT_BUTTER_1 && get(cave, x, y)<=O_ALT_BUTTER_4)
1879 base=O_ALT_BUTTER_1;
1880 else
1881 g_assert_not_reached();
1883 dir=get(cave, x, y)-base; /* facing where */
1884 creature_move=cave->creatures_backwards? creature_chdir:creature_dir;
1886 /* now change direction if backwards */
1887 if (cave->creatures_backwards)
1888 ccw=!ccw;
1890 if (ccw) {
1891 dirn=(dir+3)&3; /* fast turn */
1892 dirp=(dir+1)&3; /* slow turn */
1893 } else {
1894 dirn=(dir+1)&3; /* fast turn */
1895 dirp=(dir+3)&3; /* slow turn */
1898 if (is_space_dir(cave, x, y, creature_move[dirn]))
1899 move(cave, x, y, creature_move[dirn], base+dirn); /* turn and move to preferred dir */
1900 else if (is_space_dir(cave, x, y, creature_move[dir]))
1901 move(cave, x, y, creature_move[dir], base+dir); /* go on */
1902 else
1903 store(cave, x, y, base+dirp); /* turn in place if nothing else possible */
1905 break;
1907 case O_WAITING_STONE:
1908 if (is_space_dir(cave, x, y, grav_compat)) { /* beginning to fall */
1909 /* it wakes up. */
1910 move(cave, x, y, grav_compat, O_CHASING_STONE);
1912 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat])) { /* rolling down a brick wall or a stone */
1913 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])) {
1914 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
1915 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
1917 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])) {
1918 /* or maybe right */
1919 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
1922 break;
1924 case O_CHASING_STONE:
1926 int px=cave->px[0];
1927 int py=cave->py[0];
1928 gboolean horizontal=g_rand_boolean(cave->random);
1929 gboolean dont_move=FALSE;
1930 int i=3;
1932 /* try to move... */
1933 while (1) {
1934 if (horizontal) { /*********************************/
1935 /* check for a horizontal movement */
1936 if (px==x) {
1937 /* if coordinates are the same */
1938 i-=1;
1939 horizontal=!horizontal;
1940 if (i==2)
1941 continue;
1942 } else {
1943 if (px>x && is_space_dir(cave, x, y, MV_RIGHT)) {
1944 move(cave, x, y, MV_RIGHT, O_CHASING_STONE);
1945 dont_move=TRUE;
1946 break;
1947 } else
1948 if (px<x && is_space_dir(cave, x, y, MV_LEFT)) {
1949 move(cave, x, y, MV_LEFT, O_CHASING_STONE);
1950 dont_move=TRUE;
1951 break;
1952 } else {
1953 i-=2;
1954 if (i==1) {
1955 horizontal=!horizontal;
1956 continue;
1960 } else { /********************************/
1961 /* check for a vertical movement */
1962 if (py==y) {
1963 /* if coordinates are the same */
1964 i-=1;
1965 horizontal=!horizontal;
1966 if (i==2)
1967 continue;
1968 } else {
1969 if (py>y && is_space_dir(cave, x, y, MV_DOWN)) {
1970 move(cave, x, y, MV_DOWN, O_CHASING_STONE);
1971 dont_move=TRUE;
1972 break;
1973 } else
1974 if (py<y && is_space_dir(cave, x, y, MV_UP)) {
1975 move(cave, x, y, MV_UP, O_CHASING_STONE);
1976 dont_move=TRUE;
1977 break;
1978 } else {
1979 i-=2;
1980 if (i==1) {
1981 horizontal=!horizontal;
1982 continue;
1987 if (i!=0)
1988 dont_move=TRUE;
1989 break;
1992 /* if we should move in both directions, but can not move in any, stop. */
1993 if (!dont_move) {
1994 if (horizontal) { /* check for horizontal */
1995 if (x>=px) {
1996 if (is_space_dir(cave, x, y, MV_UP) && is_space_dir(cave, x, y, MV_UP_LEFT))
1997 move(cave, x, y, MV_UP, O_CHASING_STONE);
1998 else
1999 if (is_space_dir(cave, x, y, MV_DOWN) && is_space_dir(cave, x, y, MV_DOWN_LEFT))
2000 move(cave, x, y, MV_DOWN, O_CHASING_STONE);
2001 } else {
2002 if (is_space_dir(cave, x, y, MV_UP) && is_space_dir(cave, x, y, MV_UP_RIGHT))
2003 move(cave, x, y, MV_UP, O_CHASING_STONE);
2004 else
2005 if (is_space_dir(cave, x, y, MV_DOWN) && is_space_dir(cave, x, y, MV_DOWN_RIGHT))
2006 move(cave, x, y, MV_DOWN, O_CHASING_STONE);
2008 } else { /* check for vertical */
2009 if (y>=py) {
2010 if (is_space_dir(cave, x, y, MV_LEFT) && is_space_dir(cave, x, y, MV_UP_LEFT))
2011 move(cave, x, y, MV_LEFT, O_CHASING_STONE);
2012 else
2013 if (is_space_dir(cave, x, y, MV_RIGHT) && is_space_dir(cave, x, y, MV_UP_RIGHT))
2014 move(cave, x, y, MV_RIGHT, O_CHASING_STONE);
2015 } else {
2016 if (is_space_dir(cave, x, y, MV_LEFT) && is_space_dir(cave, x, y, MV_DOWN_LEFT))
2017 move(cave, x, y, MV_LEFT, O_CHASING_STONE);
2018 else
2019 if (is_space_dir(cave, x, y, MV_RIGHT) && is_space_dir(cave, x, y, MV_DOWN_RIGHT))
2020 move(cave, x, y, MV_RIGHT, O_CHASING_STONE);
2025 break;
2027 case O_REPLICATOR:
2028 if (cave->replicators_wait_frame==0 && cave->replicators_active && !cave->gravity_disabled) {
2029 /* only replicate, if space is under it. */
2030 /* do not replicate players! */
2031 /* also obeys gravity settings. */
2032 /* only replicate element if it is not a scanned one */
2033 /* do not replicate space... that condition looks like it makes no sense,
2034 but otherwise it generates SCANNED spaces, which cannot be "collected" by the player, so he cannot run under a replicator */
2035 if (is_space_dir(cave, x, y, cave->gravity) && !is_player_dir(cave, x, y, opposite[cave->gravity])
2036 && !is_space_dir(cave, x, y, opposite[cave->gravity])) {
2037 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2038 gd_sound_play(cave, GD_S_REPLICATOR);
2041 break;
2043 case O_BITER_1:
2044 case O_BITER_2:
2045 case O_BITER_3:
2046 case O_BITER_4:
2047 if (cave->biters_wait_frame==0) {
2048 static GdDirection biter_move[]={ MV_UP, MV_RIGHT, MV_DOWN, MV_LEFT };
2049 int dir=get(cave, x, y)-O_BITER_1; /* direction, last two bits 0..3 */
2050 int dirn=(dir+3)&3;
2051 int dirp=(dir+1)&3;
2052 int i;
2053 GdElement made_sound_of=O_NONE;
2055 for (i=0; i<G_N_ELEMENTS (biter_try); i++) {
2056 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i])) {
2057 move(cave, x, y, biter_move[dir], O_BITER_1+dir);
2058 if (biter_try[i]!=O_SPACE)
2059 made_sound_of=O_BITER_1; /* sound of a biter eating */
2060 break;
2062 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i])) {
2063 move(cave, x, y, biter_move[dirn], O_BITER_1+dirn);
2064 if (biter_try[i]!=O_SPACE)
2065 made_sound_of=O_BITER_1; /* sound of a biter eating */
2066 break;
2068 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i])) {
2069 move(cave, x, y, biter_move[dirp], O_BITER_1+dirp);
2070 if (biter_try[i]!=O_SPACE)
2071 made_sound_of=O_BITER_1; /* sound of a biter eating */
2072 break;
2075 if (i==G_N_ELEMENTS (biter_try))
2076 /* i=number of elements in array: could not move, so just turn */
2077 store(cave, x, y, O_BITER_1+dirp);
2078 else if (biter_try[i]==O_STONE) {
2079 /* if there was a stone there, where we moved... do not eat stones, just throw them back */
2080 store(cave, x, y, O_STONE);
2081 made_sound_of=O_STONE;
2084 /* if biter did move, we had sound. play it. */
2085 if (made_sound_of!=O_NONE)
2086 play_sound_of_element(cave, made_sound_of);
2088 break;
2090 case O_DRAGONFLY_1:
2091 case O_DRAGONFLY_2:
2092 case O_DRAGONFLY_3:
2093 case O_DRAGONFLY_4:
2094 /* check if touches a voodoo */
2095 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)
2096 cave->voodoo_touched=TRUE;
2097 /* check if touches something bad and should explode (includes voodoo by the flags) */
2098 if (blows_up_flies_dir(cave, x, y, MV_DOWN) || blows_up_flies_dir(cave, x, y, MV_UP)
2099 || blows_up_flies_dir(cave, x, y, MV_LEFT) || blows_up_flies_dir(cave, x, y, MV_RIGHT))
2100 explode (cave, x, y);
2101 /* otherwise move */
2102 else {
2103 const GdDirection *creature_move;
2104 gboolean ccw=rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2105 GdElement base=O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2106 int dir, dirn; /* direction */
2108 dir=get(cave, x, y)-base; /* facing where */
2109 creature_move=cave->creatures_backwards? creature_chdir:creature_dir;
2111 /* now change direction if backwards */
2112 if (cave->creatures_backwards)
2113 ccw=!ccw;
2115 if (ccw)
2116 dirn=(dir+3)&3; /* fast turn */
2117 else
2118 dirn=(dir+1)&3; /* fast turn */
2120 /* if can move forward, does so. */
2121 if (is_space_dir(cave, x, y, creature_move[dir]))
2122 move(cave, x, y, creature_move[dir], base+dir);
2123 else
2124 /* otherwise turns 90 degrees in place. */
2125 store(cave, x, y, base+dirn);
2127 break;
2130 case O_BLADDER:
2131 store(cave, x, y, O_BLADDER_1);
2132 break;
2134 case O_BLADDER_1:
2135 case O_BLADDER_2:
2136 case O_BLADDER_3:
2137 case O_BLADDER_4:
2138 case O_BLADDER_5:
2139 case O_BLADDER_6:
2140 case O_BLADDER_7:
2141 case O_BLADDER_8:
2142 /* bladder with any delay state: try to convert to clock. */
2143 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by)
2144 || is_element_dir(cave, x, y, cw_fourth[grav_compat], cave->bladder_converts_by)
2145 || is_element_dir(cave, x, y, ccw_fourth[grav_compat], cave->bladder_converts_by)) {
2146 /* if touches the specified element, let it be a clock */
2147 store(cave, x, y, O_PRE_CLOCK_1);
2148 play_sound_of_element(cave, O_PRE_CLOCK_1); /* plays the bladder convert sound */
2149 } else {
2150 /* is space over the bladder? */
2151 if (is_space_dir(cave, x, y, opposite[grav_compat])) {
2152 if (get(cave, x, y)==O_BLADDER_8) {
2153 /* if it is a bladder 8, really move up */
2154 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2155 play_sound_of_element(cave, O_BLADDER);
2157 else
2158 /* if smaller delay, just increase delay. */
2159 next(cave, x, y);
2161 else
2162 /* if not space, is something sloped over the bladder? */
2163 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) && sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat])) {
2164 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]])
2165 && is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]])
2166 && is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]])) {
2167 /* rolling up, to left */
2168 if (get(cave, x, y)==O_BLADDER_8) {
2169 /* if it is a bladder 8, really roll */
2170 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2171 play_sound_of_element(cave, O_BLADDER);
2172 } else
2173 /* if smaller delay, just increase delay. */
2174 next(cave, x, y);
2176 else
2177 if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]])
2178 && is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]])
2179 && is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]])) {
2180 /* rolling up, to left */
2181 if (get(cave, x, y)==O_BLADDER_8) {
2182 /* if it is a bladder 8, really roll */
2183 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2184 play_sound_of_element(cave, O_BLADDER);
2185 } else
2186 /* if smaller delay, just increase delay. */
2187 next(cave, x, y);
2190 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2191 else
2192 store(cave, x, y, O_BLADDER_1);
2194 break;
2196 case O_GHOST:
2197 if (blows_up_flies_dir(cave, x, y, MV_DOWN) || blows_up_flies_dir(cave, x, y, MV_UP)
2198 || blows_up_flies_dir(cave, x, y, MV_LEFT) || blows_up_flies_dir(cave, x, y, MV_RIGHT))
2199 explode (cave, x, y);
2200 else {
2201 int i;
2203 /* the ghost is given four possibilities to move. */
2204 for (i=0; i<4; i++) {
2205 static GdDirection dirs[]={MV_UP, MV_DOWN, MV_LEFT, MV_RIGHT};
2206 GdDirection random_dir;
2208 random_dir=dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2209 if (is_space_dir(cave, x, y, random_dir)) {
2210 move(cave, x, y, random_dir, O_GHOST);
2211 break; /* ghost did move -> exit loop */
2215 break;
2220 * A C T I V E E L E M E N T S
2223 case O_AMOEBA:
2224 amoeba_count++;
2225 switch (cave->amoeba_state) {
2226 case GD_AM_TOO_BIG:
2227 store(cave, x, y, cave->amoeba_too_big_effect);
2228 break;
2229 case GD_AM_ENCLOSED:
2230 store(cave, x, y, cave->amoeba_enclosed_effect);
2231 break;
2232 case GD_AM_SLEEPING:
2233 case GD_AM_AWAKE:
2234 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2235 if (amoeba_found_enclosed)
2236 /* if still found enclosed, check all four directions, if this one is able to grow. */
2237 if (amoeba_eats_dir(cave, x, y, MV_UP) || amoeba_eats_dir(cave, x, y, MV_DOWN)
2238 || amoeba_eats_dir(cave, x, y, MV_LEFT) || amoeba_eats_dir(cave, x, y, MV_RIGHT)) {
2239 amoeba_found_enclosed=FALSE; /* not enclosed. this is a local (per scan) flag! */
2240 cave->amoeba_state=GD_AM_AWAKE;
2243 /* if alive, check in which dir to grow (or not) */
2244 if (cave->amoeba_state==GD_AM_AWAKE) {
2245 if (g_rand_int_range(cave->random, 0, 1000000)<cave->amoeba_growth_prob*1000000) {
2246 switch (g_rand_int_range(cave->random, 0, 4)) { /* decided to grow, choose a random direction. */
2247 case 0: /* let this be up. numbers indifferent. */
2248 if (amoeba_eats_dir(cave, x, y, MV_UP))
2249 store_dir(cave, x, y, MV_UP, O_AMOEBA);
2250 break;
2251 case 1: /* down */
2252 if (amoeba_eats_dir(cave, x, y, MV_DOWN))
2253 store_dir(cave, x, y, MV_DOWN, O_AMOEBA);
2254 break;
2255 case 2: /* left */
2256 if (amoeba_eats_dir(cave, x, y, MV_LEFT))
2257 store_dir(cave, x, y, MV_LEFT, O_AMOEBA);
2258 break;
2259 case 3: /* right */
2260 if (amoeba_eats_dir(cave, x, y, MV_RIGHT))
2261 store_dir(cave, x, y, MV_RIGHT, O_AMOEBA);
2262 break;
2266 break;
2268 break;
2270 case O_AMOEBA_2:
2271 amoeba_2_count++;
2272 /* check if it is touching an amoeba, and explosion is enabled */
2273 if (cave->amoeba_2_explodes_by_amoeba
2274 && (is_element_dir(cave, x, y, MV_DOWN, O_AMOEBA) || is_element_dir(cave, x, y, MV_UP, O_AMOEBA)
2275 || is_element_dir(cave, x, y, MV_LEFT, O_AMOEBA) || is_element_dir(cave, x, y, MV_RIGHT, O_AMOEBA)))
2276 explode (cave, x, y);
2277 else
2278 switch (cave->amoeba_2_state) {
2279 case GD_AM_TOO_BIG:
2280 store(cave, x, y, cave->amoeba_2_too_big_effect);
2281 break;
2282 case GD_AM_ENCLOSED:
2283 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2284 break;
2285 case GD_AM_SLEEPING:
2286 case GD_AM_AWAKE:
2287 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2288 if (amoeba_2_found_enclosed)
2289 if (amoeba_eats_dir(cave, x, y, MV_UP) || amoeba_eats_dir(cave, x, y, MV_DOWN)
2290 || amoeba_eats_dir(cave, x, y, MV_LEFT) || amoeba_eats_dir(cave, x, y, MV_RIGHT)) {
2291 amoeba_2_found_enclosed=FALSE; /* not enclosed. this is a local (per scan) flag! */
2292 cave->amoeba_2_state=GD_AM_AWAKE;
2295 if (cave->amoeba_2_state==GD_AM_AWAKE) /* if it is alive, decide if it attempts to grow */
2296 if (g_rand_int_range(cave->random, 0, 1000000)<cave->amoeba_2_growth_prob*1000000) {
2297 switch (g_rand_int_range(cave->random, 0, 4)) { /* decided to grow, choose a random direction. */
2298 case 0: /* let this be up. numbers indifferent. */
2299 if (amoeba_eats_dir(cave, x, y, MV_UP))
2300 store_dir(cave, x, y, MV_UP, O_AMOEBA_2);
2301 break;
2302 case 1: /* down */
2303 if (amoeba_eats_dir(cave, x, y, MV_DOWN))
2304 store_dir(cave, x, y, MV_DOWN, O_AMOEBA_2);
2305 break;
2306 case 2: /* left */
2307 if (amoeba_eats_dir(cave, x, y, MV_LEFT))
2308 store_dir(cave, x, y, MV_LEFT, O_AMOEBA_2);
2309 break;
2310 case 3: /* right */
2311 if (amoeba_eats_dir(cave, x, y, MV_RIGHT))
2312 store_dir(cave, x, y, MV_RIGHT, O_AMOEBA_2);
2313 break;
2316 break;
2318 break;
2320 case O_ACID:
2321 /* choose randomly, if it spreads */
2322 if (g_rand_int_range(cave->random, 0, 1000000)<=cave->acid_spread_ratio*1000000) {
2323 /* the current one explodes */
2324 store(cave, x, y, cave->acid_turns_to);
2325 /* and if neighbours are eaten, put acid there. */
2326 if (is_element_dir(cave, x, y, MV_UP, cave->acid_eats_this)) {
2327 play_sound_of_element(cave, O_ACID);
2328 store_dir(cave, x, y, MV_UP, O_ACID);
2330 if (is_element_dir(cave, x, y, MV_DOWN, cave->acid_eats_this)) {
2331 play_sound_of_element(cave, O_ACID);
2332 store_dir(cave, x, y, MV_DOWN, O_ACID);
2334 if (is_element_dir(cave, x, y, MV_LEFT, cave->acid_eats_this)) {
2335 play_sound_of_element(cave, O_ACID);
2336 store_dir(cave, x, y, MV_LEFT, O_ACID);
2338 if (is_element_dir(cave, x, y, MV_RIGHT, cave->acid_eats_this)) {
2339 play_sound_of_element(cave, O_ACID);
2340 store_dir(cave, x, y, MV_RIGHT, O_ACID);
2343 break;
2345 case O_WATER:
2346 found_water=TRUE;
2347 if (!cave->water_does_not_flow_down && is_space_dir(cave, x, y, MV_DOWN)) /* emulating the odd behaviour in crdr */
2348 store_dir(cave, x, y, MV_DOWN, O_WATER_1);
2349 if (is_space_dir(cave, x, y, MV_UP))
2350 store_dir(cave, x, y, MV_UP, O_WATER_1);
2351 if (is_space_dir(cave, x, y, MV_LEFT))
2352 store_dir(cave, x, y, MV_LEFT, O_WATER_1);
2353 if (is_space_dir(cave, x, y, MV_RIGHT))
2354 store_dir(cave, x, y, MV_RIGHT, O_WATER_1);
2355 break;
2357 case O_WATER_16:
2358 store(cave, x, y, O_WATER);
2359 break;
2361 case O_H_EXPANDING_WALL:
2362 case O_V_EXPANDING_WALL:
2363 case O_H_EXPANDING_STEEL_WALL:
2364 case O_V_EXPANDING_STEEL_WALL:
2365 /* checks first if direction is changed. */
2366 if (((get(cave, x, y)==O_H_EXPANDING_WALL || get(cave, x, y)==O_H_EXPANDING_STEEL_WALL) && !cave->expanding_wall_changed)
2367 || ((get(cave, x, y)==O_V_EXPANDING_WALL || get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) && cave->expanding_wall_changed)) {
2368 if (is_space_dir(cave, x, y, MV_LEFT)) {
2369 store_dir(cave, x, y, MV_LEFT, get(cave, x, y));
2370 play_sound_of_element(cave, get(cave, x, y));
2372 if (is_space_dir(cave, x, y, MV_RIGHT)) {
2373 store_dir(cave, x, y, MV_RIGHT, get(cave, x, y));
2374 play_sound_of_element(cave, get(cave, x, y));
2377 else {
2378 if (is_space_dir(cave, x, y, MV_UP)) {
2379 store_dir(cave, x, y, MV_UP, get(cave, x, y));
2380 play_sound_of_element(cave, get(cave, x, y));
2382 if (is_space_dir(cave, x, y, MV_DOWN)) {
2383 store_dir(cave, x, y, MV_DOWN, get(cave, x, y));
2384 play_sound_of_element(cave, get(cave, x, y));
2387 break;
2389 case O_EXPANDING_WALL:
2390 case O_EXPANDING_STEEL_WALL:
2391 /* the wall which grows in all four directions. */
2392 if (is_space_dir(cave, x, y, MV_LEFT)) {
2393 store_dir(cave, x, y, MV_LEFT, get(cave, x, y));
2394 play_sound_of_element(cave, get(cave, x, y));
2396 if (is_space_dir(cave, x, y, MV_RIGHT)) {
2397 store_dir(cave, x, y, MV_RIGHT, get(cave, x, y));
2398 play_sound_of_element(cave, get(cave, x, y));
2400 if (is_space_dir(cave, x, y, MV_UP)) {
2401 store_dir(cave, x, y, MV_UP, get(cave, x, y));
2402 play_sound_of_element(cave, get(cave, x, y));
2404 if (is_space_dir(cave, x, y, MV_DOWN)) {
2405 store_dir(cave, x, y, MV_DOWN, get(cave, x, y));
2406 play_sound_of_element(cave, get(cave, x, y));
2408 break;
2410 case O_SLIME:
2412 * unpredictable: g_rand_int
2413 * predictable: c64 predictable random generator.
2414 * for predictable, a random number is generated, whether or not it is even possible that the stone
2415 * will be able to pass.
2417 if (cave->slime_predictable? ((gd_cave_c64_random (cave)&cave->slime_permeability_c64)==0) : g_rand_int_range(cave->random, 0, 1000000)<cave->slime_permeability*1000000) {
2418 GdDirection grav=cave->gravity;
2419 GdDirection oppos=opposite[cave->gravity];
2421 /* space under the slime? elements may pass from top to bottom then. */
2422 if (is_space_dir(cave, x, y, grav)) {
2423 if (get_dir(cave, x, y, oppos)==cave->slime_eats_1) {
2424 store_dir(cave, x, y, grav, cave->slime_converts_1); /* output a falling xy under */
2425 store_dir(cave, x, y, oppos, O_SPACE);
2426 play_sound_of_element(cave, O_SLIME);
2428 else if (get_dir(cave, x, y, oppos)==cave->slime_eats_2) {
2429 store_dir(cave, x, y, grav, cave->slime_converts_2);
2430 store_dir(cave, x, y, oppos, O_SPACE);
2431 play_sound_of_element(cave, O_SLIME);
2433 else if (get_dir(cave, x, y, oppos)==O_WAITING_STONE) { /* waiting stones pass without awakening */
2434 store_dir(cave, x, y, grav, O_WAITING_STONE);
2435 store_dir(cave, x, y, oppos, O_SPACE);
2436 play_sound_of_element(cave, O_SLIME);
2438 else if (get_dir(cave, x, y, oppos)==O_CHASING_STONE) { /* chasing stones pass */
2439 store_dir(cave, x, y, grav, O_CHASING_STONE);
2440 store_dir(cave, x, y, oppos, O_SPACE);
2441 play_sound_of_element(cave, O_SLIME);
2443 } else
2444 /* or space over the slime? elements may pass from bottom to up then. */
2445 if (is_space_dir(cave, x, y, oppos)) {
2446 if (get_dir(cave, x, y, grav)==O_BLADDER) { /* bladders move UP the slime */
2447 store_dir(cave, x, y, grav, O_SPACE);
2448 store_dir(cave, x, y, oppos, O_BLADDER_1);
2449 play_sound_of_element(cave, O_SLIME);
2450 } else
2451 if (get_dir(cave, x, y, grav)==O_FLYING_STONE) {
2452 store_dir(cave, x, y, grav, O_SPACE);
2453 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
2454 play_sound_of_element(cave, O_SLIME);
2455 } else
2456 if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND) {
2457 store_dir(cave, x, y, grav, O_SPACE);
2458 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
2459 play_sound_of_element(cave, O_SLIME);
2463 break;
2465 case O_FALLING_WALL:
2466 if (is_space_dir(cave, x, y, grav_compat)) {
2467 /* try falling if space under. */
2468 int yy;
2469 for (yy=y+1; yy<y+cave->h; yy++)
2470 /* yy<y+cave->h is to check everything OVER the wall - since caves wrap around !! */
2471 if (get(cave, x, yy)!=O_SPACE)
2472 /* stop cycle when other than space */
2473 break;
2474 /* if scanning stopped by a player... start falling! */
2475 if (get(cave, x, yy)==O_PLAYER || get(cave, x, yy)==O_PLAYER_GLUED || get(cave, x, yy)==O_PLAYER_BOMB) {
2476 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2477 /* no sound when the falling wall starts falling! */
2480 break;
2482 case O_FALLING_WALL_F:
2483 switch (get_dir(cave, x, y, grav_compat)) {
2484 case O_PLAYER:
2485 case O_PLAYER_GLUED:
2486 case O_PLAYER_BOMB:
2487 /* if player under, it explodes - the falling wall, not the player! */
2488 explode (cave, x, y);
2489 break;
2490 case O_SPACE:
2491 /* continue falling */
2492 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2493 break;
2494 default:
2495 /* stop */
2496 play_sound_of_element(cave, get(cave, x, y));
2497 store(cave, x, y, O_FALLING_WALL);
2498 break;
2500 break;
2504 * C O N V E Y O R B E L T S
2506 case O_CONVEYOR_RIGHT:
2507 case O_CONVEYOR_LEFT:
2508 /* only works if gravity is up or down!!! */
2509 /* first, check for gravity and running belts. */
2510 if (!cave->gravity_disabled && cave->conveyor_belts_active) {
2511 const GdDirection *dir;
2512 gboolean left;
2514 /* decide direction */
2515 left=get(cave, x, y)!=O_CONVEYOR_RIGHT;
2516 if (cave->conveyor_belts_direction_changed)
2517 left=!left;
2518 dir=left?ccw_eighth:cw_eighth;
2520 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
2521 /* if gravity is normal, and the conveyor belt has something ABOVE which can be moved
2523 the gravity is up, so anything that should float now goes DOWN and touches the conveyor */
2524 if ((cave->gravity==MV_DOWN && moved_by_conveyor_top_dir(cave, x, y, MV_UP))
2525 || (cave->gravity==MV_UP && moved_by_conveyor_bottom_dir(cave, x, y, MV_UP))) {
2526 if (!is_scanned_dir(cave, x, y, MV_UP) && is_space_dir(cave, x, y, dir[MV_UP]))
2528 store_dir(cave, x, y, dir[MV_UP], get_dir(cave, x, y, MV_UP)); /* move */
2529 store_dir(cave, x, y, MV_UP, O_SPACE); /* and place a space. */
2532 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
2533 if ((cave->gravity==MV_UP && moved_by_conveyor_top_dir(cave, x, y, MV_DOWN))
2534 || (cave->gravity==MV_DOWN && moved_by_conveyor_bottom_dir(cave, x, y, MV_DOWN))) {
2535 if (!is_scanned_dir(cave, x, y, MV_DOWN) && is_space_dir(cave, x, y, dir[MV_DOWN]))
2537 store_dir(cave, x, y, dir[MV_DOWN], get_dir(cave, x, y, MV_DOWN)); /* move */
2538 store_dir(cave, x, y, MV_DOWN, O_SPACE); /* and clear. */
2542 break;
2545 * S I M P L E C H A N G I N G; E X P L O S I O N S
2547 case O_EXPLODE_5:
2548 store(cave, x, y, cave->explosion_effect);
2549 break;
2550 case O_NUT_EXPL_4:
2551 store(cave, x, y, O_DIAMOND);
2552 break;
2553 case O_PRE_DIA_5:
2554 store(cave, x, y, cave->diamond_birth_effect);
2555 break;
2556 case O_PRE_STONE_4:
2557 store(cave, x, y, O_STONE);
2558 break;
2560 case O_NITRO_EXPL_4:
2561 store(cave, x, y, cave->nitro_explosion_effect);
2562 break;
2563 case O_BOMB_EXPL_4:
2564 store(cave, x, y, cave->bomb_explosion_effect);
2565 break;
2566 case O_AMOEBA_2_EXPL_4:
2567 store(cave, x, y, cave->amoeba_2_explosion_effect);
2568 break;
2570 case O_GHOST_EXPL_4:
2572 static GdElement ghost_explode[]={
2573 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
2574 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
2575 O_WAITING_STONE, O_BITER_1
2578 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
2580 break;
2581 case O_PRE_STEEL_4:
2582 store(cave, x, y, O_STEEL);
2583 break;
2584 case O_PRE_CLOCK_4:
2585 store(cave, x, y, O_CLOCK);
2586 break;
2587 case O_BOMB_TICK_7:
2588 explode(cave, x, y);
2589 break;
2591 case O_TRAPPED_DIAMOND:
2592 if (cave->diamond_key_collected)
2593 store(cave, x, y, O_DIAMOND);
2594 break;
2596 case O_PRE_OUTBOX:
2597 if (cave->gate_open) /* if no more diamonds needed */
2598 store(cave, x, y, O_OUTBOX); /* open outbox */
2599 break;
2600 case O_PRE_INVIS_OUTBOX:
2601 if (cave->gate_open) /* if no more diamonds needed */
2602 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
2603 break;
2604 case O_INBOX:
2605 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
2606 store(cave, x, y, O_PRE_PL_1);
2607 inbox_toggle=!inbox_toggle;
2608 break;
2609 case O_PRE_PL_3:
2610 store(cave, x, y, O_PLAYER);
2611 break;
2613 case O_PRE_DIA_1:
2614 case O_PRE_DIA_2:
2615 case O_PRE_DIA_3:
2616 case O_PRE_DIA_4:
2617 case O_PRE_STONE_1:
2618 case O_PRE_STONE_2:
2619 case O_PRE_STONE_3:
2620 case O_BOMB_TICK_1:
2621 case O_BOMB_TICK_2:
2622 case O_BOMB_TICK_3:
2623 case O_BOMB_TICK_4:
2624 case O_BOMB_TICK_5:
2625 case O_BOMB_TICK_6:
2626 case O_PRE_STEEL_1:
2627 case O_PRE_STEEL_2:
2628 case O_PRE_STEEL_3:
2629 case O_BOMB_EXPL_1:
2630 case O_BOMB_EXPL_2:
2631 case O_BOMB_EXPL_3:
2632 case O_NUT_EXPL_1:
2633 case O_NUT_EXPL_2:
2634 case O_NUT_EXPL_3:
2635 case O_GHOST_EXPL_1:
2636 case O_GHOST_EXPL_2:
2637 case O_GHOST_EXPL_3:
2638 case O_EXPLODE_1:
2639 case O_EXPLODE_2:
2640 case O_EXPLODE_3:
2641 case O_EXPLODE_4:
2642 case O_PRE_PL_1:
2643 case O_PRE_PL_2:
2644 case O_PRE_CLOCK_1:
2645 case O_PRE_CLOCK_2:
2646 case O_PRE_CLOCK_3:
2647 case O_NITRO_EXPL_1:
2648 case O_NITRO_EXPL_2:
2649 case O_NITRO_EXPL_3:
2650 case O_AMOEBA_2_EXPL_1:
2651 case O_AMOEBA_2_EXPL_2:
2652 case O_AMOEBA_2_EXPL_3:
2653 /* simply the next identifier */
2654 next(cave, x, y);
2655 break;
2656 case O_WATER_1:
2657 case O_WATER_2:
2658 case O_WATER_3:
2659 case O_WATER_4:
2660 case O_WATER_5:
2661 case O_WATER_6:
2662 case O_WATER_7:
2663 case O_WATER_8:
2664 case O_WATER_9:
2665 case O_WATER_10:
2666 case O_WATER_11:
2667 case O_WATER_12:
2668 case O_WATER_13:
2669 case O_WATER_14:
2670 case O_WATER_15:
2671 found_water=TRUE; /* for sound */
2672 /* simply the next identifier */
2673 next(cave, x, y);
2674 break;
2676 case O_BLADDER_SPENDER:
2677 if (is_space_dir(cave, x, y, opposite[grav_compat])) {
2678 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
2679 store(cave, x, y, O_PRE_STEEL_1);
2680 play_sound_of_element(cave, O_BLADDER_SPENDER);
2682 break;
2686 default:
2687 /* other inanimate elements that do nothing */
2688 break;
2692 /* POSTPROCESSING */
2694 /* another scan-like routine: */
2695 /* short explosions (for example, in bd1) started with explode_2. */
2696 /* internally we use explode_1; and change it to explode_2 if needed. */
2697 if (cave->short_explosions)
2698 for (y=0; y<cave->h; y++)
2699 for (x=0; x<cave->w; x++)
2700 if (is_first_stage_of_explosion(cave, x, y)) {
2701 next(cave, x, y);
2702 store(cave, x, y, get(cave, x, y)&~SCANNED);
2705 /* finally: forget "scanned" flags for objects. */
2706 /* also, check for time penalties. */
2707 /* these is something like an effect table, but we do not really use one. */
2708 for (y=0; y<cave->h; y++)
2709 for (x=0; x<cave->w; x++) {
2710 if (get(cave, x, y)&SCANNED)
2711 store(cave, x, y, get(cave, x, y)&~SCANNED);
2712 if (get(cave, x, y)==O_TIME_PENALTY) {
2713 store(cave, x, y, O_GRAVESTONE);
2714 time_decrement_sec+=cave->time_penalty; /* there is time penalty for destroying the voodoo */
2719 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
2720 /* but we only do this, if a living player was found. if not yet, the setup routine coordinates are used */
2721 if (cave->player_state==GD_PL_LIVING) {
2722 if (cave->active_is_first_found) {
2723 /* to be 1stb compatible, we do everything backwards. */
2724 for (y=cave->h-1; y>=0; y--)
2725 for (x=cave->w-1; x>=0; x--)
2726 if (is_player(cave, x, y)) {
2727 /* here we remember the coordinates. */
2728 cave->player_x=x;
2729 cave->player_y=y;
2732 else
2734 /* as in the original: look for the last one */
2735 for (y=0; y<cave->h; y++)
2736 for (x=0; x<cave->w; x++)
2737 if (is_player(cave, x, y)) {
2738 /* here we remember the coordinates. */
2739 cave->player_x=x;
2740 cave->player_y=y;
2745 /* record coordinates of player for chasing stone */
2746 for (i=0; i<G_N_ELEMENTS(cave->px)-1; i++) {
2747 cave->px[i]=cave->px[i+1];
2748 cave->py[i]=cave->py[i+1];
2750 cave->px[G_N_ELEMENTS(cave->px)-1]=cave->player_x;
2751 cave->py[G_N_ELEMENTS(cave->py)-1]=cave->player_y;
2753 /* SCHEDULING */
2755 /* update timing calculated by iterating and counting elements which
2756 were slow to process on c64 */
2757 switch (cave->scheduling) {
2758 case GD_SCHEDULING_MILLISECONDS:
2759 /* cave->speed already contains the milliseconds value, do not touch it */
2760 break;
2762 case GD_SCHEDULING_BD1:
2763 if (!cave->intermission)
2764 /* non-intermissions */
2765 cave->speed=(88+3.66*cave->c64_timing+(cave->ckdelay+cave->ckdelay_extra_for_animation)/1000);
2766 else
2767 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
2768 cave->speed=(60+3.66*cave->c64_timing+(cave->ckdelay+cave->ckdelay_extra_for_animation)/1000);
2769 break;
2771 case GD_SCHEDULING_BD1_ATARI:
2772 /* about 20ms/frame faster than c64 version */
2773 if (!cave->intermission)
2774 cave->speed=(74+3.2*cave->c64_timing+(cave->ckdelay)/1000); /* non-intermissions */
2775 else
2776 cave->speed=(65+2.88*cave->c64_timing+(cave->ckdelay)/1000); /* for intermissions */
2777 break;
2779 case GD_SCHEDULING_BD2:
2780 /* 60 is a guess. */
2781 cave->speed=MAX(60+(cave->ckdelay+cave->ckdelay_extra_for_animation)/1000, cave->c64_timing*20);
2782 break;
2784 case GD_SCHEDULING_PLCK:
2785 /* 65 is totally empty cave in construction kit, with delay=0) */
2786 cave->speed=MAX(65+cave->ckdelay/1000, cave->c64_timing*20);
2787 break;
2789 case GD_SCHEDULING_BD2_PLCK_ATARI:
2790 /* a really fast engine; timing works like c64 plck. */
2791 /* 40 ms was measured in the construction kit, with delay=0 */
2792 cave->speed=MAX(40+cave->ckdelay/1000, cave->c64_timing*20);
2793 break;
2795 case GD_SCHEDULING_CRDR:
2796 if (cave->hammered_walls_reappear)
2797 cave->ckdelay+=60000;
2798 cave->speed=MAX(130+cave->ckdelay/1000, cave->c64_timing*20);
2799 break;
2801 case GD_SCHEDULING_MAX:
2802 /* to avoid compiler warning */
2803 g_assert_not_reached();
2804 break;
2807 /* cave 3 sounds. precedence is controlled by the sound_play function. */
2808 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
2809 if (found_water && cave->water_sound)
2810 gd_sound_play(cave, GD_S_WATER);
2811 magic_sound=cave->magic_wall_state==GD_MW_ACTIVE && cave->magic_wall_sound;
2812 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));
2813 if (amoeba_sound && magic_sound)
2814 gd_sound_play(cave, GD_S_AMOEBA_MAGIC);
2815 else
2816 if (amoeba_sound)
2817 gd_sound_play(cave, GD_S_AMOEBA);
2818 else
2819 if (magic_sound)
2820 gd_sound_play(cave, GD_S_MAGIC_WALL);
2821 if (cave->hatched)
2822 if ((amoeba_count>0 && cave->amoeba_state==GD_AM_AWAKE)
2823 || (amoeba_2_count>0 && cave->amoeba_2_state==GD_AM_AWAKE))
2824 play_sound_of_element(cave, O_AMOEBA);
2825 /* pneumatic hammer sound - overrides everything. */
2826 if (cave->pneumatic_hammer_active_delay>0 && cave->pneumatic_hammer_sound)
2827 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER);
2829 /* CAVE VARIABLES */
2831 /* PLAYER */
2832 if ((cave->player_state==GD_PL_LIVING && cave->player_seen_ago>15) || cave->kill_player) /* check if player is alive. */
2833 cave->player_state=GD_PL_DIED;
2834 if (cave->voodoo_touched) /* check if any voodoo exploded, and kill players the next scan if that happended. */
2835 cave->kill_player=TRUE;
2837 /* AMOEBA */
2838 if (cave->amoeba_state==GD_AM_AWAKE) {
2839 /* check flags after evaluating. */
2840 if (amoeba_count>=cave->amoeba_max_count)
2841 cave->amoeba_state=GD_AM_TOO_BIG;
2842 if (amoeba_found_enclosed)
2843 cave->amoeba_state=GD_AM_ENCLOSED;
2845 /* amoeba can also be turned into diamond by magic wall */
2846 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state==GD_MW_ACTIVE)
2847 cave->amoeba_state=GD_AM_ENCLOSED;
2848 /* AMOEBA 2 */
2849 if (cave->amoeba_2_state==GD_AM_AWAKE) {
2850 /* check flags after evaluating. */
2851 if (amoeba_2_count>=cave->amoeba_2_max_count)
2852 cave->amoeba_2_state=GD_AM_TOO_BIG;
2853 if (amoeba_2_found_enclosed)
2854 cave->amoeba_2_state=GD_AM_ENCLOSED;
2856 /* amoeba 2 can also be turned into diamond by magic wall */
2857 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state==GD_MW_ACTIVE)
2858 cave->amoeba_2_state=GD_AM_ENCLOSED;
2861 /* now check times. --------------------------- */
2862 /* decrement time if a voodoo was killed. */
2863 cave->time-=time_decrement_sec*cave->timing_factor;
2864 if (cave->time<0)
2865 cave->time=0;
2867 /* only decrement time when player is already born. */
2868 if (cave->hatched) {
2869 int secondsbefore, secondsafter;
2871 secondsbefore=cave->time/cave->timing_factor;
2872 cave->time-=cave->speed;
2873 if (cave->time<=0)
2874 cave->time=0;
2875 secondsafter=cave->time/cave->timing_factor;
2876 if (cave->time/cave->timing_factor<10)
2877 /* if less than 10 seconds, no walking sound, but play explosion sound */
2878 gd_sound_play(cave, GD_S_NONE);
2879 if (secondsbefore!=secondsafter)
2880 gd_cave_set_seconds_sound(cave);
2882 /* a gravity switch was activated; seconds counting down */
2883 if (cave->gravity_will_change>0) {
2884 cave->gravity_will_change-=cave->speed;
2885 if (cave->gravity_will_change<0)
2886 cave->gravity_will_change=0;
2888 if (cave->gravity_will_change==0) {
2889 cave->gravity=cave->gravity_next_direction;
2890 if (cave->gravity_change_sound)
2891 gd_sound_play(cave, GD_S_GRAVITY_CHANGE); /* takes precedence over amoeba and magic wall sound */
2895 /* creatures direction automatically change */
2896 if (cave->creatures_direction_will_change>0) {
2897 cave->creatures_direction_will_change-=cave->speed;
2898 if (cave->creatures_direction_will_change<0)
2899 cave->creatures_direction_will_change=0;
2901 if (cave->creatures_direction_will_change==0) {
2902 if (cave->creature_direction_auto_change_sound)
2903 gd_sound_play(cave, GD_S_SWITCH_CREATURES);
2904 cave->creatures_backwards=!cave->creatures_backwards;
2905 cave->creatures_direction_will_change=cave->creatures_direction_auto_change_time*cave->timing_factor;
2909 /* magic wall; if active&wait or not wait for hatching */
2910 if (cave->magic_wall_state==GD_MW_ACTIVE && (cave->hatched || !cave->magic_timer_wait_for_hatching)) {
2911 cave->magic_wall_time-=cave->speed;
2912 if (cave->magic_wall_time<0)
2913 cave->magic_wall_time=0;
2914 if (cave->magic_wall_time==0)
2915 cave->magic_wall_state=GD_MW_EXPIRED;
2917 /* we may wait for hatching, when starting amoeba */
2918 if (cave->amoeba_timer_started_immediately || (cave->amoeba_state==GD_AM_AWAKE && (cave->hatched || !cave->amoeba_timer_wait_for_hatching))) {
2919 cave->amoeba_time-=cave->speed;
2920 if (cave->amoeba_time<0)
2921 cave->amoeba_time=0;
2922 if (cave->amoeba_time==0)
2923 cave->amoeba_growth_prob=cave->amoeba_fast_growth_prob;
2925 /* we may wait for hatching, when starting amoeba */
2926 if (cave->amoeba_timer_started_immediately || (cave->amoeba_2_state==GD_AM_AWAKE && (cave->hatched || !cave->amoeba_timer_wait_for_hatching))) {
2927 cave->amoeba_2_time-=cave->speed;
2928 if (cave->amoeba_2_time<0)
2929 cave->amoeba_2_time=0;
2930 if (cave->amoeba_2_time==0)
2931 cave->amoeba_2_growth_prob=cave->amoeba_2_fast_growth_prob;
2934 /* check for player hatching. */
2935 start_signal=FALSE;
2936 /* if not the c64 scheduling, but the correct frametime is used, hatching delay should always be decremented. */
2937 /* otherwise, the if (millisecs...) condition below will set this. */
2938 if (cave->scheduling==GD_SCHEDULING_MILLISECONDS) { /* NON-C64 scheduling */
2939 if (cave->hatching_delay_frame>0) {
2940 cave->hatching_delay_frame--; /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
2941 if (cave->hatching_delay_frame==0)
2942 start_signal=TRUE;
2945 else { /* C64 scheduling */
2946 if (cave->hatching_delay_time>0) {
2947 cave->hatching_delay_time-=cave->speed; /* for c64 schedulings, hatching delay means milliseconds. */
2948 if (cave->hatching_delay_time<=0) {
2949 cave->hatching_delay_time=0;
2950 start_signal=TRUE;
2955 /* if decremented hatching, and it became zero: */
2956 if (start_signal) { /* THIS IS THE CAVE START SIGNAL */
2957 cave->hatched=TRUE; /* record that now the cave is in its normal state */
2959 gd_cave_count_diamonds(cave); /* if diamonds needed is below zero, we count the available diamonds now. */
2961 /* setup direction auto change */
2962 if (cave->creatures_direction_auto_change_time) {
2963 cave->creatures_direction_will_change=cave->creatures_direction_auto_change_time*cave->timing_factor;
2965 if (cave->creatures_direction_auto_change_on_start)
2966 cave->creatures_backwards=!cave->creatures_backwards;
2969 gd_sound_play(cave, GD_S_CRACK);
2972 /* for biters */
2973 if (cave->biters_wait_frame==0)
2974 cave->biters_wait_frame=cave->biter_delay_frame;
2975 else
2976 cave->biters_wait_frame--;
2977 /* replicators delay */
2978 if (cave->replicators_wait_frame==0)
2979 cave->replicators_wait_frame=cave->replicator_delay_frame;
2980 else
2981 cave->replicators_wait_frame--;
2984 /* LAST THOUGTS */
2986 /* check if cave failed by timeout */
2987 if (cave->player_state==GD_PL_LIVING && cave->time==0) {
2988 gd_cave_clear_sounds(cave);
2989 cave->player_state=GD_PL_TIMEOUT;
2990 gd_sound_play(cave, GD_S_TIMEOUT);
2993 /* set these for drawing. */
2994 cave->last_direction=player_move;
2995 /* here we remember last movements for animation. this is needed here, as animation
2996 is in sync with the game, not the keyboard directly. (for example, after exiting
2997 the cave, the player was "running" in the original, till bonus points were counted
2998 for remaining time and so on. */
2999 if (player_move==MV_LEFT || player_move==MV_UP_LEFT || player_move==MV_DOWN_LEFT)
3000 cave->last_horizontal_direction=MV_LEFT;
3001 if (player_move==MV_RIGHT || player_move==MV_UP_RIGHT || player_move==MV_DOWN_RIGHT)
3002 cave->last_horizontal_direction=MV_RIGHT;