20100212
[gdash.git] / src / caveengine.c
blob4ef494c327407c672d82ce58a9ed554226c2dd99
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; /* probability with sweet */
891 else
892 prob=cave->pushing_stone_prob; /* 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;
1040 static void
1041 do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction, GdElement falling_element)
1043 if (cave->gravity_disabled)
1044 return;
1046 if (is_space_dir(cave, x, y, falling_direction)) { /* beginning to fall */
1047 play_sound_of_element(cave, get(cave, x, y));
1048 move(cave, x, y, falling_direction, falling_element);
1050 /* check if it is on a sloped element, and it can roll. */
1051 /* for example, sloped wall looks like: */
1052 /* /| */
1053 /* /_| */
1054 /* this is tagged as sloped up&left. */
1055 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1056 /* then check the direction to roll (left or right) */
1057 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1058 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction])) { /* rolling down, if sitting on a sloped object */
1059 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) && is_space_dir(cave, x, y, cw_fourth[falling_direction]) && is_space_dir(cave, x, y, cw_eighth[falling_direction])) {
1060 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw, so here we use cw_fourth */
1061 play_sound_of_element(cave, get(cave, x, y));
1062 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1064 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) && is_space_dir(cave, x, y, ccw_fourth[falling_direction]) && is_space_dir(cave, x, y, ccw_eighth[falling_direction])) {
1065 /* rolling right? */
1066 play_sound_of_element(cave, get(cave, x, y));
1067 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1072 static gboolean
1073 do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1075 if (get_dir(cave, x, y, fall_dir)==O_VOODOO && cave->voodoo_dies_by_stone) {
1076 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1077 explode_dir(cave, x, y, fall_dir);
1078 return TRUE;
1080 else
1081 return FALSE;
1084 static gboolean
1085 do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1087 if (get_dir(cave, x, y, fall_dir)==O_VOODOO && cave->voodoo_collects_diamonds) {
1088 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1089 player_get_element(cave, O_DIAMOND); /* as if player got diamond */
1090 store(cave, x, y, O_SPACE); /* diamond disappears */
1091 return TRUE;
1093 else
1094 return FALSE;
1098 static gboolean
1099 do_fall_try_crack_nut(GdCave *cave, int x, int y, GdDirection fall_dir, GdElement bouncing)
1101 if (get_dir(cave, x, y, fall_dir)==O_NUT || get_dir(cave, x, y, fall_dir)==O_NUT_F) {
1102 /* stones */
1103 store(cave, x, y, bouncing);
1104 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1105 if (cave->nut_sound)
1106 gd_sound_play(cave, GD_S_NUT_CRACK);
1107 return TRUE;
1109 else
1110 return FALSE;
1113 static gboolean
1114 do_fall_try_magic(GdCave *cave, int x, int y, GdDirection fall_dir, GdElement magic)
1116 if (get_dir(cave, x, y, fall_dir)==O_MAGIC_WALL) {
1117 play_sound_of_element(cave, O_DIAMOND); /* always play diamond sound */
1118 if (cave->magic_wall_state==GD_MW_DORMANT)
1119 cave->magic_wall_state=GD_MW_ACTIVE;
1120 if (cave->magic_wall_state==GD_MW_ACTIVE && is_space_dir(cave, x, y, MV_TWICE+fall_dir)) {
1121 /* if magic wall active and place underneath, it turns element into anything the effect says to do. */
1122 store_dir(cave, x, y, MV_TWICE+fall_dir, magic);
1124 store(cave, x, y, O_SPACE); /* active or non-active or anything, element falling in will always disappear */
1125 return TRUE;
1127 else
1128 return FALSE;
1131 static gboolean
1132 do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1134 if (explodes_by_hit_dir(cave, x, y, fall_dir)) {
1135 explode_dir(cave, x, y, fall_dir);
1136 return TRUE;
1138 else
1139 return FALSE;
1142 static gboolean
1143 do_fall_roll_or_stop(GdCave *cave, int x, int y, GdDirection fall_dir, GdElement bouncing)
1145 if (is_space_dir(cave, x, y, fall_dir)) { /* falling further */
1146 move(cave, x, y, fall_dir, get(cave, x, y));
1147 return TRUE;
1149 /* check if it is on a sloped element, and it can roll. */
1150 /* for example, sloped wall looks like: */
1151 /* /| */
1152 /* /_| */
1153 /* this is tagged as sloped up&left. */
1154 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1155 /* then check the direction to roll (left or right) */
1156 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1157 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir])) { /* sloped element, falling to left or right */
1158 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) && is_space_dir(cave, x, y, cw_eighth[fall_dir]) && is_space_dir(cave, x, y, cw_fourth[fall_dir])) {
1159 play_sound_of_element(cave, get(cave, x, y));
1160 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y)); /* try to roll left first - see O_STONE to understand why cw_fourth */
1162 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) && is_space_dir(cave, x, y, ccw_eighth[fall_dir]) && is_space_dir(cave, x, y, ccw_fourth[fall_dir])) {
1163 play_sound_of_element(cave, get(cave, x, y));
1164 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y)); /* if not, try to roll right */
1166 else {
1167 /* cannot roll in any direction, so it stops */
1168 play_sound_of_element(cave, get(cave, x, y));
1169 store(cave, x, y, bouncing);
1171 return TRUE;
1174 /* any other element, stops */
1175 play_sound_of_element(cave, get(cave, x, y));
1176 store(cave, x, y, bouncing);
1177 return TRUE;
1190 /* process a cave. */
1191 void
1192 gd_cave_iterate(GdCave *cave, GdDirection player_move, gboolean player_fire, gboolean suicide)
1194 int x, y, i;
1195 int ymin, ymax; /* for border scan */
1196 gboolean amoeba_found_enclosed, amoeba_2_found_enclosed; /* amoeba found to be enclosed. if not, this is cleared */
1197 int amoeba_count, amoeba_2_count; /* counting the number of amoebas. after scan, check if too much */
1198 gboolean found_water; /* cave scan found water - for sound */
1199 gboolean inbox_toggle;
1200 gboolean start_signal;
1201 GdDirection grav_compat=cave->gravity_affects_all?cave->gravity:MV_DOWN; /* gravity for falling wall, bladder, ... */
1202 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1203 static const GdDirection creature_dir[]={ MV_LEFT, MV_UP, MV_RIGHT, MV_DOWN };
1204 static const GdDirection creature_chdir[]={ MV_RIGHT, MV_DOWN, MV_LEFT, MV_UP };
1205 int time_decrement_sec;
1206 GdElement biter_try[]={ O_DIRT, cave->biter_eat, O_SPACE, O_STONE }; /* biters eating elements preference, they try to go in this order */
1207 gboolean amoeba_sound, magic_sound;
1209 gd_cave_clear_sounds(cave);
1211 /* if diagonal movements not allowed, */
1212 /* horizontal movements have precedence. [BROADRIBB] */
1213 if (!cave->diagonal_movements) {
1214 switch (player_move) {
1215 case MV_UP_RIGHT:
1216 case MV_DOWN_RIGHT:
1217 player_move=MV_RIGHT;
1218 break;
1219 case MV_UP_LEFT:
1220 case MV_DOWN_LEFT:
1221 player_move=MV_LEFT;
1222 break;
1223 default:
1224 /* no correction needed */
1225 break;
1229 /* set cave get function; to implement perfect or lineshifting borders */
1230 if (cave->lineshift)
1231 cave->getp=getp_shift;
1232 else
1233 cave->getp=getp_perfect;
1235 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1236 if (cave->player_seen_ago<100)
1237 cave->player_seen_ago++;
1239 if (cave->pneumatic_hammer_active_delay>0)
1240 cave->pneumatic_hammer_active_delay--;
1242 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1243 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1244 cave->inbox_flash_toggle=!cave->inbox_flash_toggle;
1245 inbox_toggle=cave->inbox_flash_toggle;
1247 if (cave->gate_open_flash>0)
1248 cave->gate_open_flash--;
1250 /* score collected this frame */
1251 cave->score=0;
1253 /* suicide only kills the active player */
1254 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1255 /* we must check if there is a player or not - he may have exploded or something like that */
1256 if (suicide && cave->player_state==GD_PL_LIVING && is_player(cave, cave->player_x, cave->player_y))
1257 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1259 /* check for walls reappearing */
1260 if (cave->hammered_reappear)
1261 for (y=0; y<cave->h; y++)
1262 for (x=0; x<cave->w; x++) {
1263 /* timer for the cell > 0? */
1264 if (cave->hammered_reappear[y][x]>0) {
1265 /* decrease timer */
1266 cave->hammered_reappear[y][x]--;
1267 /* check if it became zero */
1268 if (cave->hammered_reappear[y][x]==0) {
1269 store(cave, x, y, O_BRICK);
1270 gd_sound_play(cave, GD_S_WALL_REAPPEAR);
1275 /* variables to check during the scan */
1276 amoeba_found_enclosed=TRUE; /* will be set to false if any of the amoeba is found free. */
1277 amoeba_2_found_enclosed=TRUE;
1278 amoeba_count=0;
1279 amoeba_2_count=0;
1280 found_water=FALSE;
1281 cave->ckdelay=0;
1282 time_decrement_sec=0;
1284 /* check whether to scan the first and last line */
1285 if (cave->border_scan_first_and_last) {
1286 ymin=0;
1287 ymax=cave->h-1;
1288 } else {
1289 ymin=1;
1290 ymax=cave->h-2;
1292 /* the cave scan routine */
1293 for (y=ymin; y<=ymax; y++)
1294 for (x=0; x<cave->w; x++) {
1295 /* if we find a scanned element, change it to the normal one, and that's all. */
1296 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1297 if (get(cave, x, y)&SCANNED) {
1298 store(cave, x, y, get(cave, x, y)& ~SCANNED);
1299 continue;
1302 /* add the ckdelay correction value for every element seen. */
1303 cave->ckdelay+=gd_elements[get(cave, x, y)].ckdelay;
1305 switch (get(cave, x, y)) {
1307 * P L A Y E R S
1309 case O_PLAYER:
1310 if (cave->kill_player) {
1311 explode (cave, x, y);
1312 break;
1314 cave->player_seen_ago=0;
1315 /* bd4 intermission caves have many players. so if one of them has exited,
1316 * do not change the flag anymore. so this if () is needed */
1317 if (cave->player_state!=GD_PL_EXITED)
1318 cave->player_state=GD_PL_LIVING;
1320 /* check for pneumatic hammer things */
1321 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right for hammer 5) stand on something */
1322 if (player_fire && cave->got_pneumatic_hammer && is_space_dir(cave, x, y, player_move)
1323 && !is_space_dir(cave, x, y, MV_DOWN)) {
1324 if (player_move==MV_LEFT && can_be_hammered_dir(cave, x, y, MV_DOWN_LEFT)) {
1325 cave->pneumatic_hammer_active_delay=cave->pneumatic_hammer_frame;
1326 store_dir(cave, x, y, MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1327 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1328 break; /* finished. */
1330 if (player_move==MV_RIGHT && can_be_hammered_dir(cave, x, y, MV_DOWN_RIGHT)) {
1331 cave->pneumatic_hammer_active_delay=cave->pneumatic_hammer_frame;
1332 store_dir(cave, x, y, MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1333 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1334 break; /* finished. */
1338 if (player_move!=MV_STILL) {
1339 /* only do every check if he is not moving */
1340 GdElement what=get_dir(cave, x, y, player_move);
1341 GdElement remains=what;
1342 gboolean push;
1344 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1345 if (what==O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1346 break;
1348 /* try to push element; if successful, break */
1349 push=do_push(cave, x, y, player_move, player_fire);
1350 if (push)
1351 remains=O_SPACE;
1352 else
1353 switch (what) {
1354 case O_BOMB:
1355 /* if its a bomb, remember he now has one. */
1356 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1357 gd_sound_play(cave, GD_S_BOMB_COLLECT);
1358 store_dir(cave, x, y, player_move, O_SPACE);
1359 if (player_fire)
1360 store(cave, x, y, O_PLAYER_BOMB);
1361 else
1362 move(cave, x, y, player_move, O_PLAYER_BOMB);
1363 break;
1365 case O_POT:
1366 /* we do not change the "remains" and "what" variables, so that part of the code will be ineffective */
1367 if (!player_fire && !cave->gravity_switch_active && cave->skeletons_collected>=cave->skeletons_needed_for_pot) {
1368 cave->skeletons_collected-=cave->skeletons_needed_for_pot;
1369 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1370 cave->gravity_disabled=TRUE;
1372 break;
1374 case O_GRAVITY_SWITCH:
1375 /* (we cannot use player_get for this as it does not have player_move parameter) */
1376 /* only allow changing direction if the new dir is not diagonal */
1377 if (cave->gravity_switch_active && (player_move==MV_LEFT || player_move==MV_RIGHT || player_move==MV_UP || player_move==MV_DOWN)) {
1378 gd_sound_play(cave, GD_S_SWITCH_GRAVITY);
1379 cave->gravity_will_change=cave->gravity_change_time*cave->timing_factor;
1380 cave->gravity_next_direction=player_move;
1381 cave->gravity_switch_active=FALSE;
1383 break;
1385 default:
1386 /* get element - process others. if cannot get, player_get_element will return the same */
1387 remains=player_get_element (cave, what);
1388 break;
1391 if (remains!=what || remains==O_SPACE) {
1392 /* if anything changed, apply the change. */
1394 /* if snapping anything and we have snapping explosions set. but these is not true for pushing. */
1395 if (remains==O_SPACE && player_fire && !push)
1396 remains=cave->snap_element;
1397 if (remains!=O_SPACE || player_fire)
1398 /* if any other element than space, player cannot move. also if pressing fire, will not move. */
1399 store_dir(cave, x, y, player_move, remains);
1400 else
1401 /* if space remains there, the player moves. */
1402 move(cave, x, y, player_move, O_PLAYER);
1406 break;
1408 case O_PLAYER_BOMB:
1409 /* much simpler; cannot steal stones */
1410 if (cave->kill_player) {
1411 explode (cave, x, y);
1412 break;
1414 cave->player_seen_ago=0;
1415 /* bd4 intermission caves have many players. so if one of them has exited,
1416 * do not change the flag anymore. so this if () is needed */
1417 if (cave->player_state!=GD_PL_EXITED)
1418 cave->player_state=GD_PL_LIVING;
1420 if (player_move!=MV_STILL) { /* if the player does not move, nothing to do */
1421 GdElement what=get_dir(cave, x, y, player_move);
1422 GdElement remains=what;
1424 if (player_fire) {
1425 /* placing a bomb into empty space or dirt */
1426 if (is_space_dir(cave, x, y, player_move) || is_element_dir(cave, x, y, player_move, O_DIRT)) {
1427 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1428 /* placed bomb, he is normal player again */
1429 store(cave, x, y, O_PLAYER);
1430 gd_sound_play(cave, GD_S_BOMB_PLACE);
1432 break;
1435 /* pushing and collecting */
1436 /* if we are 'eating' a teleporter, and the function returns true (teleporting worked), break here */
1437 if (what==O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1438 break;
1440 if (do_push(cave, x, y, player_move, FALSE)) /* player fire is false... */
1441 remains=O_SPACE;
1442 else {
1443 switch (what) {
1444 case O_GRAVITY_SWITCH:
1445 /* (we cannot use player_get for this as it does not have player_move parameter) */
1446 /* only allow changing direction if the new dir is not diagonal */
1447 if (cave->gravity_switch_active && (player_move==MV_LEFT || player_move==MV_RIGHT || player_move==MV_UP || player_move==MV_DOWN)) {
1448 gd_sound_play(cave, GD_S_SWITCH_GRAVITY);
1449 cave->gravity_will_change=cave->gravity_change_time*cave->timing_factor;
1450 cave->gravity_next_direction=player_move;
1451 cave->gravity_switch_active=FALSE;
1453 break;
1454 default:
1455 /* get element. if cannot get, player_get_element will return the same */
1456 remains=player_get_element (cave, what);
1457 break;
1461 /* if element changed, OR there is space, move. */
1462 if (remains!=what || remains==O_SPACE) {
1463 /* if anything changed, apply the change. */
1464 move(cave, x, y, player_move, O_PLAYER_BOMB);
1467 break;
1469 case O_PLAYER_STIRRING:
1470 if (cave->kill_player) {
1471 explode (cave, x, y);
1472 break;
1474 gd_sound_play(cave, GD_S_STIRRING); /* stirring sound, if no other walking sound or explosion */
1475 cave->player_seen_ago=0;
1476 /* bd4 intermission caves have many players. so if one of them has exited,
1477 * do not change the flag anymore. so this if () is needed */
1478 if (cave->player_state!=GD_PL_EXITED)
1479 cave->player_state=GD_PL_LIVING;
1480 if (player_fire) {
1481 /* player "exits" stirring the pot by pressing fire */
1482 cave->gravity_disabled=FALSE;
1483 store(cave, x, y, O_PLAYER);
1484 cave->gravity_switch_active=TRUE;
1486 break;
1488 /* player holding pneumatic hammer */
1489 case O_PLAYER_PNEUMATIC_LEFT:
1490 case O_PLAYER_PNEUMATIC_RIGHT:
1491 /* usual player stuff */
1492 if (cave->kill_player) {
1493 explode (cave, x, y);
1494 break;
1496 cave->player_seen_ago=0;
1497 if (cave->player_state!=GD_PL_EXITED)
1498 cave->player_state=GD_PL_LIVING;
1499 if (cave->pneumatic_hammer_active_delay==0) /* if hammering time is up, becomes a normal player again. */
1500 store(cave, x, y, O_PLAYER);
1501 break;
1503 /* the active pneumatic hammer itself */
1504 case O_PNEUMATIC_ACTIVE_RIGHT:
1505 case O_PNEUMATIC_ACTIVE_LEFT:
1506 if (cave->pneumatic_hammer_active_delay==0) {
1507 GdElement new_elem;
1509 store(cave, x, y, O_SPACE); /* pneumatic hammer element disappears */
1510 /* which is the new element which appears after that one is hammered? */
1511 new_elem=gd_element_get_hammered(get_dir(cave, x, y, MV_DOWN));
1512 /* if there is a new element, display it */
1513 /* O_NONE might be returned, for example if the element being hammered explodes during hammering (by a nearby explosion) */
1514 if (new_elem!=O_NONE) {
1515 store_dir(cave, x, y, MV_DOWN, new_elem);
1517 /* and if walls reappear, remember it in array */
1518 if (cave->hammered_walls_reappear) {
1519 int wall_y;
1521 wall_y=(y+1)%cave->h;
1522 cave->hammered_reappear[wall_y][x]=cave->hammered_wall_reappear_frame;
1526 break;
1530 * S T O N E S, D I A M O N D S
1532 case O_STONE: /* standing stone */
1533 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1534 break;
1536 case O_MEGA_STONE: /* standing mega_stone */
1537 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1538 break;
1540 case O_DIAMOND: /* standing diamond */
1541 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1542 break;
1544 case O_NUT: /* standing nut */
1545 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1546 break;
1548 case O_DIRT_BALL: /* standing dirt ball */
1549 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1550 break;
1552 case O_DIRT_LOOSE: /* standing loose dirt */
1553 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1554 break;
1556 case O_FLYING_STONE: /* standing stone */
1557 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1558 break;
1560 case O_FLYING_DIAMOND: /* standing diamond */
1561 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1562 break;
1565 * F A L L I N G E L E M E N T S, F L Y I N G S T O N E S, D I A M O N D S
1567 case O_DIRT_BALL_F: /* falling dirt ball */
1568 if (!cave->gravity_disabled)
1569 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
1570 break;
1572 case O_DIRT_LOOSE_F: /* falling loose dirt */
1573 if (!cave->gravity_disabled)
1574 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
1575 break;
1577 case O_STONE_F: /* falling stone */
1578 if (!cave->gravity_disabled) {
1579 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity)) break;
1580 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect)) break;
1581 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to)) break;
1582 if (do_fall_try_crush(cave, x, y, cave->gravity)) break;
1583 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
1585 break;
1587 case O_MEGA_STONE_F: /* falling mega */
1588 if (!cave->gravity_disabled) {
1589 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity)) break;
1590 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE)) break;
1591 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to)) break;
1592 if (do_fall_try_crush(cave, x, y, cave->gravity)) break;
1593 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
1595 break;
1597 case O_DIAMOND_F: /* falling diamond */
1598 if (!cave->gravity_disabled) {
1599 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity)) break;
1600 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to)) break;
1601 if (do_fall_try_crush(cave, x, y, cave->gravity)) break;
1602 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
1604 break;
1606 case O_NUT_F: /* falling nut */
1607 if (!cave->gravity_disabled) {
1608 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to)) break;
1609 if (do_fall_try_crush(cave, x, y, cave->gravity)) break;
1610 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
1612 break;
1614 case O_FLYING_STONE_F: /* falling stone */
1615 if (!cave->gravity_disabled) {
1616 GdDirection fall_dir=opposite[cave->gravity];
1618 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir)) break;
1619 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE)) break;
1620 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to)) break;
1621 if (do_fall_try_crush(cave, x, y, fall_dir)) break;
1622 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
1624 break;
1626 case O_FLYING_DIAMOND_F: /* falling diamond */
1627 if (!cave->gravity_disabled) {
1628 GdDirection fall_dir=opposite[cave->gravity];
1630 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir)) break;
1631 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to)) break;
1632 if (do_fall_try_crush(cave, x, y, fall_dir)) break;
1633 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
1635 break;
1640 * N I T R O P A C K
1642 case O_NITRO_PACK: /* standing nitro pack */
1643 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
1644 break;
1646 case O_NITRO_PACK_F: /* falling nitro pack */
1647 if (!cave->gravity_disabled) {
1648 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
1649 move(cave, x, y, cave->gravity, get(cave, x, y));
1650 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to)) {
1651 /* try magic wall; if true, function did the work */
1653 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT)) {
1654 /* falling on a dirt, it does NOT explode - just stops at its place. */
1655 play_sound_of_element(cave, O_NITRO_PACK);
1656 store(cave, x, y, O_NITRO_PACK);
1658 else
1659 /* falling on any other element it explodes */
1660 explode(cave, x, y);
1662 break;
1663 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
1664 explode(cave, x, y);
1665 break;
1670 * C R E A T U R E S
1673 case O_COW_1:
1674 case O_COW_2:
1675 case O_COW_3:
1676 case O_COW_4:
1677 /* if cannot move in any direction, becomes an enclosed cow */
1678 if (!is_space_dir(cave, x, y, MV_UP) && !is_space_dir(cave, x, y, MV_DOWN)
1679 && !is_space_dir(cave, x, y, MV_LEFT) && !is_space_dir(cave, x, y, MV_RIGHT))
1680 store(cave, x, y, O_COW_ENCLOSED_1);
1681 else {
1682 /* THIS IS THE CREATURE MOVE thing copied. */
1683 const GdDirection *creature_move;
1684 gboolean ccw=rotates_ccw(cave, x, y); /* check if default is counterclockwise */
1685 GdElement base; /* base element number (which is like O_***_1) */
1686 int dir, dirn, dirp; /* direction */
1688 base=O_COW_1;
1690 dir=get(cave, x, y)-base; /* facing where */
1691 creature_move=cave->creatures_backwards? creature_chdir:creature_dir;
1693 /* now change direction if backwards */
1694 if (cave->creatures_backwards)
1695 ccw=!ccw;
1697 if (ccw) {
1698 dirn=(dir+3)&3; /* fast turn */
1699 dirp=(dir+1)&3; /* slow turn */
1700 } else {
1701 dirn=(dir+1)&3; /* fast turn */
1702 dirp=(dir+3)&3; /* slow turn */
1705 if (is_space_dir(cave, x, y, creature_move[dirn]))
1706 move(cave, x, y, creature_move[dirn], base+dirn); /* turn and move to preferred dir */
1707 else if (is_space_dir(cave, x, y, creature_move[dir]))
1708 move(cave, x, y, creature_move[dir], base+dir); /* go on */
1709 else
1710 store(cave, x, y, base+dirp); /* turn in place if nothing else possible */
1712 break;
1713 /* enclosed cows wait some time before turning to a skeleton */
1714 case O_COW_ENCLOSED_1:
1715 case O_COW_ENCLOSED_2:
1716 case O_COW_ENCLOSED_3:
1717 case O_COW_ENCLOSED_4:
1718 case O_COW_ENCLOSED_5:
1719 case O_COW_ENCLOSED_6:
1720 if (is_space_dir(cave, x, y, MV_UP) || is_space_dir(cave, x, y, MV_LEFT) || is_space_dir(cave, x, y, MV_RIGHT) || is_space_dir(cave, x, y, MV_DOWN))
1721 store(cave, x, y, O_COW_1);
1722 else
1723 next(cave, x, y);
1724 break;
1725 case O_COW_ENCLOSED_7:
1726 if (is_space_dir(cave, x, y, MV_UP) || is_space_dir(cave, x, y, MV_LEFT) || is_space_dir(cave, x, y, MV_RIGHT) || is_space_dir(cave, x, y, MV_DOWN))
1727 store(cave, x, y, O_COW_1);
1728 else
1729 store(cave, x, y, O_SKELETON);
1730 break;
1732 case O_FIREFLY_1:
1733 case O_FIREFLY_2:
1734 case O_FIREFLY_3:
1735 case O_FIREFLY_4:
1736 case O_ALT_FIREFLY_1:
1737 case O_ALT_FIREFLY_2:
1738 case O_ALT_FIREFLY_3:
1739 case O_ALT_FIREFLY_4:
1740 case O_BUTTER_1:
1741 case O_BUTTER_2:
1742 case O_BUTTER_3:
1743 case O_BUTTER_4:
1744 case O_ALT_BUTTER_1:
1745 case O_ALT_BUTTER_2:
1746 case O_ALT_BUTTER_3:
1747 case O_ALT_BUTTER_4:
1748 case O_STONEFLY_1:
1749 case O_STONEFLY_2:
1750 case O_STONEFLY_3:
1751 case O_STONEFLY_4:
1752 /* check if touches a voodoo */
1753 if (get_dir(cave, x, y, MV_LEFT)==O_VOODOO || get_dir(cave, x, y, MV_RIGHT)==O_VOODOO || get_dir(cave, x, y, MV_UP)==O_VOODOO || get_dir(cave, x, y, MV_DOWN)==O_VOODOO)
1754 cave->voodoo_touched=TRUE;
1755 /* check if touches something bad and should explode (includes voodoo by the flags) */
1756 if (blows_up_flies_dir(cave, x, y, MV_DOWN) || blows_up_flies_dir(cave, x, y, MV_UP)
1757 || blows_up_flies_dir(cave, x, y, MV_LEFT) || blows_up_flies_dir(cave, x, y, MV_RIGHT))
1758 explode (cave, x, y);
1759 /* otherwise move */
1760 else {
1761 const GdDirection *creature_move;
1762 gboolean ccw=rotates_ccw(cave, x, y); /* check if default is counterclockwise */
1763 GdElement base; /* base element number (which is like O_***_1) */
1764 int dir, dirn, dirp; /* direction */
1766 if (get(cave, x, y)>=O_FIREFLY_1 && get(cave, x, y)<=O_FIREFLY_4)
1767 base=O_FIREFLY_1;
1768 else if (get(cave, x, y)>=O_BUTTER_1 && get(cave, x, y)<=O_BUTTER_4)
1769 base=O_BUTTER_1;
1770 else if (get(cave, x, y)>=O_STONEFLY_1 && get(cave, x, y)<=O_STONEFLY_4)
1771 base=O_STONEFLY_1;
1772 else if (get(cave, x, y)>=O_ALT_FIREFLY_1 && get(cave, x, y)<=O_ALT_FIREFLY_4)
1773 base=O_ALT_FIREFLY_1;
1774 else if (get(cave, x, y)>=O_ALT_BUTTER_1 && get(cave, x, y)<=O_ALT_BUTTER_4)
1775 base=O_ALT_BUTTER_1;
1776 else
1777 g_assert_not_reached();
1779 dir=get(cave, x, y)-base; /* facing where */
1780 creature_move=cave->creatures_backwards? creature_chdir:creature_dir;
1782 /* now change direction if backwards */
1783 if (cave->creatures_backwards)
1784 ccw=!ccw;
1786 if (ccw) {
1787 dirn=(dir+3)&3; /* fast turn */
1788 dirp=(dir+1)&3; /* slow turn */
1789 } else {
1790 dirn=(dir+1)&3; /* fast turn */
1791 dirp=(dir+3)&3; /* slow turn */
1794 if (is_space_dir(cave, x, y, creature_move[dirn]))
1795 move(cave, x, y, creature_move[dirn], base+dirn); /* turn and move to preferred dir */
1796 else if (is_space_dir(cave, x, y, creature_move[dir]))
1797 move(cave, x, y, creature_move[dir], base+dir); /* go on */
1798 else
1799 store(cave, x, y, base+dirp); /* turn in place if nothing else possible */
1801 break;
1803 case O_WAITING_STONE:
1804 if (is_space_dir(cave, x, y, grav_compat)) { /* beginning to fall */
1805 /* it wakes up. */
1806 move(cave, x, y, grav_compat, O_CHASING_STONE);
1808 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat])) { /* rolling down a brick wall or a stone */
1809 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) && is_space_dir(cave, x, y, cw_fourth[grav_compat]) && is_space_dir(cave, x, y, cw_eighth[grav_compat])) {
1810 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
1811 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
1813 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) && is_space_dir(cave, x, y, ccw_fourth[grav_compat]) && is_space_dir(cave, x, y, ccw_eighth[grav_compat])) {
1814 /* or maybe right */
1815 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
1818 break;
1820 case O_CHASING_STONE:
1822 int px=cave->px[0];
1823 int py=cave->py[0];
1824 gboolean horizontal=g_rand_boolean(cave->random);
1825 gboolean dont_move=FALSE;
1826 int i=3;
1828 /* try to move... */
1829 while (1) {
1830 if (horizontal) { /*********************************/
1831 /* check for a horizontal movement */
1832 if (px==x) {
1833 /* if coordinates are the same */
1834 i-=1;
1835 horizontal=!horizontal;
1836 if (i==2)
1837 continue;
1838 } else {
1839 if (px>x && is_space_dir(cave, x, y, MV_RIGHT)) {
1840 move(cave, x, y, MV_RIGHT, O_CHASING_STONE);
1841 dont_move=TRUE;
1842 break;
1843 } else
1844 if (px<x && is_space_dir(cave, x, y, MV_LEFT)) {
1845 move(cave, x, y, MV_LEFT, O_CHASING_STONE);
1846 dont_move=TRUE;
1847 break;
1848 } else {
1849 i-=2;
1850 if (i==1) {
1851 horizontal=!horizontal;
1852 continue;
1856 } else { /********************************/
1857 /* check for a vertical movement */
1858 if (py==y) {
1859 /* if coordinates are the same */
1860 i-=1;
1861 horizontal=!horizontal;
1862 if (i==2)
1863 continue;
1864 } else {
1865 if (py>y && is_space_dir(cave, x, y, MV_DOWN)) {
1866 move(cave, x, y, MV_DOWN, O_CHASING_STONE);
1867 dont_move=TRUE;
1868 break;
1869 } else
1870 if (py<y && is_space_dir(cave, x, y, MV_UP)) {
1871 move(cave, x, y, MV_UP, O_CHASING_STONE);
1872 dont_move=TRUE;
1873 break;
1874 } else {
1875 i-=2;
1876 if (i==1) {
1877 horizontal=!horizontal;
1878 continue;
1883 if (i!=0)
1884 dont_move=TRUE;
1885 break;
1888 /* if we should move in both directions, but can not move in any, stop. */
1889 if (!dont_move) {
1890 if (horizontal) { /* check for horizontal */
1891 if (x>=px) {
1892 if (is_space_dir(cave, x, y, MV_UP) && is_space_dir(cave, x, y, MV_UP_LEFT))
1893 move(cave, x, y, MV_UP, O_CHASING_STONE);
1894 else
1895 if (is_space_dir(cave, x, y, MV_DOWN) && is_space_dir(cave, x, y, MV_DOWN_LEFT))
1896 move(cave, x, y, MV_DOWN, O_CHASING_STONE);
1897 } else {
1898 if (is_space_dir(cave, x, y, MV_UP) && is_space_dir(cave, x, y, MV_UP_RIGHT))
1899 move(cave, x, y, MV_UP, O_CHASING_STONE);
1900 else
1901 if (is_space_dir(cave, x, y, MV_DOWN) && is_space_dir(cave, x, y, MV_DOWN_RIGHT))
1902 move(cave, x, y, MV_DOWN, O_CHASING_STONE);
1904 } else { /* check for vertical */
1905 if (y>=py) {
1906 if (is_space_dir(cave, x, y, MV_LEFT) && is_space_dir(cave, x, y, MV_UP_LEFT))
1907 move(cave, x, y, MV_LEFT, O_CHASING_STONE);
1908 else
1909 if (is_space_dir(cave, x, y, MV_RIGHT) && is_space_dir(cave, x, y, MV_UP_RIGHT))
1910 move(cave, x, y, MV_RIGHT, O_CHASING_STONE);
1911 } else {
1912 if (is_space_dir(cave, x, y, MV_LEFT) && is_space_dir(cave, x, y, MV_DOWN_LEFT))
1913 move(cave, x, y, MV_LEFT, O_CHASING_STONE);
1914 else
1915 if (is_space_dir(cave, x, y, MV_RIGHT) && is_space_dir(cave, x, y, MV_DOWN_RIGHT))
1916 move(cave, x, y, MV_RIGHT, O_CHASING_STONE);
1921 break;
1923 case O_REPLICATOR:
1924 if (cave->replicators_wait_frame==0 && cave->replicators_active && !cave->gravity_disabled) {
1925 /* only replicate, if space is under it. */
1926 /* do not replicate players! */
1927 /* also obeys gravity settings. */
1928 /* only replicate element if it is not a scanned one */
1929 /* do not replicate space... that condition looks like it makes no sense,
1930 but otherwise it generates SCANNED spaces, which cannot be "collected" by the player, so he cannot run under a replicator */
1931 if (is_space_dir(cave, x, y, cave->gravity) && !is_player_dir(cave, x, y, opposite[cave->gravity])
1932 && !is_space_dir(cave, x, y, opposite[cave->gravity])) {
1933 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
1934 gd_sound_play(cave, GD_S_REPLICATOR);
1937 break;
1939 case O_BITER_1:
1940 case O_BITER_2:
1941 case O_BITER_3:
1942 case O_BITER_4:
1943 if (cave->biters_wait_frame==0) {
1944 static GdDirection biter_move[]={ MV_UP, MV_RIGHT, MV_DOWN, MV_LEFT };
1945 int dir=get(cave, x, y)-O_BITER_1; /* direction, last two bits 0..3 */
1946 int dirn=(dir+3)&3;
1947 int dirp=(dir+1)&3;
1948 int i;
1949 GdElement made_sound_of=O_NONE;
1951 for (i=0; i<G_N_ELEMENTS (biter_try); i++) {
1952 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i])) {
1953 move(cave, x, y, biter_move[dir], O_BITER_1+dir);
1954 if (biter_try[i]!=O_SPACE)
1955 made_sound_of=O_BITER_1; /* sound of a biter eating */
1956 break;
1958 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i])) {
1959 move(cave, x, y, biter_move[dirn], O_BITER_1+dirn);
1960 if (biter_try[i]!=O_SPACE)
1961 made_sound_of=O_BITER_1; /* sound of a biter eating */
1962 break;
1964 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i])) {
1965 move(cave, x, y, biter_move[dirp], O_BITER_1+dirp);
1966 if (biter_try[i]!=O_SPACE)
1967 made_sound_of=O_BITER_1; /* sound of a biter eating */
1968 break;
1971 if (i==G_N_ELEMENTS (biter_try))
1972 /* i=number of elements in array: could not move, so just turn */
1973 store(cave, x, y, O_BITER_1+dirp);
1974 else if (biter_try[i]==O_STONE) {
1975 /* if there was a stone there, where we moved... do not eat stones, just throw them back */
1976 store(cave, x, y, O_STONE);
1977 made_sound_of=O_STONE;
1980 /* if biter did move, we had sound. play it. */
1981 if (made_sound_of!=O_NONE)
1982 play_sound_of_element(cave, made_sound_of);
1984 break;
1986 case O_DRAGONFLY_1:
1987 case O_DRAGONFLY_2:
1988 case O_DRAGONFLY_3:
1989 case O_DRAGONFLY_4:
1990 /* check if touches a voodoo */
1991 if (get_dir(cave, x, y, MV_LEFT)==O_VOODOO || get_dir(cave, x, y, MV_RIGHT)==O_VOODOO || get_dir(cave, x, y, MV_UP)==O_VOODOO || get_dir(cave, x, y, MV_DOWN)==O_VOODOO)
1992 cave->voodoo_touched=TRUE;
1993 /* check if touches something bad and should explode (includes voodoo by the flags) */
1994 if (blows_up_flies_dir(cave, x, y, MV_DOWN) || blows_up_flies_dir(cave, x, y, MV_UP)
1995 || blows_up_flies_dir(cave, x, y, MV_LEFT) || blows_up_flies_dir(cave, x, y, MV_RIGHT))
1996 explode (cave, x, y);
1997 /* otherwise move */
1998 else {
1999 const GdDirection *creature_move;
2000 gboolean ccw=rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2001 GdElement base=O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2002 int dir, dirn; /* direction */
2004 dir=get(cave, x, y)-base; /* facing where */
2005 creature_move=cave->creatures_backwards? creature_chdir:creature_dir;
2007 /* now change direction if backwards */
2008 if (cave->creatures_backwards)
2009 ccw=!ccw;
2011 if (ccw)
2012 dirn=(dir+3)&3; /* fast turn */
2013 else
2014 dirn=(dir+1)&3; /* fast turn */
2016 /* if can move forward, does so. */
2017 if (is_space_dir(cave, x, y, creature_move[dir]))
2018 move(cave, x, y, creature_move[dir], base+dir);
2019 else
2020 /* otherwise turns 90 degrees in place. */
2021 store(cave, x, y, base+dirn);
2023 break;
2026 case O_BLADDER:
2027 store(cave, x, y, O_BLADDER_1);
2028 break;
2030 case O_BLADDER_1:
2031 case O_BLADDER_2:
2032 case O_BLADDER_3:
2033 case O_BLADDER_4:
2034 case O_BLADDER_5:
2035 case O_BLADDER_6:
2036 case O_BLADDER_7:
2037 case O_BLADDER_8:
2038 /* bladder with any delay state: try to convert to clock. */
2039 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by)
2040 || is_element_dir(cave, x, y, cw_fourth[grav_compat], cave->bladder_converts_by)
2041 || is_element_dir(cave, x, y, ccw_fourth[grav_compat], cave->bladder_converts_by)) {
2042 /* if touches the specified element, let it be a clock */
2043 store(cave, x, y, O_PRE_CLOCK_1);
2044 play_sound_of_element(cave, O_PRE_CLOCK_1); /* plays the bladder convert sound */
2045 } else {
2046 /* is space over the bladder? */
2047 if (is_space_dir(cave, x, y, opposite[grav_compat])) {
2048 if (get(cave, x, y)==O_BLADDER_8) {
2049 /* if it is a bladder 8, really move up */
2050 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2051 play_sound_of_element(cave, O_BLADDER);
2053 else
2054 /* if smaller delay, just increase delay. */
2055 next(cave, x, y);
2057 else
2058 /* if not space, is something sloped over the bladder? */
2059 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) && sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat])) {
2060 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]])
2061 && is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]])
2062 && is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]])) {
2063 /* rolling up, to left */
2064 if (get(cave, x, y)==O_BLADDER_8) {
2065 /* if it is a bladder 8, really roll */
2066 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2067 play_sound_of_element(cave, O_BLADDER);
2068 } else
2069 /* if smaller delay, just increase delay. */
2070 next(cave, x, y);
2072 else
2073 if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]])
2074 && is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]])
2075 && is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]])) {
2076 /* rolling up, to left */
2077 if (get(cave, x, y)==O_BLADDER_8) {
2078 /* if it is a bladder 8, really roll */
2079 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2080 play_sound_of_element(cave, O_BLADDER);
2081 } else
2082 /* if smaller delay, just increase delay. */
2083 next(cave, x, y);
2086 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2087 else
2088 store(cave, x, y, O_BLADDER_1);
2090 break;
2092 case O_GHOST:
2093 if (blows_up_flies_dir(cave, x, y, MV_DOWN) || blows_up_flies_dir(cave, x, y, MV_UP)
2094 || blows_up_flies_dir(cave, x, y, MV_LEFT) || blows_up_flies_dir(cave, x, y, MV_RIGHT))
2095 explode (cave, x, y);
2096 else {
2097 int i;
2099 /* the ghost is given four possibilities to move. */
2100 for (i=0; i<4; i++) {
2101 static GdDirection dirs[]={MV_UP, MV_DOWN, MV_LEFT, MV_RIGHT};
2102 GdDirection random_dir;
2104 random_dir=dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2105 if (is_space_dir(cave, x, y, random_dir)) {
2106 move(cave, x, y, random_dir, O_GHOST);
2107 break; /* ghost did move -> exit loop */
2111 break;
2116 * A C T I V E E L E M E N T S
2119 case O_AMOEBA:
2120 amoeba_count++;
2121 switch (cave->amoeba_state) {
2122 case GD_AM_TOO_BIG:
2123 store(cave, x, y, cave->amoeba_too_big_effect);
2124 break;
2125 case GD_AM_ENCLOSED:
2126 store(cave, x, y, cave->amoeba_enclosed_effect);
2127 break;
2128 case GD_AM_SLEEPING:
2129 case GD_AM_AWAKE:
2130 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2131 if (amoeba_found_enclosed)
2132 /* if still found enclosed, check all four directions, if this one is able to grow. */
2133 if (amoeba_eats_dir(cave, x, y, MV_UP) || amoeba_eats_dir(cave, x, y, MV_DOWN)
2134 || amoeba_eats_dir(cave, x, y, MV_LEFT) || amoeba_eats_dir(cave, x, y, MV_RIGHT)) {
2135 amoeba_found_enclosed=FALSE; /* not enclosed. this is a local (per scan) flag! */
2136 cave->amoeba_state=GD_AM_AWAKE;
2139 /* if alive, check in which dir to grow (or not) */
2140 if (cave->amoeba_state==GD_AM_AWAKE) {
2141 if (g_rand_int_range(cave->random, 0, 1000000)<cave->amoeba_growth_prob) {
2142 switch (g_rand_int_range(cave->random, 0, 4)) { /* decided to grow, choose a random direction. */
2143 case 0: /* let this be up. numbers indifferent. */
2144 if (amoeba_eats_dir(cave, x, y, MV_UP))
2145 store_dir(cave, x, y, MV_UP, O_AMOEBA);
2146 break;
2147 case 1: /* down */
2148 if (amoeba_eats_dir(cave, x, y, MV_DOWN))
2149 store_dir(cave, x, y, MV_DOWN, O_AMOEBA);
2150 break;
2151 case 2: /* left */
2152 if (amoeba_eats_dir(cave, x, y, MV_LEFT))
2153 store_dir(cave, x, y, MV_LEFT, O_AMOEBA);
2154 break;
2155 case 3: /* right */
2156 if (amoeba_eats_dir(cave, x, y, MV_RIGHT))
2157 store_dir(cave, x, y, MV_RIGHT, O_AMOEBA);
2158 break;
2162 break;
2164 break;
2166 case O_AMOEBA_2:
2167 amoeba_2_count++;
2168 /* check if it is touching an amoeba, and explosion is enabled */
2169 if (cave->amoeba_2_explodes_by_amoeba
2170 && (is_element_dir(cave, x, y, MV_DOWN, O_AMOEBA) || is_element_dir(cave, x, y, MV_UP, O_AMOEBA)
2171 || is_element_dir(cave, x, y, MV_LEFT, O_AMOEBA) || is_element_dir(cave, x, y, MV_RIGHT, O_AMOEBA)))
2172 explode (cave, x, y);
2173 else
2174 switch (cave->amoeba_2_state) {
2175 case GD_AM_TOO_BIG:
2176 store(cave, x, y, cave->amoeba_2_too_big_effect);
2177 break;
2178 case GD_AM_ENCLOSED:
2179 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2180 break;
2181 case GD_AM_SLEEPING:
2182 case GD_AM_AWAKE:
2183 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2184 if (amoeba_2_found_enclosed)
2185 if (amoeba_eats_dir(cave, x, y, MV_UP) || amoeba_eats_dir(cave, x, y, MV_DOWN)
2186 || amoeba_eats_dir(cave, x, y, MV_LEFT) || amoeba_eats_dir(cave, x, y, MV_RIGHT)) {
2187 amoeba_2_found_enclosed=FALSE; /* not enclosed. this is a local (per scan) flag! */
2188 cave->amoeba_2_state=GD_AM_AWAKE;
2191 if (cave->amoeba_2_state==GD_AM_AWAKE) /* if it is alive, decide if it attempts to grow */
2192 if (g_rand_int_range(cave->random, 0, 1000000)<cave->amoeba_2_growth_prob) {
2193 switch (g_rand_int_range(cave->random, 0, 4)) { /* decided to grow, choose a random direction. */
2194 case 0: /* let this be up. numbers indifferent. */
2195 if (amoeba_eats_dir(cave, x, y, MV_UP))
2196 store_dir(cave, x, y, MV_UP, O_AMOEBA_2);
2197 break;
2198 case 1: /* down */
2199 if (amoeba_eats_dir(cave, x, y, MV_DOWN))
2200 store_dir(cave, x, y, MV_DOWN, O_AMOEBA_2);
2201 break;
2202 case 2: /* left */
2203 if (amoeba_eats_dir(cave, x, y, MV_LEFT))
2204 store_dir(cave, x, y, MV_LEFT, O_AMOEBA_2);
2205 break;
2206 case 3: /* right */
2207 if (amoeba_eats_dir(cave, x, y, MV_RIGHT))
2208 store_dir(cave, x, y, MV_RIGHT, O_AMOEBA_2);
2209 break;
2212 break;
2214 break;
2216 case O_ACID:
2217 /* choose randomly, if it spreads */
2218 if (g_rand_int_range(cave->random, 0, 1000000)<=cave->acid_spread_ratio) {
2219 /* the current one explodes */
2220 store(cave, x, y, cave->acid_turns_to);
2221 /* and if neighbours are eaten, put acid there. */
2222 if (is_element_dir(cave, x, y, MV_UP, cave->acid_eats_this)) {
2223 play_sound_of_element(cave, O_ACID);
2224 store_dir(cave, x, y, MV_UP, O_ACID);
2226 if (is_element_dir(cave, x, y, MV_DOWN, cave->acid_eats_this)) {
2227 play_sound_of_element(cave, O_ACID);
2228 store_dir(cave, x, y, MV_DOWN, O_ACID);
2230 if (is_element_dir(cave, x, y, MV_LEFT, cave->acid_eats_this)) {
2231 play_sound_of_element(cave, O_ACID);
2232 store_dir(cave, x, y, MV_LEFT, O_ACID);
2234 if (is_element_dir(cave, x, y, MV_RIGHT, cave->acid_eats_this)) {
2235 play_sound_of_element(cave, O_ACID);
2236 store_dir(cave, x, y, MV_RIGHT, O_ACID);
2239 break;
2241 case O_WATER:
2242 found_water=TRUE;
2243 if (!cave->water_does_not_flow_down && is_space_dir(cave, x, y, MV_DOWN)) /* emulating the odd behaviour in crdr */
2244 store_dir(cave, x, y, MV_DOWN, O_WATER_1);
2245 if (is_space_dir(cave, x, y, MV_UP))
2246 store_dir(cave, x, y, MV_UP, O_WATER_1);
2247 if (is_space_dir(cave, x, y, MV_LEFT))
2248 store_dir(cave, x, y, MV_LEFT, O_WATER_1);
2249 if (is_space_dir(cave, x, y, MV_RIGHT))
2250 store_dir(cave, x, y, MV_RIGHT, O_WATER_1);
2251 break;
2253 case O_WATER_16:
2254 store(cave, x, y, O_WATER);
2255 break;
2257 case O_H_EXPANDING_WALL:
2258 case O_V_EXPANDING_WALL:
2259 case O_H_EXPANDING_STEEL_WALL:
2260 case O_V_EXPANDING_STEEL_WALL:
2261 /* checks first if direction is changed. */
2262 if (((get(cave, x, y)==O_H_EXPANDING_WALL || get(cave, x, y)==O_H_EXPANDING_STEEL_WALL) && !cave->expanding_wall_changed)
2263 || ((get(cave, x, y)==O_V_EXPANDING_WALL || get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) && cave->expanding_wall_changed)) {
2264 if (is_space_dir(cave, x, y, MV_LEFT)) {
2265 store_dir(cave, x, y, MV_LEFT, get(cave, x, y));
2266 play_sound_of_element(cave, get(cave, x, y));
2268 if (is_space_dir(cave, x, y, MV_RIGHT)) {
2269 store_dir(cave, x, y, MV_RIGHT, get(cave, x, y));
2270 play_sound_of_element(cave, get(cave, x, y));
2273 else {
2274 if (is_space_dir(cave, x, y, MV_UP)) {
2275 store_dir(cave, x, y, MV_UP, get(cave, x, y));
2276 play_sound_of_element(cave, get(cave, x, y));
2278 if (is_space_dir(cave, x, y, MV_DOWN)) {
2279 store_dir(cave, x, y, MV_DOWN, get(cave, x, y));
2280 play_sound_of_element(cave, get(cave, x, y));
2283 break;
2285 case O_EXPANDING_WALL:
2286 case O_EXPANDING_STEEL_WALL:
2287 /* the wall which grows in all four directions. */
2288 if (is_space_dir(cave, x, y, MV_LEFT)) {
2289 store_dir(cave, x, y, MV_LEFT, get(cave, x, y));
2290 play_sound_of_element(cave, get(cave, x, y));
2292 if (is_space_dir(cave, x, y, MV_RIGHT)) {
2293 store_dir(cave, x, y, MV_RIGHT, get(cave, x, y));
2294 play_sound_of_element(cave, get(cave, x, y));
2296 if (is_space_dir(cave, x, y, MV_UP)) {
2297 store_dir(cave, x, y, MV_UP, get(cave, x, y));
2298 play_sound_of_element(cave, get(cave, x, y));
2300 if (is_space_dir(cave, x, y, MV_DOWN)) {
2301 store_dir(cave, x, y, MV_DOWN, get(cave, x, y));
2302 play_sound_of_element(cave, get(cave, x, y));
2304 break;
2306 case O_SLIME:
2307 g_print("Step[%03d]", cave->frame); /* XXX */
2308 int rrr=gd_cave_c64_random(cave);
2309 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64, (rrr&cave->slime_permeability_c64)==0);
2311 * unpredictable: g_rand_int
2312 * predictable: c64 predictable random generator.
2313 * for predictable, a random number is generated, whether or not it is even possible that the stone
2314 * will be able to pass.
2316 if (cave->slime_predictable? ((rrr /* XXX */ &cave->slime_permeability_c64)==0) : g_rand_int_range(cave->random, 0, 1000000)<cave->slime_permeability) {
2317 GdDirection grav=cave->gravity;
2318 GdDirection oppos=opposite[cave->gravity];
2320 /* space under the slime? elements may pass from top to bottom then. */
2321 if (is_space_dir(cave, x, y, grav)) {
2322 if (get_dir(cave, x, y, oppos)==cave->slime_eats_1) {
2323 store_dir(cave, x, y, grav, cave->slime_converts_1); /* output a falling xy under */
2324 store_dir(cave, x, y, oppos, O_SPACE);
2325 play_sound_of_element(cave, O_SLIME);
2327 else if (get_dir(cave, x, y, oppos)==cave->slime_eats_2) {
2328 store_dir(cave, x, y, grav, cave->slime_converts_2);
2329 store_dir(cave, x, y, oppos, O_SPACE);
2330 play_sound_of_element(cave, O_SLIME);
2332 else if (get_dir(cave, x, y, oppos)==cave->slime_eats_3) {
2333 store_dir(cave, x, y, grav, cave->slime_converts_3);
2334 store_dir(cave, x, y, oppos, O_SPACE);
2335 play_sound_of_element(cave, O_SLIME);
2337 else if (get_dir(cave, x, y, oppos)==O_WAITING_STONE) { /* waiting stones pass without awakening */
2338 store_dir(cave, x, y, grav, O_WAITING_STONE);
2339 store_dir(cave, x, y, oppos, O_SPACE);
2340 play_sound_of_element(cave, O_SLIME);
2342 else if (get_dir(cave, x, y, oppos)==O_CHASING_STONE) { /* chasing stones pass */
2343 store_dir(cave, x, y, grav, O_CHASING_STONE);
2344 store_dir(cave, x, y, oppos, O_SPACE);
2345 play_sound_of_element(cave, O_SLIME);
2347 } else
2348 /* or space over the slime? elements may pass from bottom to up then. */
2349 if (is_space_dir(cave, x, y, oppos)) {
2350 if (get_dir(cave, x, y, grav)==O_BLADDER) { /* bladders move UP the slime */
2351 store_dir(cave, x, y, grav, O_SPACE);
2352 store_dir(cave, x, y, oppos, O_BLADDER_1);
2353 play_sound_of_element(cave, O_SLIME);
2354 } else
2355 if (get_dir(cave, x, y, grav)==O_FLYING_STONE) {
2356 store_dir(cave, x, y, grav, O_SPACE);
2357 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
2358 play_sound_of_element(cave, O_SLIME);
2359 } else
2360 if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND) {
2361 store_dir(cave, x, y, grav, O_SPACE);
2362 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
2363 play_sound_of_element(cave, O_SLIME);
2367 break;
2369 case O_FALLING_WALL:
2370 if (is_space_dir(cave, x, y, grav_compat)) {
2371 /* try falling if space under. */
2372 int yy;
2373 for (yy=y+1; yy<y+cave->h; yy++)
2374 /* yy<y+cave->h is to check everything OVER the wall - since caves wrap around !! */
2375 if (get(cave, x, yy)!=O_SPACE)
2376 /* stop cycle when other than space */
2377 break;
2378 /* if scanning stopped by a player... start falling! */
2379 if (get(cave, x, yy)==O_PLAYER || get(cave, x, yy)==O_PLAYER_GLUED || get(cave, x, yy)==O_PLAYER_BOMB) {
2380 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2381 /* no sound when the falling wall starts falling! */
2384 break;
2386 case O_FALLING_WALL_F:
2387 switch (get_dir(cave, x, y, grav_compat)) {
2388 case O_PLAYER:
2389 case O_PLAYER_GLUED:
2390 case O_PLAYER_BOMB:
2391 /* if player under, it explodes - the falling wall, not the player! */
2392 explode (cave, x, y);
2393 break;
2394 case O_SPACE:
2395 /* continue falling */
2396 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2397 break;
2398 default:
2399 /* stop */
2400 play_sound_of_element(cave, get(cave, x, y));
2401 store(cave, x, y, O_FALLING_WALL);
2402 break;
2404 break;
2408 * C O N V E Y O R B E L T S
2410 case O_CONVEYOR_RIGHT:
2411 case O_CONVEYOR_LEFT:
2412 /* only works if gravity is up or down!!! */
2413 /* first, check for gravity and running belts. */
2414 if (!cave->gravity_disabled && cave->conveyor_belts_active) {
2415 const GdDirection *dir;
2416 gboolean left;
2418 /* decide direction */
2419 left=get(cave, x, y)!=O_CONVEYOR_RIGHT;
2420 if (cave->conveyor_belts_direction_changed)
2421 left=!left;
2422 dir=left?ccw_eighth:cw_eighth;
2424 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
2425 /* if gravity is normal, and the conveyor belt has something ABOVE which can be moved
2427 the gravity is up, so anything that should float now goes DOWN and touches the conveyor */
2428 if ((cave->gravity==MV_DOWN && moved_by_conveyor_top_dir(cave, x, y, MV_UP))
2429 || (cave->gravity==MV_UP && moved_by_conveyor_bottom_dir(cave, x, y, MV_UP))) {
2430 if (!is_scanned_dir(cave, x, y, MV_UP) && is_space_dir(cave, x, y, dir[MV_UP]))
2432 store_dir(cave, x, y, dir[MV_UP], get_dir(cave, x, y, MV_UP)); /* move */
2433 store_dir(cave, x, y, MV_UP, O_SPACE); /* and place a space. */
2436 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
2437 if ((cave->gravity==MV_UP && moved_by_conveyor_top_dir(cave, x, y, MV_DOWN))
2438 || (cave->gravity==MV_DOWN && moved_by_conveyor_bottom_dir(cave, x, y, MV_DOWN))) {
2439 if (!is_scanned_dir(cave, x, y, MV_DOWN) && is_space_dir(cave, x, y, dir[MV_DOWN]))
2441 store_dir(cave, x, y, dir[MV_DOWN], get_dir(cave, x, y, MV_DOWN)); /* move */
2442 store_dir(cave, x, y, MV_DOWN, O_SPACE); /* and clear. */
2446 break;
2449 * S I M P L E C H A N G I N G; E X P L O S I O N S
2451 case O_EXPLODE_5:
2452 store(cave, x, y, cave->explosion_effect);
2453 break;
2454 case O_NUT_EXPL_4:
2455 store(cave, x, y, O_DIAMOND);
2456 break;
2457 case O_PRE_DIA_5:
2458 store(cave, x, y, cave->diamond_birth_effect);
2459 break;
2460 case O_PRE_STONE_4:
2461 store(cave, x, y, O_STONE);
2462 break;
2464 case O_NITRO_EXPL_4:
2465 store(cave, x, y, cave->nitro_explosion_effect);
2466 break;
2467 case O_BOMB_EXPL_4:
2468 store(cave, x, y, cave->bomb_explosion_effect);
2469 break;
2470 case O_AMOEBA_2_EXPL_4:
2471 store(cave, x, y, cave->amoeba_2_explosion_effect);
2472 break;
2474 case O_GHOST_EXPL_4:
2476 static GdElement ghost_explode[]={
2477 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
2478 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
2479 O_WAITING_STONE, O_BITER_1
2482 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
2484 break;
2485 case O_PRE_STEEL_4:
2486 store(cave, x, y, O_STEEL);
2487 break;
2488 case O_PRE_CLOCK_4:
2489 store(cave, x, y, O_CLOCK);
2490 break;
2491 case O_BOMB_TICK_7:
2492 explode(cave, x, y);
2493 break;
2495 case O_TRAPPED_DIAMOND:
2496 if (cave->diamond_key_collected)
2497 store(cave, x, y, O_DIAMOND);
2498 break;
2500 case O_PRE_OUTBOX:
2501 if (cave->gate_open) /* if no more diamonds needed */
2502 store(cave, x, y, O_OUTBOX); /* open outbox */
2503 break;
2504 case O_PRE_INVIS_OUTBOX:
2505 if (cave->gate_open) /* if no more diamonds needed */
2506 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
2507 break;
2508 case O_INBOX:
2509 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
2510 store(cave, x, y, O_PRE_PL_1);
2511 inbox_toggle=!inbox_toggle;
2512 break;
2513 case O_PRE_PL_3:
2514 store(cave, x, y, O_PLAYER);
2515 break;
2517 case O_PRE_DIA_1:
2518 case O_PRE_DIA_2:
2519 case O_PRE_DIA_3:
2520 case O_PRE_DIA_4:
2521 case O_PRE_STONE_1:
2522 case O_PRE_STONE_2:
2523 case O_PRE_STONE_3:
2524 case O_BOMB_TICK_1:
2525 case O_BOMB_TICK_2:
2526 case O_BOMB_TICK_3:
2527 case O_BOMB_TICK_4:
2528 case O_BOMB_TICK_5:
2529 case O_BOMB_TICK_6:
2530 case O_PRE_STEEL_1:
2531 case O_PRE_STEEL_2:
2532 case O_PRE_STEEL_3:
2533 case O_BOMB_EXPL_1:
2534 case O_BOMB_EXPL_2:
2535 case O_BOMB_EXPL_3:
2536 case O_NUT_EXPL_1:
2537 case O_NUT_EXPL_2:
2538 case O_NUT_EXPL_3:
2539 case O_GHOST_EXPL_1:
2540 case O_GHOST_EXPL_2:
2541 case O_GHOST_EXPL_3:
2542 case O_EXPLODE_1:
2543 case O_EXPLODE_2:
2544 case O_EXPLODE_3:
2545 case O_EXPLODE_4:
2546 case O_PRE_PL_1:
2547 case O_PRE_PL_2:
2548 case O_PRE_CLOCK_1:
2549 case O_PRE_CLOCK_2:
2550 case O_PRE_CLOCK_3:
2551 case O_NITRO_EXPL_1:
2552 case O_NITRO_EXPL_2:
2553 case O_NITRO_EXPL_3:
2554 case O_AMOEBA_2_EXPL_1:
2555 case O_AMOEBA_2_EXPL_2:
2556 case O_AMOEBA_2_EXPL_3:
2557 /* simply the next identifier */
2558 next(cave, x, y);
2559 break;
2560 case O_WATER_1:
2561 case O_WATER_2:
2562 case O_WATER_3:
2563 case O_WATER_4:
2564 case O_WATER_5:
2565 case O_WATER_6:
2566 case O_WATER_7:
2567 case O_WATER_8:
2568 case O_WATER_9:
2569 case O_WATER_10:
2570 case O_WATER_11:
2571 case O_WATER_12:
2572 case O_WATER_13:
2573 case O_WATER_14:
2574 case O_WATER_15:
2575 found_water=TRUE; /* for sound */
2576 /* simply the next identifier */
2577 next(cave, x, y);
2578 break;
2580 case O_BLADDER_SPENDER:
2581 if (is_space_dir(cave, x, y, opposite[grav_compat])) {
2582 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
2583 store(cave, x, y, O_PRE_STEEL_1);
2584 play_sound_of_element(cave, O_BLADDER_SPENDER);
2586 break;
2590 default:
2591 /* other inanimate elements that do nothing */
2592 break;
2596 /* POSTPROCESSING */
2598 /* another scan-like routine: */
2599 /* short explosions (for example, in bd1) started with explode_2. */
2600 /* internally we use explode_1; and change it to explode_2 if needed. */
2601 if (cave->short_explosions)
2602 for (y=0; y<cave->h; y++)
2603 for (x=0; x<cave->w; x++)
2604 if (is_first_stage_of_explosion(cave, x, y)) {
2605 next(cave, x, y); /* select next frame of explosion */
2606 store(cave, x, y, get(cave, x, y)&~SCANNED); /* forget scanned flag immediately */
2609 /* finally: forget "scanned" flags for objects. */
2610 /* also, check for time penalties. */
2611 /* these is something like an effect table, but we do not really use one. */
2612 for (y=0; y<cave->h; y++)
2613 for (x=0; x<cave->w; x++) {
2614 if (get(cave, x, y)&SCANNED)
2615 store(cave, x, y, get(cave, x, y)&~SCANNED);
2616 if (get(cave, x, y)==O_TIME_PENALTY) {
2617 store(cave, x, y, O_GRAVESTONE);
2618 time_decrement_sec+=cave->time_penalty; /* there is time penalty for destroying the voodoo */
2623 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
2624 /* but we only do this, if a living player was found. if not yet, the setup routine coordinates are used */
2625 if (cave->player_state==GD_PL_LIVING) {
2626 if (cave->active_is_first_found) {
2627 /* to be 1stb compatible, we do everything backwards. */
2628 for (y=cave->h-1; y>=0; y--)
2629 for (x=cave->w-1; x>=0; x--)
2630 if (is_player(cave, x, y)) {
2631 /* here we remember the coordinates. */
2632 cave->player_x=x;
2633 cave->player_y=y;
2636 else
2638 /* as in the original: look for the last one */
2639 for (y=0; y<cave->h; y++)
2640 for (x=0; x<cave->w; x++)
2641 if (is_player(cave, x, y)) {
2642 /* here we remember the coordinates. */
2643 cave->player_x=x;
2644 cave->player_y=y;
2649 /* record coordinates of player for chasing stone */
2650 for (i=0; i<G_N_ELEMENTS(cave->px)-1; i++) {
2651 cave->px[i]=cave->px[i+1];
2652 cave->py[i]=cave->py[i+1];
2654 cave->px[G_N_ELEMENTS(cave->px)-1]=cave->player_x;
2655 cave->py[G_N_ELEMENTS(cave->py)-1]=cave->player_y;
2657 /* SCHEDULING */
2659 /* update timing calculated by iterating and counting elements which
2660 were slow to process on c64 */
2661 switch (cave->scheduling) {
2662 case GD_SCHEDULING_MILLISECONDS:
2663 /* cave->speed already contains the milliseconds value, do not touch it */
2664 break;
2666 case GD_SCHEDULING_BD1:
2667 if (!cave->intermission)
2668 /* non-intermissions */
2669 cave->speed=(88+3.66*cave->c64_timing+(cave->ckdelay+cave->ckdelay_extra_for_animation)/1000);
2670 else
2671 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
2672 cave->speed=(60+3.66*cave->c64_timing+(cave->ckdelay+cave->ckdelay_extra_for_animation)/1000);
2673 break;
2675 case GD_SCHEDULING_BD1_ATARI:
2676 /* about 20ms/frame faster than c64 version */
2677 if (!cave->intermission)
2678 cave->speed=(74+3.2*cave->c64_timing+(cave->ckdelay)/1000); /* non-intermissions */
2679 else
2680 cave->speed=(65+2.88*cave->c64_timing+(cave->ckdelay)/1000); /* for intermissions */
2681 break;
2683 case GD_SCHEDULING_BD2:
2684 /* 60 is a guess. */
2685 cave->speed=MAX(60+(cave->ckdelay+cave->ckdelay_extra_for_animation)/1000, cave->c64_timing*20);
2686 break;
2688 case GD_SCHEDULING_PLCK:
2689 /* 65 is totally empty cave in construction kit, with delay=0) */
2690 cave->speed=MAX(65+cave->ckdelay/1000, cave->c64_timing*20);
2691 break;
2693 case GD_SCHEDULING_BD2_PLCK_ATARI:
2694 /* a really fast engine; timing works like c64 plck. */
2695 /* 40 ms was measured in the construction kit, with delay=0 */
2696 cave->speed=MAX(40+cave->ckdelay/1000, cave->c64_timing*20);
2697 break;
2699 case GD_SCHEDULING_CRDR:
2700 if (cave->hammered_walls_reappear) /* this made the engine very slow. */
2701 cave->ckdelay+=60000;
2702 cave->speed=MAX(130+cave->ckdelay/1000, cave->c64_timing*20);
2703 break;
2705 case GD_SCHEDULING_MAX:
2706 /* to avoid compiler warning */
2707 g_assert_not_reached();
2708 break;
2711 /* cave 3 sounds. precedence is controlled by the sound_play function. */
2712 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
2713 if (found_water && cave->water_sound)
2714 gd_sound_play(cave, GD_S_WATER);
2715 magic_sound=cave->magic_wall_state==GD_MW_ACTIVE && cave->magic_wall_sound;
2716 amoeba_sound=cave->hatched && cave->amoeba_sound && ((amoeba_count>0 && cave->amoeba_state==GD_AM_AWAKE) || (amoeba_2_count>0 && cave->amoeba_2_state==GD_AM_AWAKE));
2717 if (amoeba_sound && magic_sound)
2718 gd_sound_play(cave, GD_S_AMOEBA_MAGIC);
2719 else
2720 if (amoeba_sound)
2721 gd_sound_play(cave, GD_S_AMOEBA);
2722 else
2723 if (magic_sound)
2724 gd_sound_play(cave, GD_S_MAGIC_WALL);
2725 if (cave->hatched)
2726 if ((amoeba_count>0 && cave->amoeba_state==GD_AM_AWAKE)
2727 || (amoeba_2_count>0 && cave->amoeba_2_state==GD_AM_AWAKE))
2728 play_sound_of_element(cave, O_AMOEBA);
2729 /* pneumatic hammer sound - overrides everything. */
2730 if (cave->pneumatic_hammer_active_delay>0 && cave->pneumatic_hammer_sound)
2731 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER);
2733 /* CAVE VARIABLES */
2735 /* PLAYER */
2736 if ((cave->player_state==GD_PL_LIVING && cave->player_seen_ago>15) || cave->kill_player) /* check if player is alive. */
2737 cave->player_state=GD_PL_DIED;
2738 if (cave->voodoo_touched) /* check if any voodoo exploded, and kill players the next scan if that happended. */
2739 cave->kill_player=TRUE;
2741 /* AMOEBA */
2742 if (cave->amoeba_state==GD_AM_AWAKE) {
2743 /* check flags after evaluating. */
2744 if (amoeba_count>=cave->amoeba_max_count)
2745 cave->amoeba_state=GD_AM_TOO_BIG;
2746 if (amoeba_found_enclosed)
2747 cave->amoeba_state=GD_AM_ENCLOSED;
2749 /* amoeba can also be turned into diamond by magic wall */
2750 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state==GD_MW_ACTIVE)
2751 cave->amoeba_state=GD_AM_ENCLOSED;
2752 /* AMOEBA 2 */
2753 if (cave->amoeba_2_state==GD_AM_AWAKE) {
2754 /* check flags after evaluating. */
2755 if (amoeba_2_count>=cave->amoeba_2_max_count)
2756 cave->amoeba_2_state=GD_AM_TOO_BIG;
2757 if (amoeba_2_found_enclosed)
2758 cave->amoeba_2_state=GD_AM_ENCLOSED;
2760 /* amoeba 2 can also be turned into diamond by magic wall */
2761 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state==GD_MW_ACTIVE)
2762 cave->amoeba_2_state=GD_AM_ENCLOSED;
2765 /* now check times. --------------------------- */
2766 /* decrement time if a voodoo was killed. */
2767 cave->time-=time_decrement_sec*cave->timing_factor;
2768 if (cave->time<0)
2769 cave->time=0;
2771 /* only decrement time when player is already born. */
2772 if (cave->hatched) {
2773 int secondsbefore, secondsafter;
2775 secondsbefore=cave->time/cave->timing_factor;
2776 cave->time-=cave->speed;
2777 if (cave->time<=0)
2778 cave->time=0;
2779 secondsafter=cave->time/cave->timing_factor;
2780 if (cave->time/cave->timing_factor<10)
2781 /* if less than 10 seconds, no walking sound, but play explosion sound */
2782 gd_sound_play(cave, GD_S_NONE);
2783 if (secondsbefore!=secondsafter)
2784 gd_cave_set_seconds_sound(cave);
2786 /* a gravity switch was activated; seconds counting down */
2787 if (cave->gravity_will_change>0) {
2788 cave->gravity_will_change-=cave->speed;
2789 if (cave->gravity_will_change<0)
2790 cave->gravity_will_change=0;
2792 if (cave->gravity_will_change==0) {
2793 cave->gravity=cave->gravity_next_direction;
2794 if (cave->gravity_change_sound)
2795 gd_sound_play(cave, GD_S_GRAVITY_CHANGE); /* takes precedence over amoeba and magic wall sound */
2799 /* creatures direction automatically change */
2800 if (cave->creatures_direction_will_change>0) {
2801 cave->creatures_direction_will_change-=cave->speed;
2802 if (cave->creatures_direction_will_change<0)
2803 cave->creatures_direction_will_change=0;
2805 if (cave->creatures_direction_will_change==0) {
2806 if (cave->creature_direction_auto_change_sound)
2807 gd_sound_play(cave, GD_S_SWITCH_CREATURES);
2808 cave->creatures_backwards=!cave->creatures_backwards;
2809 cave->creatures_direction_will_change=cave->creatures_direction_auto_change_time*cave->timing_factor;
2813 /* magic wall; if active&wait or not wait for hatching */
2814 if (cave->magic_wall_state==GD_MW_ACTIVE && (cave->hatched || !cave->magic_timer_wait_for_hatching)) {
2815 cave->magic_wall_time-=cave->speed;
2816 if (cave->magic_wall_time<0)
2817 cave->magic_wall_time=0;
2818 if (cave->magic_wall_time==0)
2819 cave->magic_wall_state=GD_MW_EXPIRED;
2821 /* we may wait for hatching, when starting amoeba */
2822 if (cave->amoeba_timer_started_immediately || (cave->amoeba_state==GD_AM_AWAKE && (cave->hatched || !cave->amoeba_timer_wait_for_hatching))) {
2823 cave->amoeba_time-=cave->speed;
2824 if (cave->amoeba_time<0)
2825 cave->amoeba_time=0;
2826 if (cave->amoeba_time==0)
2827 cave->amoeba_growth_prob=cave->amoeba_fast_growth_prob;
2829 /* we may wait for hatching, when starting amoeba */
2830 if (cave->amoeba_timer_started_immediately || (cave->amoeba_2_state==GD_AM_AWAKE && (cave->hatched || !cave->amoeba_timer_wait_for_hatching))) {
2831 cave->amoeba_2_time-=cave->speed;
2832 if (cave->amoeba_2_time<0)
2833 cave->amoeba_2_time=0;
2834 if (cave->amoeba_2_time==0)
2835 cave->amoeba_2_growth_prob=cave->amoeba_2_fast_growth_prob;
2838 /* check for player hatching. */
2839 start_signal=FALSE;
2840 /* if not the c64 scheduling, but the correct frametime is used, hatching delay should always be decremented. */
2841 /* otherwise, the if (millisecs...) condition below will set this. */
2842 if (cave->scheduling==GD_SCHEDULING_MILLISECONDS) { /* NON-C64 scheduling */
2843 if (cave->hatching_delay_frame>0) {
2844 cave->hatching_delay_frame--; /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
2845 if (cave->hatching_delay_frame==0)
2846 start_signal=TRUE;
2849 else { /* C64 scheduling */
2850 if (cave->hatching_delay_time>0) {
2851 cave->hatching_delay_time-=cave->speed; /* for c64 schedulings, hatching delay means milliseconds. */
2852 if (cave->hatching_delay_time<=0) {
2853 cave->hatching_delay_time=0;
2854 start_signal=TRUE;
2859 /* if decremented hatching, and it became zero: */
2860 if (start_signal) { /* THIS IS THE CAVE START SIGNAL */
2861 cave->hatched=TRUE; /* record that now the cave is in its normal state */
2863 gd_cave_count_diamonds(cave); /* if diamonds needed is below zero, we count the available diamonds now. */
2865 /* setup direction auto change */
2866 if (cave->creatures_direction_auto_change_time) {
2867 cave->creatures_direction_will_change=cave->creatures_direction_auto_change_time*cave->timing_factor;
2869 if (cave->creatures_direction_auto_change_on_start)
2870 cave->creatures_backwards=!cave->creatures_backwards;
2873 gd_sound_play(cave, GD_S_CRACK);
2876 /* for biters */
2877 if (cave->biters_wait_frame==0)
2878 cave->biters_wait_frame=cave->biter_delay_frame;
2879 else
2880 cave->biters_wait_frame--;
2881 /* replicators delay */
2882 if (cave->replicators_wait_frame==0)
2883 cave->replicators_wait_frame=cave->replicator_delay_frame;
2884 else
2885 cave->replicators_wait_frame--;
2888 /* LAST THOUGTS */
2890 /* check if cave failed by timeout */
2891 if (cave->player_state==GD_PL_LIVING && cave->time==0) {
2892 gd_cave_clear_sounds(cave);
2893 cave->player_state=GD_PL_TIMEOUT;
2894 gd_sound_play(cave, GD_S_TIMEOUT);
2897 /* set these for drawing. */
2898 cave->last_direction=player_move;
2899 /* here we remember last movements for animation. this is needed here, as animation
2900 is in sync with the game, not the keyboard directly. (for example, after exiting
2901 the cave, the player was "running" in the original, till bonus points were counted
2902 for remaining time and so on. */
2903 if (player_move==MV_LEFT || player_move==MV_UP_LEFT || player_move==MV_DOWN_LEFT)
2904 cave->last_horizontal_direction=MV_LEFT;
2905 if (player_move==MV_RIGHT || player_move==MV_UP_RIGHT || player_move==MV_DOWN_RIGHT)
2906 cave->last_horizontal_direction=MV_RIGHT;
2910 cave->frame++; /* XXX */