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.
22 * Lava absorbs everything going into it. Everything.
23 * But it does not "pull" elements; only the things disappear which
24 * _do_ go directly into it. So if the player steps into the lava,
25 * he will die. If a dragonfly flies over it, it will not.
27 * This behavior is implemented in the is_space_dir and the store
28 * functions. is_space_dir returns true for the lava, too. The store
29 * function ignores any store requests into the lava.
30 * The player_get function will also behave for lava as it does for space.
37 static const GdDirection ccw_eighth[] =
49 static const GdDirection ccw_fourth[] =
63 static const GdDirection cw_eighth[] =
76 static const GdDirection cw_fourth[] =
89 static const GdDirection opposite[] =
102 // sets timeout sound.
103 void gd_cave_set_seconds_sound(GdCave *cave)
105 // when not counting bonus time, timeout sounds will be played by main game engine;
106 // also skip timeout sounds when not using native sound engine
107 if (game_bd.game == NULL || game_bd.game->state_counter != GAME_INT_CHECK_BONUS_TIME ||
108 !game.use_native_bd_sound_engine)
111 // this is an integer division, so 0 seconds can be 0.5 seconds...
112 // also, when this reaches 8, the player still has 8.9999 seconds.
113 // so the sound is played at almost t = 9s.
114 switch (cave->time / cave->timing_factor)
116 case 9: gd_sound_play(cave, GD_S_TIMEOUT_10, O_NONE, -1, -1); break;
117 case 8: gd_sound_play(cave, GD_S_TIMEOUT_9, O_NONE, -1, -1); break;
118 case 7: gd_sound_play(cave, GD_S_TIMEOUT_8, O_NONE, -1, -1); break;
119 case 6: gd_sound_play(cave, GD_S_TIMEOUT_7, O_NONE, -1, -1); break;
120 case 5: gd_sound_play(cave, GD_S_TIMEOUT_6, O_NONE, -1, -1); break;
121 case 4: gd_sound_play(cave, GD_S_TIMEOUT_5, O_NONE, -1, -1); break;
122 case 3: gd_sound_play(cave, GD_S_TIMEOUT_4, O_NONE, -1, -1); break;
123 case 2: gd_sound_play(cave, GD_S_TIMEOUT_3, O_NONE, -1, -1); break;
124 case 1: gd_sound_play(cave, GD_S_TIMEOUT_2, O_NONE, -1, -1); break;
125 case 0: gd_sound_play(cave, GD_S_TIMEOUT_1, O_NONE, -1, -1); break;
129 // returns true if the element can fall
130 static inline boolean el_can_fall(const int element)
132 return (gd_elements[element & O_MASK].properties & P_CAN_FALL) != 0;
135 // play diamond or stone sound of given element.
136 static void play_sound_of_element(GdCave *cave, GdElement element, int x, int y)
138 // check if sound should be skipped for falling elements (and only be played on impact)
139 if (el_can_fall(element) && skip_bd_falling_sounds())
142 // stone and diamond fall sounds.
146 gd_sound_play(cave, GD_S_NUT_FALLING, element, x, y);
150 gd_sound_play(cave, GD_S_NUT_IMPACT, element, x, y);
154 gd_sound_play(cave, GD_S_STONE_FALLING, element, x, y);
158 gd_sound_play(cave, GD_S_STONE_IMPACT, element, x, y);
162 gd_sound_play(cave, GD_S_FLYING_STONE_FALLING, element, x, y);
165 case O_FLYING_STONE_F:
166 gd_sound_play(cave, GD_S_FLYING_STONE_IMPACT, element, x, y);
170 gd_sound_play(cave, GD_S_MEGA_STONE_FALLING, element, x, y);
174 gd_sound_play(cave, GD_S_MEGA_STONE_IMPACT, element, x, y);
178 gd_sound_play(cave, GD_S_NITRO_PACK_FALLING, element, x, y);
182 gd_sound_play(cave, GD_S_NITRO_PACK_IMPACT, element, x, y);
186 gd_sound_play(cave, GD_S_FALLING_WALL_FALLING, element, x, y);
189 case O_FALLING_WALL_F:
190 gd_sound_play(cave, GD_S_FALLING_WALL_IMPACT, element, x, y);
193 case O_H_EXPANDING_WALL:
194 case O_V_EXPANDING_WALL:
195 case O_EXPANDING_WALL:
196 case O_H_EXPANDING_STEEL_WALL:
197 case O_V_EXPANDING_STEEL_WALL:
198 case O_EXPANDING_STEEL_WALL:
199 gd_sound_play(cave, GD_S_EXPANDING_WALL, element, x, y);
203 gd_sound_play(cave, GD_S_DIAMOND_FALLING_RANDOM, element, x, y);
207 gd_sound_play(cave, GD_S_DIAMOND_IMPACT_RANDOM, element, x, y);
210 case O_FLYING_DIAMOND:
211 gd_sound_play(cave, GD_S_FLYING_DIAMOND_FALLING_RANDOM, element, x, y);
214 case O_FLYING_DIAMOND_F:
215 gd_sound_play(cave, GD_S_FLYING_DIAMOND_IMPACT_RANDOM, element, x, y);
218 case O_BLADDER_SPENDER:
219 gd_sound_play(cave, GD_S_BLADDER_SPENDER, element, x, y);
223 gd_sound_play(cave, GD_S_BLADDER_CONVERTING, element, x, y);
227 gd_sound_play(cave, GD_S_SLIME, element, x, y);
231 gd_sound_play(cave, GD_S_LAVA, element, x, y);
235 gd_sound_play(cave, GD_S_ACID_SPREADING, element, x, y);
239 gd_sound_play(cave, GD_S_BLADDER_MOVING, element, x, y);
246 gd_sound_play(cave, GD_S_BITER_EATING, element, x, y);
250 gd_sound_play(cave, GD_S_DIRT_BALL_FALLING, element, x, y);
254 gd_sound_play(cave, GD_S_DIRT_BALL_IMPACT, element, x, y);
258 gd_sound_play(cave, GD_S_DIRT_LOOSE_FALLING, element, x, y);
262 gd_sound_play(cave, GD_S_DIRT_LOOSE_IMPACT, element, x, y);
271 // play sound of given element being pushed.
272 static void play_sound_of_element_pushing(GdCave *cave, GdElement element, int x, int y)
277 gd_sound_play(cave, GD_S_NUT_PUSHING, element, x, y);
281 gd_sound_play(cave, GD_S_STONE_PUSHING, element, x, y);
285 gd_sound_play(cave, GD_S_FLYING_STONE_PUSHING, element, x, y);
289 gd_sound_play(cave, GD_S_MEGA_STONE_PUSHING, element, x, y);
292 case O_WAITING_STONE:
293 gd_sound_play(cave, GD_S_WAITING_STONE_PUSHING, element, x, y);
296 case O_CHASING_STONE:
297 gd_sound_play(cave, GD_S_CHASING_STONE_PUSHING, element, x, y);
301 gd_sound_play(cave, GD_S_NITRO_PACK_PUSHING, element, x, y);
305 gd_sound_play(cave, GD_S_BLADDER_PUSHING, element, x, y);
314 static inline int getx(const GdCave *cave, const int x, const int y)
316 return cave->getx(cave, x, y);
319 static inline int gety(const GdCave *cave, const int x, const int y)
321 return cave->gety(cave, x, y);
324 // perfect (non-lineshifting) GET x/y functions; returns range corrected x/y position
325 static inline int getx_perfect(const GdCave *cave, const int x, const int y)
327 return (x + cave->w) % cave->w;
330 static inline int gety_perfect(const GdCave *cave, const int x, const int y)
332 return (y + cave->h) % cave->h;
335 // line shifting GET x/y function; returns range corrected x/y position
336 static inline int getx_shift(const GdCave *cave, int x, int y)
338 return (x + cave->w) % cave->w;
341 static inline int gety_shift(const GdCave *cave, int x, int y)
343 return ((x < 0 ? y - 1 : x >= cave->w ? y + 1 : y) + cave->h) % cave->h;
346 static inline GdElement *getp(const GdCave *cave, const int x, const int y)
348 return cave->getp(cave, x, y);
352 perfect (non-lineshifting) GET function.
353 returns a pointer to a selected cave element by its coordinates.
355 static inline GdElement *getp_perfect(const GdCave *cave, const int x, const int y)
357 // (x + n) mod n: this works also for x >= n and -n + 1 < x < 0
358 return &(cave->map[(y + cave->h) % cave->h][(x + cave->w) % cave->w]);
362 line shifting GET function; returns a pointer to the selected cave element.
363 this is used to emulate the line-shifting behaviour of original games, so that
364 the player entering one side will appear one row above or below on the other.
366 static inline GdElement *getp_shift(const GdCave *cave, int x, int y)
379 y = (y + cave->h) % cave->h;
381 return &(cave->map[y][x]);
384 static inline GdElement get(const GdCave *cave, const int x, const int y)
386 return *getp(cave, x, y);
389 // returns an element which is somewhere near x,y
390 static inline GdElement get_dir(const GdCave *cave, const int x, const int y,
391 const GdDirection dir)
393 return get(cave, x + gd_dx[dir], y + gd_dy[dir]);
396 static inline boolean explodes_by_hit_dir(const GdCave *cave, const int x,
397 const int y, GdDirection dir)
399 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_EXPLODES_BY_HIT) != 0;
402 // returns true if the element is not explodable, for example the steel wall
403 static inline boolean non_explodable(const GdCave *cave, const int x, const int y)
405 return (gd_elements[get(cave, x,y) & O_MASK].properties & P_NON_EXPLODABLE) != 0;
408 // returns true if the element can be eaten by the amoeba, eg. space and dirt.
409 static inline boolean amoeba_eats_dir(const GdCave *cave, const int x, const int y,
410 const GdDirection dir)
412 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_AMOEBA_CONSUMES) != 0;
415 // returns true if the element is sloped, so stones and diamonds roll down on it.
416 // for example a stone or brick wall
417 static inline boolean sloped_dir(const GdCave *cave, const int x, const int y,
418 const GdDirection dir, const GdDirection slop)
423 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_LEFT) != 0;
426 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_RIGHT) != 0;
429 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_UP) != 0;
432 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_DOWN) != 0;
441 // returns true if the element is sloped for bladder movement
442 // (brick = yes, diamond = no, for example)
443 static inline boolean sloped_for_bladder_dir (const GdCave *cave, const int x, const int y,
444 const GdDirection dir)
446 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLADDER_SLOPED) != 0;
449 static inline boolean blows_up_flies_dir(const GdCave *cave, const int x, const int y,
450 const GdDirection dir)
452 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLOWS_UP_FLIES) != 0;
455 // returns true if the element is a counter-clockwise creature
456 static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
458 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_CCW) != 0;
461 // returns true if the element is a player
462 boolean is_player(const GdCave *cave, const int x, const int y)
464 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
467 // returns true if the element is a player
468 static inline boolean is_player_dir(const GdCave *cave, const int x, const int y,
469 const GdDirection dir)
471 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PLAYER) != 0;
474 static inline boolean can_be_hammered_dir(const GdCave *cave, const int x, const int y,
475 const GdDirection dir)
477 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
480 // returns true if the element can be pushed
481 boolean can_be_pushed_dir(const GdCave *cave, const int x, const int y,
482 const GdDirection dir)
484 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PUSHABLE) != 0;
487 // returns true if the element is explodable and explodes to space, for example the player
488 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
490 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
493 // returns true if the element is moved by the conveyor belt
494 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
495 const GdDirection dir)
497 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
500 // returns true if the element is moved by the conveyor belt
501 static inline boolean moved_by_conveyor_bottom_dir(const GdCave *cave, const int x, const int y,
502 const GdDirection dir)
504 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_BOTTOM) != 0;
507 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
508 const GdDirection dir)
510 return (get_dir(cave, x, y, dir) & SCANNED) != 0;
513 // returns true if neighbouring element is "e"
514 // treats dirt specially
515 // treats lava specially
516 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
517 const GdDirection dir, GdElement e)
519 GdElement examined = get_dir(cave, x, y, dir);
521 // if it is a dirt-like, change to dirt, so equality will evaluate to true
522 if (gd_elements[examined & O_MASK].properties & P_DIRT)
525 if (gd_elements[e & O_MASK].properties & P_DIRT)
528 // if the element on the map is a lava, it should be like space
529 if (examined == O_LAVA)
532 return (e == examined);
535 // returns true if neighbouring element is space
536 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
537 const GdDirection dir)
539 GdElement e = get_dir(cave, x, y, dir) & O_MASK;
541 return (e == O_SPACE || e == O_LAVA);
544 static inline void store_dir_buffer(GdCave *cave, const int x, const int y, const GdDirection dir)
546 // raw values without range correction
547 int raw_x = x + gd_dx[dir];
548 int raw_y = y + gd_dy[dir];
550 // final values with range correction
551 int new_x = getx(cave, raw_x, raw_y);
552 int new_y = gety(cave, raw_x, raw_y);
553 int new_dir = (dir > GD_MV_TWICE ? dir - GD_MV_TWICE : dir);
555 game_bd.game->dir_buffer[new_y][new_x] = new_dir;
558 // store an element at the given position
559 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
561 GdElement *e = getp(cave, x, y);
565 play_sound_of_element(cave, O_LAVA, x, y);
573 // store an element with SCANNED flag turned on
574 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
576 store(cave, x, y, element | SCANNED);
579 // store an element to a neighbouring cell
580 static inline void store_dir(GdCave *cave, const int x, const int y,
581 const GdDirection dir, const GdElement element)
583 store_dir_buffer(cave, x, y, dir);
584 store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED);
587 // store an element to a neighbouring cell
588 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
589 const GdDirection dir, const GdElement element)
591 store_dir_buffer(cave, x, y, dir);
592 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
595 // move element to direction; then place space at x, y
596 static inline void move(GdCave *cave, const int x, const int y,
597 const GdDirection dir, const GdElement e)
599 store_dir(cave, x, y, dir, e);
600 store(cave, x, y, O_SPACE);
603 // increment a cave element; can be used for elements which are one after
604 // the other, for example bladder1, bladder2, bladder3...
605 static inline void next(GdCave *cave, const int x, const int y)
607 (*getp(cave, x, y))++;
610 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
612 if (non_explodable (cave, x, y))
615 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
616 cave->voodoo_touched = TRUE;
618 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
619 // voodoo turns into a time penalty
620 store_sc(cave, x, y, O_TIME_PENALTY);
621 else if (get(cave, x, y) == O_NITRO_PACK ||
622 get(cave, x, y) == O_NITRO_PACK_F)
623 // nitro pack inside an explosion - it is now triggered
624 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
626 // for everything else
627 store_sc(cave, x, y, explode_to);
630 // a creature explodes to a 3x3 something.
631 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
635 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
636 cave->ckdelay += 1200;
637 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
639 for (yy = y - 1; yy <= y + 1; yy++)
640 for (xx = x - 1; xx <= x + 1; xx++)
641 cell_explode(cave, xx, yy, explode_to);
644 static void nitro_explode(GdCave *cave, int x, int y)
648 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
649 cave->ckdelay += 1200;
650 gd_sound_play(cave, GD_S_NITRO_PACK_EXPLODING, get(cave, x, y), x, y);
652 for (yy = y - 1; yy <= y + 1; yy++)
653 for (xx = x - 1; xx <= x + 1; xx++)
654 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
656 // the current cell is explicitly changed into a nitro expl,
657 // as cell_explode changes it to a triggered nitro pack
658 store_sc(cave, x, y, O_NITRO_EXPL_1);
661 // a voodoo explodes, leaving a 3x3 steel and a time penalty behind.
662 static void voodoo_explode(GdCave *cave, int x, int y)
666 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
667 cave->ckdelay += 1000;
669 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
670 if (cave->voodoo_any_hurt_kills_player)
671 cave->voodoo_touched = TRUE;
673 // voodoo explodes to 3x3 steel
674 for (yy = y - 1; yy <= y + 1; yy++)
675 for (xx = x - 1; xx <= x + 1; xx++)
676 store_sc(cave, xx, yy, O_PRE_STEEL_1);
678 // middle is a time penalty (which will be turned into a gravestone)
679 store_sc(cave, x, y, O_TIME_PENALTY);
683 a bomb does not explode the voodoo, neither does the ghost.
684 this function check this, and stores the new element or not.
685 destroying the voodoo is also controlled by the
686 voodoo_disappear_in_explosion flag.
688 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
690 if (non_explodable (cave, x, y))
693 // bomb does not explode voodoo
694 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
697 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
698 cave->voodoo_touched = TRUE;
700 store_sc (cave, x, y, expl);
703 // X shaped ghost explosion; does not touch voodoo!
704 static void ghost_explode(GdCave *cave, const int x, const int y)
706 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
708 // the processing of an explosion took pretty much time: processing 5 elements
709 cave->ckdelay += 650;
711 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
712 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
713 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
714 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
715 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
718 // +shaped bomb explosion; does not touch voodoo!
719 static void bomb_explode(GdCave *cave, const int x, const int y)
721 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
723 // the processing of an explosion took pretty much time: processing 5 elements
724 cave->ckdelay += 650;
726 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
727 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
728 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
729 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
730 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
734 explode an element with the appropriate type of exlposion.
736 static void explode(GdCave *cave, int x, int y)
738 GdElement e = get(cave, x, y) & O_MASK;
743 ghost_explode(cave, x, y);
747 bomb_explode(cave, x, y);
751 voodoo_explode(cave, x, y);
756 case O_NITRO_PACK_EXPLODE:
757 nitro_explode(cave, x, y);
761 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
764 case O_FALLING_WALL_F:
765 creature_explode(cave, x, y, O_EXPLODE_1);
772 creature_explode(cave, x, y, O_EXPLODE_1);
779 creature_explode(cave, x, y, cave->butterfly_explode_to);
786 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
793 creature_explode(cave, x, y, cave->firefly_explode_to);
796 case O_ALT_FIREFLY_1:
797 case O_ALT_FIREFLY_2:
798 case O_ALT_FIREFLY_3:
799 case O_ALT_FIREFLY_4:
800 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
806 case O_PLAYER_STIRRING:
807 case O_PLAYER_ROCKET_LAUNCHER:
808 case O_PLAYER_PNEUMATIC_LEFT:
809 case O_PLAYER_PNEUMATIC_RIGHT:
810 creature_explode(cave, x, y, O_EXPLODE_1);
817 creature_explode(cave, x, y, cave->stonefly_explode_to);
824 creature_explode(cave, x, y, cave->dragonfly_explode_to);
832 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
834 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
838 player eats specified object.
839 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
840 returns other element if something other appears there and he can't move.
841 cave pointer is needed to know the diamond values.
843 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
850 cave->diamond_key_collected = TRUE;
851 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
856 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
861 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
866 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
873 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
880 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
887 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
892 case O_CREATURE_SWITCH: // creatures change direction.
893 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
894 cave->creatures_backwards = !cave->creatures_backwards;
897 case O_EXPANDING_WALL_SWITCH: // expanding wall change direction.
898 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
899 cave->expanding_wall_changed = !cave->expanding_wall_changed;
902 case O_BITER_SWITCH: // biter change delay
903 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
904 cave->biter_delay_frame++;
905 if (cave->biter_delay_frame == 4)
906 cave->biter_delay_frame = 0;
909 case O_REPLICATOR_SWITCH: // replicator on/off switch
910 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
911 cave->replicators_active = !cave->replicators_active;
914 case O_CONVEYOR_SWITCH: // conveyor belts on/off
915 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
916 cave->conveyor_belts_active = !cave->conveyor_belts_active;
919 case O_CONVEYOR_DIR_SWITCH: // conveyor belts switch direction
920 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
921 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
927 case O_STEEL_EATABLE:
928 case O_BRICK_EATABLE:
929 case O_DIRT_SLOPED_UP_RIGHT:
930 case O_DIRT_SLOPED_UP_LEFT:
931 case O_DIRT_SLOPED_DOWN_LEFT:
932 case O_DIRT_SLOPED_DOWN_RIGHT:
935 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
939 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
940 cave->sweet_eaten = TRUE;
943 case O_PNEUMATIC_HAMMER:
944 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
945 cave->got_pneumatic_hammer = TRUE;
950 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
951 cave->time += cave->time_bonus * cave->timing_factor;
952 if (cave->time > cave->max_time * cave->timing_factor)
953 cave->time -= cave->max_time * cave->timing_factor;
954 // no space, rather a dirt remains there...
958 case O_FLYING_DIAMOND:
959 // prevent diamond sounds for O_SKELETON (see below)
960 if (x != -1 && y != -1)
961 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
962 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
964 cave->score += cave->diamond_value;
965 cave->diamonds_collected++;
967 if (cave->diamonds_needed == cave->diamonds_collected)
969 cave->gate_open = TRUE;
971 // extra is worth more points.
972 cave->diamond_value = cave->extra_diamond_value;
974 cave->gate_open_flash = 1;
975 cave->sound3 = GD_S_CRACKING;
976 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
981 cave->skeletons_collected++;
983 // as if player got a diamond
984 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
985 player_get_element(cave, O_DIAMOND, -1, -1);
987 // _after_ calling get_element for the fake diamonds, so we overwrite its sounds
988 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
993 cave->player_state = GD_PL_EXITED; // player now exits the cave!
997 case O_LAVA: // player goes into lava, as if it was space
998 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
1002 // the object will remain there.
1008 process a crazy dream-style teleporter.
1009 called from gd_cave_iterate, for a player or a player_bomb.
1010 player is standing at px, py, and trying to move in the direction player_move,
1011 where there is a teleporter at (tx_start, ty_start). we check the whole cave,
1012 from (tx_start + 1, ty_start), till we get back to (tx_start, ty_start) (by wrapping
1013 around). the first teleporter we find, and which is suitable, will be the destination.
1014 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
1016 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
1018 // start at teleporter position (not at player position!)
1019 int tx_start = px + gd_dx[player_move];
1020 int ty_start = py + gd_dy[player_move];
1026 // jump to next element; wrap around columns and rows.
1038 // if we found a teleporter...
1039 if (get(cave, tx, ty) == O_TELEPORTER &&
1040 is_space_dir(cave, tx, ty, player_move))
1042 // new player appears near teleporter found
1043 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1045 // current player disappears
1046 store(cave, px, py, O_SPACE);
1048 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1050 return TRUE; // return true as teleporter worked
1053 // loop until we get back to original coordinates
1054 while (tx != tx_start || ty != ty_start);
1056 // return false as we did not find any usable teleporter
1061 try to push an element.
1062 returns true if the push is possible; also does move the specified _element_.
1063 up to the caller to move the _player_itself_.
1065 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1068 GdElement what = get_dir(cave, x, y, player_move);
1070 // gravity for falling wall, bladder, ...
1071 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1077 case O_WAITING_STONE:
1080 case O_CHASING_STONE:
1082 case O_FLYING_STONE:
1084 // pushing some kind of stone or nut
1085 // directions possible: 90degrees cw or ccw to current gravity.
1086 // only push if player dir is orthogonal to gravity,
1087 // ie. gravity down, pushing left & right possible
1088 if (player_move == ccw_fourth[cave->gravity] ||
1089 player_move == cw_fourth[cave->gravity])
1095 // different probabilities for different elements.
1098 case O_WAITING_STONE:
1099 // waiting stones are light, can always push
1103 case O_CHASING_STONE:
1104 // chasing can be pushed if player is turbo
1105 if (cave->sweet_eaten)
1110 // mega may(!) be pushed if player is turbo
1111 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1117 case O_FLYING_STONE:
1119 if (cave->sweet_eaten)
1120 prob = cave->pushing_stone_prob_sweet; // probability with sweet
1122 prob = cave->pushing_stone_prob; // probability without sweet.
1129 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1130 gd_rand_int_range(cave->random, 0, 1000000) < prob)
1132 // if decided that he will be able to push,
1133 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1134 play_sound_of_element_pushing(cave, what, x, y);
1149 // pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1150 // not an O_BLADDER_x.
1151 // there is no "delayed" state of a bladder, so we use store_dir_no_scanned!
1153 // first check: we cannot push a bladder "up"
1154 if (player_move != opposite[grav_compat])
1156 // pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check.
1157 // player moving in the direction of gravity.
1161 if (player_move == grav_compat)
1163 // pushing bladder down
1164 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1165 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1166 // if no space to push down, maybe left (down-left to player)
1167 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1169 // left is "down, turned right (cw)"
1170 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1171 // if not, maybe right (down-right to player)
1172 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1173 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1176 // pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check.
1180 else if (player_move == cw_fourth[grav_compat])
1182 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) // pushing it left
1183 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1184 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) // maybe down, and player will move left
1185 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1186 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) // maybe up, and player will move left
1187 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1190 // pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check.
1194 else if (player_move == ccw_fourth[grav_compat])
1196 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) // pushing it right
1197 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1198 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) // maybe down, and player will move right
1199 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1200 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) // maybe up, and player will move right
1201 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1205 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1210 // a box is only pushed with the fire pressed
1213 // but always with 100% probability
1214 switch (player_move)
1220 // pushing in some dir, two steps in that dir - is there space?
1221 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1224 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1226 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1231 // push in no other directions possible
1237 // pushing of other elements not possible
1245 // from the key press booleans, create a direction
1246 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1248 GdDirection player_move;
1250 // from the key press booleans, create a direction
1252 player_move = GD_MV_UP_RIGHT;
1253 else if (down && right)
1254 player_move = GD_MV_DOWN_RIGHT;
1255 else if (down && left)
1256 player_move = GD_MV_DOWN_LEFT;
1257 else if (up && left)
1258 player_move = GD_MV_UP_LEFT;
1260 player_move = GD_MV_UP;
1262 player_move = GD_MV_DOWN;
1264 player_move = GD_MV_LEFT;
1266 player_move = GD_MV_RIGHT;
1268 player_move = GD_MV_STILL;
1273 // clear these to no sound; and they will be set during iteration.
1274 void gd_cave_clear_sounds(GdCave *cave)
1276 cave->sound1 = GD_S_NONE;
1277 cave->sound2 = GD_S_NONE;
1278 cave->sound3 = GD_S_NONE;
1281 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1282 GdElement falling_element)
1284 if (cave->gravity_disabled)
1287 if (is_space_dir(cave, x, y, falling_direction))
1289 // beginning to fall
1290 play_sound_of_element(cave, get(cave, x, y), x, y);
1291 move(cave, x, y, falling_direction, falling_element);
1294 // check if it is on a sloped element, and it can roll.
1295 // for example, sloped wall looks like:
1298 // this is tagged as sloped up&left.
1299 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1300 // then check the direction to roll (left or right)
1301 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1302 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1304 // rolling down, if sitting on a sloped object
1305 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1306 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1307 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1309 // rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1310 // so here we use cw_fourth
1311 play_sound_of_element(cave, get(cave, x, y), x, y);
1312 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1314 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1315 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1316 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1319 play_sound_of_element(cave, get(cave, x, y), x, y);
1320 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1325 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1327 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1328 cave->voodoo_dies_by_stone)
1330 // this is a 1stB-style vodo. explodes by stone, collects diamonds
1331 explode_dir(cave, x, y, fall_dir);
1338 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1340 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1341 cave->voodoo_collects_diamonds)
1343 // this is a 1stB-style voodoo. explodes by stone, collects diamonds
1344 player_get_element(cave, O_DIAMOND, x, y); // as if player got diamond
1345 store(cave, x, y, O_SPACE); // diamond disappears
1352 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1353 GdDirection fall_dir, GdElement bouncing)
1355 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1356 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1359 store(cave, x, y, bouncing);
1360 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1362 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1370 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1371 GdDirection fall_dir, GdElement magic)
1373 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1375 play_sound_of_element(cave, O_DIAMOND, x, y); // always play diamond sound
1377 if (cave->magic_wall_state == GD_MW_DORMANT)
1378 cave->magic_wall_state = GD_MW_ACTIVE;
1380 if (cave->magic_wall_state == GD_MW_ACTIVE &&
1381 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1383 // if magic wall active and place underneath, it turns element
1384 // into anything the effect says to do.
1385 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1388 // active or non-active or anything, element falling in will always disappear
1389 store(cave, x, y, O_SPACE);
1391 if (cave->magic_wall_breakscan && cave->amoeba_state == GD_AM_AWAKE)
1392 cave->convert_amoeba_this_frame = TRUE;
1400 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1402 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1404 explode_dir(cave, x, y, fall_dir);
1411 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1412 GdDirection fall_dir, GdElement bouncing)
1414 if (is_space_dir(cave, x, y, fall_dir))
1417 move(cave, x, y, fall_dir, get(cave, x, y));
1422 // check if it is on a sloped element, and it can roll.
1423 // for example, sloped wall looks like:
1426 // this is tagged as sloped up&left.
1427 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1428 // then check the direction to roll (left or right)
1429 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1431 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1433 // sloped element, falling to left or right
1434 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1435 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1436 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1438 play_sound_of_element(cave, get(cave, x, y), x, y);
1440 // try to roll left first - see O_STONE to understand why cw_fourth
1441 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1443 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1444 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1445 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1447 play_sound_of_element(cave, get(cave, x, y), x, y);
1449 // if not, try to roll right
1450 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1454 // cannot roll in any direction, so it stops
1455 play_sound_of_element(cave, get(cave, x, y), x, y);
1456 store(cave, x, y, bouncing);
1462 // any other element, stops
1463 play_sound_of_element(cave, get(cave, x, y), x, y);
1464 store(cave, x, y, bouncing);
1468 static void update_cave_speed(GdCave *cave)
1470 // update timing calculated by iterating and counting elements which were slow to process on c64
1471 switch (cave->scheduling)
1473 case GD_SCHEDULING_MILLISECONDS:
1474 // cave->speed already contains the milliseconds value, do not touch it
1477 case GD_SCHEDULING_BD1:
1478 if (!cave->intermission)
1479 // non-intermissions
1480 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1482 // intermissions were quicker, as only lines 1-12 were processed by the engine.
1483 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1486 case GD_SCHEDULING_BD1_ATARI:
1487 // about 20ms/frame faster than c64 version
1488 if (!cave->intermission)
1489 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); // non-intermissions
1491 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); // for intermissions
1494 case GD_SCHEDULING_BD2:
1496 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1499 case GD_SCHEDULING_PLCK:
1500 // 65 is totally empty cave in construction kit, with delay = 0)
1501 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1504 case GD_SCHEDULING_BD2_PLCK_ATARI:
1505 // a really fast engine; timing works like c64 plck.
1506 // 40 ms was measured in the construction kit, with delay = 0
1507 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1510 case GD_SCHEDULING_CRDR:
1511 if (cave->hammered_walls_reappear) // this made the engine very slow.
1512 cave->ckdelay += 60000;
1513 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1516 case GD_SCHEDULING_MAX:
1522 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1529 // amoeba found to be enclosed. if not, this is cleared
1530 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1532 // counting the number of amoebas. after scan, check if too much
1533 int amoeba_count, amoeba_2_count;
1535 // cave scan found water - for sound
1536 boolean found_water;
1538 boolean inbox_toggle;
1539 boolean start_signal;
1541 // gravity for falling wall, bladder, ...
1542 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1544 // directions for o_something_1, 2, 3 and 4 (creatures)
1545 static const GdDirection creature_dir[] =
1552 static const GdDirection creature_chdir[] =
1559 int time_decrement_sec;
1561 // biters eating elements preference, they try to go in this order
1562 GdElement biter_try[] =
1569 boolean amoeba_sound, magic_sound;
1571 gd_cave_clear_sounds(cave);
1573 // if diagonal movements not allowed,
1574 // horizontal movements have precedence. [BROADRIBB]
1575 if (!cave->diagonal_movements)
1577 switch (player_move)
1579 case GD_MV_UP_RIGHT:
1580 case GD_MV_DOWN_RIGHT:
1581 player_move = GD_MV_RIGHT;
1585 case GD_MV_DOWN_LEFT:
1586 player_move = GD_MV_LEFT;
1590 // no correction needed
1595 // set cave get function; to implement perfect or lineshifting borders
1596 if (cave->lineshift)
1598 cave->getp = getp_shift;
1599 cave->getx = getx_shift;
1600 cave->gety = gety_shift;
1604 cave->getp = getp_perfect;
1605 cave->getx = getx_perfect;
1606 cave->gety = gety_perfect;
1609 // increment this. if the scan routine comes across player, clears it (sets to zero).
1610 if (cave->player_seen_ago < 100)
1611 cave->player_seen_ago++;
1613 if (cave->pneumatic_hammer_active_delay > 0)
1614 cave->pneumatic_hammer_active_delay--;
1616 // inboxes and outboxes flash with the rhythm of the game, not the display.
1617 // also, a player can be born only from an open, not from a steel-wall-like inbox.
1618 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1619 inbox_toggle = cave->inbox_flash_toggle;
1621 if (cave->gate_open_flash > 0)
1622 cave->gate_open_flash--;
1624 // score collected this frame
1627 // to implement buggy bd1 amoeba+magic wall behaviour
1628 cave->convert_amoeba_this_frame = FALSE;
1630 // suicide only kills the active player
1631 // player_x, player_y was set by the previous iterate routine, or the cave setup.
1632 // we must check if there is a player or not - he may have exploded or something like that
1633 if (suicide && cave->player_state == GD_PL_LIVING &&
1634 is_player(cave, cave->player_x, cave->player_y))
1635 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1637 // check for walls reappearing
1638 if (cave->hammered_reappear)
1640 for (y = 0; y < cave->h; y++)
1642 for (x = 0; x < cave->w; x++)
1644 // timer for the cell > 0?
1645 if (cave->hammered_reappear[y][x] > 0)
1648 cave->hammered_reappear[y][x]--;
1650 // check if it became zero
1651 if (cave->hammered_reappear[y][x] == 0)
1653 store(cave, x, y, O_BRICK);
1654 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1661 // variables to check during the scan
1663 // will be set to false if any of the amoeba is found free.
1664 amoeba_found_enclosed = TRUE;
1665 amoeba_2_found_enclosed = TRUE;
1668 found_water = FALSE;
1670 time_decrement_sec = 0;
1672 // check whether to scan the first and last line
1673 if (cave->border_scan_first_and_last)
1684 // the cave scan routine
1685 for (y = ymin; y <= ymax; y++)
1687 for (x = 0; x < cave->w; x++)
1689 // if we find a scanned element, change it to the normal one, and that's all.
1690 // this is required, for example for chasing stones, which have moved, always passing slime!
1691 if (get(cave, x, y) & SCANNED)
1693 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1698 // add the ckdelay correction value for every element seen.
1699 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1701 switch (get(cave, x, y))
1703 // ============================================================================
1705 // ============================================================================
1708 if (cave->kill_player)
1710 explode (cave, x, y);
1714 cave->player_seen_ago = 0;
1715 // bd4 intermission caves have many players. so if one of them has exited,
1716 // do not change the flag anymore. so this if () is needed
1717 if (cave->player_state != GD_PL_EXITED)
1718 cave->player_state = GD_PL_LIVING;
1720 // check for pneumatic hammer things
1721 // 1) press fire, 2) have pneumatic hammer 4) space on left or right
1722 // for hammer 5) stand on something
1723 if (player_fire && cave->got_pneumatic_hammer &&
1724 is_space_dir(cave, x, y, player_move) &&
1725 !is_space_dir(cave, x, y, GD_MV_DOWN))
1727 if (player_move == GD_MV_LEFT &&
1728 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1730 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1731 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1732 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1736 if (player_move == GD_MV_RIGHT &&
1737 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1739 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1740 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1741 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1746 if (player_move != GD_MV_STILL)
1748 // only do every check if he is not moving
1749 GdElement what = get_dir(cave, x, y, player_move);
1750 GdElement remains = what;
1753 // if we are 'eating' a teleporter, and the function returns true
1754 // (teleporting worked), break here
1755 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1758 // try to push element; if successful, break
1759 push = do_push(cave, x, y, player_move, player_fire);
1769 // if its a bomb, remember he now has one.
1770 // we do not change the "remains" and "what" variables,
1771 // so that part of the code will be ineffective
1772 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1773 store_dir(cave, x, y, player_move, O_SPACE);
1776 store(cave, x, y, O_PLAYER_BOMB);
1778 move(cave, x, y, player_move, O_PLAYER_BOMB);
1781 case O_ROCKET_LAUNCHER:
1782 // if its a rocket launcher, remember he now has one.
1783 // we do not change the "remains" and "what" variables,
1784 // so that part of the code will be ineffective
1785 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1786 store_dir(cave, x, y, player_move, O_SPACE);
1789 store(cave, x, y, O_PLAYER_ROCKET_LAUNCHER);
1791 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
1795 // we do not change the "remains" and "what" variables,
1796 // so that part of the code will be ineffective
1797 if (!player_fire && !cave->gravity_switch_active &&
1798 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1800 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1801 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1802 cave->gravity_disabled = TRUE;
1806 case O_GRAVITY_SWITCH:
1807 // (we cannot use player_get for this as it does not have player_move parameter)
1808 // only allow changing direction if the new dir is not diagonal
1809 if (cave->gravity_switch_active &&
1810 (player_move == GD_MV_LEFT ||
1811 player_move == GD_MV_RIGHT ||
1812 player_move == GD_MV_UP ||
1813 player_move == GD_MV_DOWN))
1815 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1816 cave->gravity_will_change =
1817 cave->gravity_change_time * cave->timing_factor;
1818 cave->gravity_next_direction = player_move;
1819 cave->gravity_switch_active = FALSE;
1824 // get element - process others.
1825 // if cannot get, player_get_element will return the same
1826 remains = player_get_element(cave, what, x, y);
1831 if (remains != what || remains == O_SPACE)
1833 // if anything changed, apply the change.
1835 // if snapping anything and we have snapping explosions set.
1836 // but these is not true for pushing.
1837 if (remains == O_SPACE && player_fire && !push)
1838 remains = cave->snap_element;
1840 if (remains != O_SPACE || player_fire)
1841 // if any other element than space, player cannot move.
1842 // also if pressing fire, will not move.
1843 store_dir(cave, x, y, player_move, remains);
1845 // if space remains there, the player moves.
1846 move(cave, x, y, player_move, O_PLAYER);
1852 // much simpler; cannot steal stones
1853 if (cave->kill_player)
1855 explode(cave, x, y);
1859 cave->player_seen_ago = 0;
1860 // bd4 intermission caves have many players. so if one of them has exited,
1861 // do not change the flag anymore. so this if () is needed
1862 if (cave->player_state != GD_PL_EXITED)
1863 cave->player_state = GD_PL_LIVING;
1865 if (player_move != GD_MV_STILL)
1867 // if the player does not move, nothing to do
1868 GdElement what = get_dir(cave, x, y, player_move);
1869 GdElement remains = what;
1873 // placing a bomb into empty space or dirt
1874 if (is_space_dir(cave, x, y, player_move) ||
1875 is_element_dir(cave, x, y, player_move, O_DIRT))
1877 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1879 // placed bomb, he is normal player again
1880 store(cave, x, y, O_PLAYER);
1881 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1886 // pushing and collecting
1887 // if we are 'eating' a teleporter, and the function returns true
1888 // (teleporting worked), break here
1889 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1892 // player fire is false...
1893 if (do_push(cave, x, y, player_move, FALSE))
1901 case O_GRAVITY_SWITCH:
1902 // (we cannot use player_get for this as it does not have
1903 // player_move parameter)
1904 // only allow changing direction if the new dir is not diagonal
1905 if (cave->gravity_switch_active &&
1906 (player_move == GD_MV_LEFT ||
1907 player_move == GD_MV_RIGHT ||
1908 player_move == GD_MV_UP ||
1909 player_move == GD_MV_DOWN))
1911 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1912 cave->gravity_will_change =
1913 cave->gravity_change_time * cave->timing_factor;
1914 cave->gravity_next_direction = player_move;
1915 cave->gravity_switch_active = FALSE;
1920 // get element. if cannot get, player_get_element will return the same
1921 remains = player_get_element (cave, what, x, y);
1926 // if element changed, OR there is space, move.
1927 if (remains != what || remains == O_SPACE)
1929 // if anything changed, apply the change.
1930 move(cave, x, y, player_move, O_PLAYER_BOMB);
1935 case O_PLAYER_ROCKET_LAUNCHER:
1936 // much simpler; cannot snap-push stones
1937 if (cave->kill_player)
1939 explode(cave, x, y);
1943 cave->player_seen_ago = 0;
1944 // bd4 intermission caves have many players. so if one of them has exited,
1945 // do not change the flag anymore. so this if () is needed
1946 if (cave->player_state != GD_PL_EXITED)
1947 cave->player_state = GD_PL_LIVING;
1950 if (player_move != GD_MV_STILL)
1952 // if the player does not move, nothing to do
1953 GdElement what = get_dir(cave, x, y, player_move);
1954 GdElement remains = what;
1956 // to fire a rocket, diagonal movement should not be allowed.
1957 // so either x or y must be zero
1960 // placing a rocket into empty space
1961 if (is_space_dir(cave, x, y, player_move))
1963 switch (player_move)
1966 store_dir(cave, x, y, player_move, O_ROCKET_1);
1967 if (!cave->infinite_rockets)
1968 store(cave, x, y, O_PLAYER);
1972 store_dir(cave, x, y, player_move, O_ROCKET_2);
1973 if (!cave->infinite_rockets)
1974 store(cave, x, y, O_PLAYER);
1978 store_dir(cave, x, y, player_move, O_ROCKET_3);
1979 if (!cave->infinite_rockets)
1980 store(cave, x, y, O_PLAYER);
1984 store_dir(cave, x, y, player_move, O_ROCKET_4);
1985 if (!cave->infinite_rockets)
1986 store(cave, x, y, O_PLAYER);
1990 // cannot fire in other directions
1994 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1997 // a player with rocket launcher cannot snap elements, so stop here
2001 // pushing and collecting
2002 // if we are 'eating' a teleporter, and the function returns true
2003 // (teleporting worked), break here
2004 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
2007 // player fire is false...
2008 if (do_push(cave, x, y, player_move, FALSE))
2014 // get element. if cannot get, player_get_element will return the same
2015 remains = player_get_element(cave, what, x, y);
2018 // if something changed, OR there is space, move.
2019 if (remains != what || remains == O_SPACE)
2021 // if anything changed, apply the change.
2022 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
2027 case O_PLAYER_STIRRING:
2028 if (cave->kill_player)
2030 explode(cave, x, y);
2034 // stirring sound, if no other walking sound or explosion
2035 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
2037 cave->player_seen_ago = 0;
2038 // bd4 intermission caves have many players. so if one of them has exited,
2039 // do not change the flag anymore. so this if () is needed
2040 if (cave->player_state != GD_PL_EXITED)
2041 cave->player_state = GD_PL_LIVING;
2045 // player "exits" stirring the pot by pressing fire
2046 cave->gravity_disabled = FALSE;
2047 store(cave, x, y, O_PLAYER);
2048 cave->gravity_switch_active = TRUE;
2052 // player holding pneumatic hammer
2053 case O_PLAYER_PNEUMATIC_LEFT:
2054 case O_PLAYER_PNEUMATIC_RIGHT:
2055 // usual player stuff
2056 if (cave->kill_player)
2058 explode(cave, x, y);
2062 cave->player_seen_ago = 0;
2063 if (cave->player_state != GD_PL_EXITED)
2064 cave->player_state = GD_PL_LIVING;
2066 // if hammering time is up, becomes a normal player again.
2067 if (cave->pneumatic_hammer_active_delay == 0)
2068 store(cave, x, y, O_PLAYER);
2071 // the active pneumatic hammer itself
2072 case O_PNEUMATIC_ACTIVE_RIGHT:
2073 case O_PNEUMATIC_ACTIVE_LEFT:
2074 if (cave->pneumatic_hammer_active_delay == 0)
2078 // pneumatic hammer element disappears
2079 store(cave, x, y, O_SPACE);
2081 // which is the new element which appears after that one is hammered?
2082 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
2084 // if there is a new element, display it
2085 // O_NONE might be returned, for example if the element being
2086 // hammered explodes during hammering (by a nearby explosion)
2087 if (new_elem != O_NONE)
2089 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
2091 // and if walls reappear, remember it in array
2092 if (cave->hammered_walls_reappear)
2096 wall_y = (y + 1) % cave->h;
2097 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
2103 // ============================================================================
2104 // S T O N E S, D I A M O N D S
2105 // ============================================================================
2107 case O_STONE: // standing stone
2108 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
2111 case O_MEGA_STONE: // standing mega_stone
2112 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
2115 case O_DIAMOND: // standing diamond
2116 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
2119 case O_NUT: // standing nut
2120 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
2123 case O_DIRT_BALL: // standing dirt ball
2124 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
2127 case O_DIRT_LOOSE: // standing loose dirt
2128 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
2131 case O_FLYING_STONE: // standing stone
2132 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
2135 case O_FLYING_DIAMOND: // standing diamond
2136 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2139 // ============================================================================
2140 // 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
2141 // ============================================================================
2143 case O_DIRT_BALL_F: // falling dirt ball
2144 if (!cave->gravity_disabled)
2145 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2148 case O_DIRT_LOOSE_F: // falling loose dirt
2149 if (!cave->gravity_disabled)
2150 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2153 case O_STONE_F: // falling stone
2154 if (!cave->gravity_disabled)
2156 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2159 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2162 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2165 if (do_fall_try_crush(cave, x, y, cave->gravity))
2168 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2172 case O_MEGA_STONE_F: // falling mega
2173 if (!cave->gravity_disabled)
2175 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2178 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2181 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2184 if (do_fall_try_crush(cave, x, y, cave->gravity))
2187 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2191 case O_DIAMOND_F: // falling diamond
2192 if (!cave->gravity_disabled)
2194 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2197 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2200 if (do_fall_try_crush(cave, x, y, cave->gravity))
2203 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2207 case O_NUT_F: // falling nut
2208 if (!cave->gravity_disabled)
2210 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2213 if (do_fall_try_crush(cave, x, y, cave->gravity))
2216 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2220 case O_FLYING_STONE_F: // falling stone
2221 if (!cave->gravity_disabled)
2223 GdDirection fall_dir = opposite[cave->gravity];
2225 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2228 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2231 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2234 if (do_fall_try_crush(cave, x, y, fall_dir))
2237 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2241 case O_FLYING_DIAMOND_F: // falling diamond
2242 if (!cave->gravity_disabled)
2244 GdDirection fall_dir = opposite[cave->gravity];
2246 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2249 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2252 if (do_fall_try_crush(cave, x, y, fall_dir))
2255 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2259 // ============================================================================
2260 // N I T R O P A C K
2261 // ============================================================================
2263 case O_NITRO_PACK: // standing nitro pack
2264 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2267 case O_NITRO_PACK_F: // falling nitro pack
2268 if (!cave->gravity_disabled)
2270 if (is_space_dir(cave, x, y, cave->gravity)) // if space, falling further
2271 move(cave, x, y, cave->gravity, get(cave, x, y));
2272 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2274 // try magic wall; if true, function did the work
2276 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2278 // falling on a dirt, it does NOT explode - just stops at its place.
2279 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2280 store(cave, x, y, O_NITRO_PACK);
2283 // falling on any other element it explodes
2284 explode(cave, x, y);
2288 case O_NITRO_PACK_EXPLODE: // a triggered nitro pack
2289 explode(cave, x, y);
2292 // ============================================================================
2293 // C R E A T U R E S
2294 // ============================================================================
2300 // if cannot move in any direction, becomes an enclosed cow
2301 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2302 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2303 store(cave, x, y, O_COW_ENCLOSED_1);
2306 // THIS IS THE CREATURE MOVE thing copied.
2307 const GdDirection *creature_move;
2308 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2309 GdElement base; // base element number (which is like O_***_1)
2310 int dir, dirn, dirp; // direction
2314 dir = get(cave, x, y)-base; // facing where
2315 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2317 // now change direction if backwards
2318 if (cave->creatures_backwards)
2323 dirn = (dir + 3) & 3; // fast turn
2324 dirp = (dir + 1) & 3; // slow turn
2328 dirn = (dir + 1) & 3; // fast turn
2329 dirp = (dir + 3) & 3; // slow turn
2332 if (is_space_dir(cave, x, y, creature_move[dirn]))
2333 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2334 else if (is_space_dir(cave, x, y, creature_move[dir]))
2335 move(cave, x, y, creature_move[dir], base + dir); // go on
2337 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2341 // enclosed cows wait some time before turning to a skeleton
2342 case O_COW_ENCLOSED_1:
2343 case O_COW_ENCLOSED_2:
2344 case O_COW_ENCLOSED_3:
2345 case O_COW_ENCLOSED_4:
2346 case O_COW_ENCLOSED_5:
2347 case O_COW_ENCLOSED_6:
2348 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2349 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2350 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2351 is_space_dir(cave, x, y, GD_MV_DOWN))
2352 store(cave, x, y, O_COW_1);
2357 case O_COW_ENCLOSED_7:
2358 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2359 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2360 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2361 is_space_dir(cave, x, y, GD_MV_DOWN))
2362 store(cave, x, y, O_COW_1);
2364 store(cave, x, y, O_SKELETON);
2371 case O_ALT_FIREFLY_1:
2372 case O_ALT_FIREFLY_2:
2373 case O_ALT_FIREFLY_3:
2374 case O_ALT_FIREFLY_4:
2379 case O_ALT_BUTTER_1:
2380 case O_ALT_BUTTER_2:
2381 case O_ALT_BUTTER_3:
2382 case O_ALT_BUTTER_4:
2387 // check if touches a voodoo
2388 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2389 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2390 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2391 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2392 cave->voodoo_touched = TRUE;
2394 // check if touches something bad and should explode (includes voodoo by the flags)
2395 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2396 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2397 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2398 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2399 explode (cave, x, y);
2403 const GdDirection *creature_move;
2404 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2405 GdElement base = -1; // base element number (which is like O_***_1)
2406 int dir, dirn, dirp; // direction
2408 if (get(cave, x, y) >= O_FIREFLY_1 &&
2409 get(cave, x, y) <= O_FIREFLY_4)
2411 else if (get(cave, x, y) >= O_BUTTER_1 &&
2412 get(cave, x, y) <= O_BUTTER_4)
2414 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2415 get(cave, x, y) <= O_STONEFLY_4)
2416 base = O_STONEFLY_1;
2417 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2418 get(cave, x, y) <= O_ALT_FIREFLY_4)
2419 base = O_ALT_FIREFLY_1;
2420 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2421 get(cave, x, y) <= O_ALT_BUTTER_4)
2422 base = O_ALT_BUTTER_1;
2424 dir = get(cave, x, y) - base; // facing where
2425 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2427 // now change direction if backwards
2428 if (cave->creatures_backwards)
2433 dirn = (dir + 3) & 3; // fast turn
2434 dirp = (dir + 1) & 3; // slow turn
2438 dirn = (dir + 1) & 3; // fast turn
2439 dirp = (dir + 3) & 3; // slow turn
2442 if (is_space_dir(cave, x, y, creature_move[dirn]))
2443 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2444 else if (is_space_dir(cave, x, y, creature_move[dir]))
2445 move(cave, x, y, creature_move[dir], base + dir); // go on
2447 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2451 case O_WAITING_STONE:
2452 if (is_space_dir(cave, x, y, grav_compat))
2454 // beginning to fall
2456 move(cave, x, y, grav_compat, O_CHASING_STONE);
2458 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2460 // rolling down a brick wall or a stone
2461 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2462 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2463 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2465 // maybe rolling left - see case O_STONE to understand why we use cw_fourth here
2466 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2468 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2469 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2470 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2473 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2478 case O_CHASING_STONE:
2480 int px = cave->px[0];
2481 int py = cave->py[0];
2482 boolean horizontal = gd_rand_boolean(cave->random);
2483 boolean dont_move = FALSE;
2491 // ------------------------------------------------------------
2492 // check for a horizontal movement
2493 // ------------------------------------------------------------
2496 // if coordinates are the same
2498 horizontal = !horizontal;
2505 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2507 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2511 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2513 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2522 horizontal = !horizontal;
2530 // ------------------------------------------------------------
2531 // check for a vertical movement
2532 // ------------------------------------------------------------
2535 // if coordinates are the same
2537 horizontal = !horizontal;
2543 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2545 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2549 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2551 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2560 horizontal = !horizontal;
2573 // if we should move in both directions, but can not move in any, stop.
2578 // check for horizontal
2581 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2582 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2583 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2584 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2585 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2586 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2590 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2591 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2592 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2593 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2594 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2595 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2600 // check for vertical
2603 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2604 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2605 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2606 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2607 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2608 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2612 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2613 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2614 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2615 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2616 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2617 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2625 if (cave->replicators_wait_frame == 0 &&
2626 cave->replicators_active &&
2627 !cave->gravity_disabled)
2629 // only replicate, if space is under it.
2630 // do not replicate players!
2631 // also obeys gravity settings.
2632 // only replicate element if it is not a scanned one
2633 // do not replicate space... that condition looks like it
2634 // makes no sense, but otherwise it generates SCANNED spaces,
2635 // which cannot be "collected" by the player, so he cannot run
2636 // under a replicator
2637 if (is_space_dir(cave, x, y, cave->gravity) &&
2638 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2639 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2641 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2642 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2651 if (cave->biters_wait_frame == 0)
2653 static GdDirection biter_move[] =
2661 // direction, last two bits 0..3
2662 int dir = get(cave, x, y) - O_BITER_1;
2663 int dirn = (dir + 3) & 3;
2664 int dirp = (dir + 1) & 3;
2666 GdElement made_sound_of = O_NONE;
2668 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2670 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2672 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2673 if (biter_try[i] != O_SPACE)
2674 made_sound_of = O_BITER_1; // sound of a biter eating
2677 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2679 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2680 if (biter_try[i] != O_SPACE)
2681 made_sound_of = O_BITER_1; // sound of a biter eating
2684 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2686 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2687 if (biter_try[i] != O_SPACE)
2688 made_sound_of = O_BITER_1; // sound of a biter eating
2693 if (i == ARRAY_SIZE(biter_try))
2694 // i = number of elements in array: could not move, so just turn
2695 store(cave, x, y, O_BITER_1 + dirp);
2696 else if (biter_try[i] == O_STONE)
2698 // if there was a stone there, where we moved...
2699 // do not eat stones, just throw them back
2700 store(cave, x, y, O_STONE);
2701 made_sound_of = O_STONE;
2704 // if biter did move, we had sound. play it.
2705 if (made_sound_of != O_NONE)
2706 play_sound_of_element(cave, made_sound_of, x, y);
2714 // check if touches a voodoo
2715 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2716 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2717 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2718 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2719 cave->voodoo_touched = TRUE;
2721 // check if touches something bad and should explode (includes voodoo by the flags)
2722 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2723 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2724 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2725 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2726 explode (cave, x, y);
2730 const GdDirection *creature_move;
2731 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2732 GdElement base = O_DRAGONFLY_1; // base element number (which is like O_***_1)
2733 int dir, dirn; // direction
2735 dir = get(cave, x, y)-base; // facing where
2736 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2738 // now change direction if backwards
2739 if (cave->creatures_backwards)
2743 dirn = (dir + 3) & 3; // fast turn
2745 dirn = (dir + 1) & 3; // fast turn
2747 // if can move forward, does so.
2748 if (is_space_dir(cave, x, y, creature_move[dir]))
2749 move(cave, x, y, creature_move[dir], base + dir);
2751 // otherwise turns 90 degrees in place.
2752 store(cave, x, y, base + dirn);
2757 store(cave, x, y, O_BLADDER_1);
2768 // bladder with any delay state: try to convert to clock.
2769 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2770 is_element_dir(cave, x, y, cw_fourth[grav_compat], cave->bladder_converts_by) || is_element_dir(cave, x, y, ccw_fourth[grav_compat], cave->bladder_converts_by))
2772 // if touches the specified element, let it be a clock
2773 store(cave, x, y, O_PRE_CLOCK_1);
2775 // plays the bladder convert sound
2776 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2780 // is space over the bladder?
2781 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2783 if (get(cave, x, y) == O_BLADDER_8)
2785 // if it is a bladder 8, really move up
2786 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2787 play_sound_of_element(cave, O_BLADDER, x, y);
2790 // if smaller delay, just increase delay.
2794 // if not space, is something sloped over the bladder?
2795 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2796 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2798 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2799 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2800 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2802 // rolling up, to left
2803 if (get(cave, x, y) == O_BLADDER_8)
2805 // if it is a bladder 8, really roll
2806 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2807 play_sound_of_element(cave, O_BLADDER, x, y);
2810 // if smaller delay, just increase delay.
2813 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2814 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2815 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2817 // rolling up, to left
2818 if (get(cave, x, y) == O_BLADDER_8)
2820 // if it is a bladder 8, really roll
2821 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2822 play_sound_of_element(cave, O_BLADDER, x, y);
2825 // if smaller delay, just increase delay.
2830 // no space, no sloped thing over it - store bladder 1 and that is for now.
2832 store(cave, x, y, O_BLADDER_1);
2837 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2838 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2839 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2840 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2841 explode (cave, x, y);
2846 // the ghost is given four possibilities to move.
2847 for (i = 0; i < 4; i++)
2849 static GdDirection dirs[] =
2856 GdDirection random_dir;
2858 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2859 if (is_space_dir(cave, x, y, random_dir))
2861 move(cave, x, y, random_dir, O_GHOST);
2862 break; // ghost did move -> exit loop
2868 // ============================================================================
2869 // A C T I V E E L E M E N T S
2870 // ============================================================================
2873 // emulating BD1 amoeba+magic wall bug
2874 if (cave->convert_amoeba_this_frame && amoeba_found_enclosed)
2876 store(cave, x, y, cave->amoeba_enclosed_effect);
2881 switch (cave->amoeba_state)
2884 store(cave, x, y, cave->amoeba_too_big_effect);
2887 case GD_AM_ENCLOSED:
2888 store(cave, x, y, cave->amoeba_enclosed_effect);
2891 case GD_AM_SLEEPING:
2893 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2894 if (amoeba_found_enclosed)
2895 // if still found enclosed, check all four directions, if this one is able to grow.
2896 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2897 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2898 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2899 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2901 // not enclosed. this is a local (per scan) flag!
2902 amoeba_found_enclosed = FALSE;
2903 cave->amoeba_state = GD_AM_AWAKE;
2906 // if alive, check in which dir to grow (or not)
2907 if (cave->amoeba_state == GD_AM_AWAKE)
2909 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2911 switch (gd_rand_int_range(cave->random, 0, 4))
2913 // decided to grow, choose a random direction.
2914 case 0: // let this be up. numbers indifferent.
2915 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2916 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2920 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2921 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2925 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2926 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2930 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2931 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2943 // check if it is touching an amoeba, and explosion is enabled
2944 if (cave->amoeba_2_explodes_by_amoeba &&
2945 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2946 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2947 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2948 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2949 explode (cave, x, y);
2951 switch (cave->amoeba_2_state)
2954 store(cave, x, y, cave->amoeba_2_too_big_effect);
2957 case GD_AM_ENCLOSED:
2958 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2961 case GD_AM_SLEEPING:
2963 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2964 if (amoeba_2_found_enclosed)
2965 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2966 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2967 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2968 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2970 // not enclosed. this is a local (per scan) flag!
2971 amoeba_2_found_enclosed = FALSE;
2972 cave->amoeba_2_state = GD_AM_AWAKE;
2975 // if it is alive, decide if it attempts to grow
2976 if (cave->amoeba_2_state == GD_AM_AWAKE)
2977 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2979 switch (gd_rand_int_range(cave->random, 0, 4))
2981 // decided to grow, choose a random direction.
2982 case 0: // let this be up. numbers indifferent.
2983 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2984 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2988 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2989 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2993 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2994 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2998 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2999 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
3009 // choose randomly, if it spreads
3010 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
3012 // the current one explodes
3013 store(cave, x, y, cave->acid_turns_to);
3015 // and if neighbours are eaten, put acid there.
3016 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
3018 play_sound_of_element(cave, O_ACID, x, y);
3019 store_dir(cave, x, y, GD_MV_UP, O_ACID);
3022 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
3024 play_sound_of_element(cave, O_ACID, x, y);
3025 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
3028 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
3030 play_sound_of_element(cave, O_ACID, x, y);
3031 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
3034 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
3036 play_sound_of_element(cave, O_ACID, x, y);
3037 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
3044 if (!cave->water_does_not_flow_down &&
3045 is_space_dir(cave, x, y, GD_MV_DOWN))
3046 // emulating the odd behaviour in crdr
3047 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
3049 if (is_space_dir(cave, x, y, GD_MV_UP))
3050 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
3052 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3053 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
3055 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3056 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
3060 store(cave, x, y, O_WATER);
3063 case O_H_EXPANDING_WALL:
3064 case O_V_EXPANDING_WALL:
3065 case O_H_EXPANDING_STEEL_WALL:
3066 case O_V_EXPANDING_STEEL_WALL:
3067 // checks first if direction is changed.
3068 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
3069 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
3070 !cave->expanding_wall_changed) ||
3071 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
3072 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
3073 cave->expanding_wall_changed))
3075 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3077 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3078 play_sound_of_element(cave, get(cave, x, y), x, y);
3081 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3082 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3083 play_sound_of_element(cave, get(cave, x, y), x, y);
3088 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3089 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3090 play_sound_of_element(cave, get(cave, x, y), x, y);
3093 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3094 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3095 play_sound_of_element(cave, get(cave, x, y), x, y);
3100 case O_EXPANDING_WALL:
3101 case O_EXPANDING_STEEL_WALL:
3102 // the wall which grows in all four directions.
3103 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3105 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3106 play_sound_of_element(cave, get(cave, x, y), x, y);
3109 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3110 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3111 play_sound_of_element(cave, get(cave, x, y), x, y);
3114 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3115 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3116 play_sound_of_element(cave, get(cave, x, y), x, y);
3119 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3120 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3121 play_sound_of_element(cave, get(cave, x, y), x, y);
3127 ; // to make compilers happy ...
3129 Info("Step[%03d]", cave->frame); // XXX
3131 int rrr = gd_cave_c64_random(cave);
3134 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
3135 (rrr & cave->slime_permeability_c64) == 0);
3138 * unpredictable: gd_rand_int
3139 * predictable: c64 predictable random generator.
3140 * for predictable, a random number is generated,
3141 * whether or not it is even possible that the stone will be able to pass.
3143 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
3145 GdDirection grav = cave->gravity;
3146 GdDirection oppos = opposite[cave->gravity];
3148 // space under the slime? elements may pass from top to bottom then.
3149 if (is_space_dir(cave, x, y, grav))
3151 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3153 // output a falling xy under
3154 store_dir(cave, x, y, grav, cave->slime_converts_1);
3156 store_dir(cave, x, y, oppos, O_SPACE);
3157 play_sound_of_element(cave, O_SLIME, x, y);
3159 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3161 store_dir(cave, x, y, grav, cave->slime_converts_2);
3162 store_dir(cave, x, y, oppos, O_SPACE);
3163 play_sound_of_element(cave, O_SLIME, x, y);
3165 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3167 store_dir(cave, x, y, grav, cave->slime_converts_3);
3168 store_dir(cave, x, y, oppos, O_SPACE);
3169 play_sound_of_element(cave, O_SLIME, x, y);
3171 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3173 // waiting stones pass without awakening
3174 store_dir(cave, x, y, grav, O_WAITING_STONE);
3175 store_dir(cave, x, y, oppos, O_SPACE);
3176 play_sound_of_element(cave, O_SLIME, x, y);
3178 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3180 // chasing stones pass
3181 store_dir(cave, x, y, grav, O_CHASING_STONE);
3182 store_dir(cave, x, y, oppos, O_SPACE);
3183 play_sound_of_element(cave, O_SLIME, x, y);
3188 // or space over the slime? elements may pass from bottom to up then.
3189 if (is_space_dir(cave, x, y, oppos))
3191 if (get_dir(cave, x, y, grav) == O_BLADDER)
3193 // bladders move UP the slime
3194 store_dir(cave, x, y, grav, O_SPACE);
3195 store_dir(cave, x, y, oppos, O_BLADDER_1);
3196 play_sound_of_element(cave, O_SLIME, x, y);
3198 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3200 store_dir(cave, x, y, grav, O_SPACE);
3201 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3202 play_sound_of_element(cave, O_SLIME, x, y);
3204 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3206 store_dir(cave, x, y, grav, O_SPACE);
3207 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3208 play_sound_of_element(cave, O_SLIME, x, y);
3215 case O_FALLING_WALL:
3216 if (is_space_dir(cave, x, y, grav_compat))
3218 // try falling if space under.
3221 for (yy = y + 1; yy < y + cave->h; yy++)
3222 // yy < y + cave->h is to check everything OVER the wall - since caves wrap around !!
3223 if (get(cave, x, yy) != O_SPACE)
3224 // stop cycle when other than space
3227 // if scanning stopped by a player... start falling!
3228 if (get(cave, x, yy) == O_PLAYER ||
3229 get(cave, x, yy) == O_PLAYER_GLUED ||
3230 get(cave, x, yy) == O_PLAYER_BOMB)
3232 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3233 // no sound when the falling wall starts falling!
3238 case O_FALLING_WALL_F:
3239 switch (get_dir(cave, x, y, grav_compat))
3242 case O_PLAYER_GLUED:
3244 // if player under, it explodes - the falling wall, not the player!
3245 explode(cave, x, y);
3250 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3255 play_sound_of_element(cave, get(cave, x, y), x, y);
3256 store(cave, x, y, O_FALLING_WALL);
3261 // ============================================================================
3262 // C O N V E Y O R B E L T S
3263 // ============================================================================
3265 case O_CONVEYOR_RIGHT:
3266 case O_CONVEYOR_LEFT:
3267 // only works if gravity is up or down!!!
3268 // first, check for gravity and running belts.
3269 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3271 const GdDirection *dir;
3275 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3276 if (cave->conveyor_belts_direction_changed)
3278 dir = left ? ccw_eighth : cw_eighth;
3280 // CHECK IF IT CONVEYS THE ELEMENT ABOVE IT
3281 // if gravity is normal, and the conveyor belt has something
3282 // ABOVE which can be moved
3284 // the gravity is up, so anything that should float now goes
3285 // DOWN and touches the conveyor
3286 if ((cave->gravity == GD_MV_DOWN &&
3287 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3288 (cave->gravity == GD_MV_UP &&
3289 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3291 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3292 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3294 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move
3295 store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space.
3299 // CHECK IF IT CONVEYS THE ELEMENT BELOW IT
3300 if ((cave->gravity == GD_MV_UP &&
3301 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3302 (cave->gravity == GD_MV_DOWN &&
3303 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3305 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3306 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3308 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move
3309 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear.
3315 // ============================================================================
3317 // ============================================================================
3320 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3321 move(cave, x, y, GD_MV_RIGHT, O_ROCKET_1);
3323 explode(cave, x, y);
3327 if (is_space_dir(cave, x, y, GD_MV_UP))
3328 move(cave, x, y, GD_MV_UP, O_ROCKET_2);
3330 explode(cave, x, y);
3334 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3335 move(cave, x, y, GD_MV_LEFT, O_ROCKET_3);
3337 explode(cave, x, y);
3341 if (is_space_dir(cave, x, y, GD_MV_DOWN))
3342 move(cave, x, y, GD_MV_DOWN, O_ROCKET_4);
3344 explode(cave, x, y);
3347 // ============================================================================
3348 // S I M P L E C H A N G I N G; E X P L O S I O N S
3349 // ============================================================================
3352 store(cave, x, y, cave->explosion_effect);
3356 store(cave, x, y, O_DIAMOND);
3360 store(cave, x, y, cave->diamond_birth_effect);
3364 store(cave, x, y, O_STONE);
3367 case O_NITRO_EXPL_4:
3368 store(cave, x, y, cave->nitro_explosion_effect);
3372 store(cave, x, y, cave->bomb_explosion_effect);
3375 case O_AMOEBA_2_EXPL_4:
3376 store(cave, x, y, cave->amoeba_2_explosion_effect);
3379 case O_GHOST_EXPL_4:
3381 static GdElement ghost_explode[] =
3383 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3384 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3385 O_WAITING_STONE, O_BITER_1
3388 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3393 store(cave, x, y, O_STEEL);
3397 store(cave, x, y, O_CLOCK);
3401 explode(cave, x, y);
3404 case O_TRAPPED_DIAMOND:
3405 if (cave->diamond_key_collected)
3406 store(cave, x, y, O_DIAMOND);
3410 if (cave->gate_open) // if no more diamonds needed
3411 store(cave, x, y, O_OUTBOX); // open outbox
3414 case O_PRE_INVIS_OUTBOX:
3415 if (cave->gate_open) // if no more diamonds needed
3416 store(cave, x, y, O_INVIS_OUTBOX); // open outbox. invisible one :P
3420 if (cave->hatched && !inbox_toggle) // if it is time of birth
3421 store(cave, x, y, O_PRE_PL_1);
3422 inbox_toggle = !inbox_toggle;
3426 store(cave, x, y, O_PLAYER);
3451 case O_GHOST_EXPL_1:
3452 case O_GHOST_EXPL_2:
3453 case O_GHOST_EXPL_3:
3463 case O_NITRO_EXPL_1:
3464 case O_NITRO_EXPL_2:
3465 case O_NITRO_EXPL_3:
3466 case O_AMOEBA_2_EXPL_1:
3467 case O_AMOEBA_2_EXPL_2:
3468 case O_AMOEBA_2_EXPL_3:
3469 // simply the next identifier
3488 found_water = TRUE; // for sound
3489 // simply the next identifier
3493 case O_BLADDER_SPENDER:
3494 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3496 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3497 store(cave, x, y, O_PRE_STEEL_1);
3498 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3503 // other inanimate elements that do nothing
3510 // ============================================================================
3512 // ============================================================================
3514 // another scan-like routine:
3515 // short explosions (for example, in bd1) started with explode_2.
3516 // internally we use explode_1; and change it to explode_2 if needed.
3517 if (cave->short_explosions)
3519 for (y = 0; y < cave->h; y++)
3521 for (x = 0; x < cave->w; x++)
3523 if (is_first_stage_of_explosion(cave, x, y))
3525 next(cave, x, y); // select next frame of explosion
3527 // forget scanned flag immediately
3528 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3534 // finally: forget "scanned" flags for objects.
3535 // also, check for time penalties.
3536 // these is something like an effect table, but we do not really use one.
3537 for (y = 0; y < cave->h; y++)
3539 for (x = 0; x < cave->w; x++)
3541 if (get(cave, x, y) & SCANNED)
3542 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3544 if (get(cave, x, y) == O_TIME_PENALTY)
3546 store(cave, x, y, O_GRAVESTONE);
3548 // there is time penalty for destroying the voodoo
3549 time_decrement_sec += cave->time_penalty;
3554 // this loop finds the coordinates of the player. needed for scrolling and chasing stone.
3555 // but we only do this, if a living player was found. if not yet, the setup
3556 // routine coordinates are used
3557 if (cave->player_state == GD_PL_LIVING)
3559 if (cave->active_is_first_found)
3561 // to be 1stb compatible, we do everything backwards.
3562 for (y = cave->h - 1; y >= 0; y--)
3564 for (x = cave->w - 1; x >= 0; x--)
3566 if (is_player(cave, x, y))
3568 // here we remember the coordinates.
3577 // as in the original: look for the last one
3578 for (y = 0; y < cave->h; y++)
3580 for (x = 0; x < cave->w; x++)
3582 if (is_player(cave, x, y))
3584 // here we remember the coordinates.
3593 // record coordinates of player for chasing stone
3594 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3596 cave->px[i] = cave->px[i + 1];
3597 cave->py[i] = cave->py[i + 1];
3600 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3601 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3605 // update timing calculated by iterating and counting elements
3606 update_cave_speed(cave);
3608 // cave 3 sounds. precedence is controlled by the sound_play function.
3609 // but we have to check amoeba&magic together as they had a different gritty sound when mixed
3611 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3613 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3614 cave->magic_wall_sound);
3616 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3617 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3618 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3620 if (amoeba_sound && magic_sound)
3622 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3627 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3628 else if (magic_sound)
3629 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3634 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3635 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3636 play_sound_of_element(cave, O_AMOEBA, x, y);
3639 // pneumatic hammer sound - overrides everything.
3640 if (cave->pneumatic_hammer_active_delay > 0)
3641 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3644 // ============================================================================
3646 // ============================================================================
3650 // check if player is alive.
3651 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
3652 cave->player_state = GD_PL_DIED;
3654 // check if any voodoo exploded, and kill players the next scan if that happended.
3655 if (cave->voodoo_touched)
3656 cave->kill_player = TRUE;
3660 // check flags after evaluating.
3661 if (cave->amoeba_state == GD_AM_AWAKE)
3663 if (amoeba_count >= cave->amoeba_max_count)
3664 cave->amoeba_state = GD_AM_TOO_BIG;
3665 if (amoeba_found_enclosed)
3666 cave->amoeba_state = GD_AM_ENCLOSED;
3669 // amoeba can also be turned into diamond by magic wall
3670 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3671 cave->amoeba_state = GD_AM_ENCLOSED;
3674 if (cave->amoeba_2_state == GD_AM_AWAKE)
3676 // check flags after evaluating.
3677 if (amoeba_2_count >= cave->amoeba_2_max_count)
3678 cave->amoeba_2_state = GD_AM_TOO_BIG;
3680 if (amoeba_2_found_enclosed)
3681 cave->amoeba_2_state = GD_AM_ENCLOSED;
3684 // amoeba 2 can also be turned into diamond by magic wall
3685 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3686 cave->amoeba_2_state = GD_AM_ENCLOSED;
3688 // now check times. ---------------------------
3689 // decrement time if a voodoo was killed.
3690 cave->time -= time_decrement_sec * cave->timing_factor;
3694 // only decrement time when player is already born.
3697 int secondsbefore, secondsafter;
3699 secondsbefore = cave->time / cave->timing_factor;
3700 cave->time -= cave->speed;
3701 if (cave->time <= 0)
3704 secondsafter = cave->time / cave->timing_factor;
3705 if (cave->time / cave->timing_factor < 10)
3706 // if less than 10 seconds, no walking sound, but play explosion sound
3707 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3709 if (secondsbefore != secondsafter)
3710 gd_cave_set_seconds_sound(cave);
3713 // a gravity switch was activated; seconds counting down
3714 if (cave->gravity_will_change > 0)
3716 cave->gravity_will_change -= cave->speed;
3717 if (cave->gravity_will_change < 0)
3718 cave->gravity_will_change = 0;
3720 if (cave->gravity_will_change == 0)
3722 cave->gravity = cave->gravity_next_direction;
3723 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound
3727 // creatures direction automatically change
3728 if (cave->creatures_direction_will_change > 0)
3730 cave->creatures_direction_will_change -= cave->speed;
3731 if (cave->creatures_direction_will_change < 0)
3732 cave->creatures_direction_will_change = 0;
3734 if (cave->creatures_direction_will_change == 0)
3736 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3738 cave->creatures_backwards = !cave->creatures_backwards;
3739 cave->creatures_direction_will_change =
3740 cave->creatures_direction_auto_change_time * cave->timing_factor;
3744 // magic wall; if active&wait or not wait for hatching
3745 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3746 (cave->hatched || !cave->magic_timer_wait_for_hatching) &&
3747 !(cave->magic_wall_time == 0 && cave->magic_timer_zero_is_infinite))
3749 cave->magic_wall_time -= cave->speed;
3750 if (cave->magic_wall_time < 0)
3751 cave->magic_wall_time = 0;
3752 if (cave->magic_wall_time == 0)
3753 cave->magic_wall_state = GD_MW_EXPIRED;
3756 // we may wait for hatching, when starting amoeba
3757 if (cave->amoeba_timer_started_immediately ||
3758 (cave->amoeba_state == GD_AM_AWAKE &&
3759 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3761 cave->amoeba_time -= cave->speed;
3762 if (cave->amoeba_time < 0)
3763 cave->amoeba_time = 0;
3764 if (cave->amoeba_time == 0)
3765 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3768 // we may wait for hatching, when starting amoeba
3769 if (cave->amoeba_timer_started_immediately ||
3770 (cave->amoeba_2_state == GD_AM_AWAKE &&
3771 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3773 cave->amoeba_2_time -= cave->speed;
3774 if (cave->amoeba_2_time < 0)
3775 cave->amoeba_2_time = 0;
3776 if (cave->amoeba_2_time == 0)
3777 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3780 // check for player hatching.
3781 start_signal = FALSE;
3783 // if not the c64 scheduling, but the correct frametime is used,
3784 // hatching delay should always be decremented.
3785 // otherwise, the if (millisecs...) condition below will set this.
3786 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3788 // NON-C64 scheduling
3789 if (cave->hatching_delay_frame > 0)
3791 // for milliseconds-based, non-c64 schedulings, hatching delay means frames.
3792 cave->hatching_delay_frame--;
3793 if (cave->hatching_delay_frame == 0)
3794 start_signal = TRUE;
3800 if (cave->hatching_delay_time > 0)
3802 // for c64 schedulings, hatching delay means milliseconds.
3803 cave->hatching_delay_time -= cave->speed;
3804 if (cave->hatching_delay_time <= 0)
3806 cave->hatching_delay_time = 0;
3807 start_signal = TRUE;
3812 // if decremented hatching, and it became zero:
3815 // THIS IS THE CAVE START SIGNAL
3817 // record that now the cave is in its normal state
3818 cave->hatched = TRUE;
3820 // if diamonds needed is below zero, we count the available diamonds now.
3821 gd_cave_count_diamonds(cave);
3823 // setup direction auto change
3824 if (cave->creatures_direction_auto_change_time)
3826 cave->creatures_direction_will_change =
3827 cave->creatures_direction_auto_change_time * cave->timing_factor;
3829 if (cave->creatures_direction_auto_change_on_start)
3830 cave->creatures_backwards = !cave->creatures_backwards;
3833 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3837 if (cave->biters_wait_frame == 0)
3838 cave->biters_wait_frame = cave->biter_delay_frame;
3840 cave->biters_wait_frame--;
3842 // replicators delay
3843 if (cave->replicators_wait_frame == 0)
3844 cave->replicators_wait_frame = cave->replicator_delay_frame;
3846 cave->replicators_wait_frame--;
3849 // ============================================================================
3851 // ============================================================================
3854 // check if cave failed by timeout is done in main game engine
3856 // check if cave failed by timeout
3857 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3859 gd_cave_clear_sounds(cave);
3860 cave->player_state = GD_PL_TIMEOUT;
3861 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3865 // set these for drawing.
3866 cave->last_direction = player_move;
3868 // here we remember last movements for animation. this is needed here,
3869 // as animation is in sync with the game, not the keyboard directly.
3870 // (for example, after exiting the cave, the player was "running" in the
3871 // original, till bonus points were counted for remaining time and so on.
3872 if (player_move == GD_MV_LEFT ||
3873 player_move == GD_MV_UP_LEFT ||
3874 player_move == GD_MV_DOWN_LEFT)
3875 cave->last_horizontal_direction = GD_MV_LEFT;
3877 if (player_move == GD_MV_RIGHT ||
3878 player_move == GD_MV_UP_RIGHT ||
3879 player_move == GD_MV_DOWN_RIGHT)
3880 cave->last_horizontal_direction = GD_MV_RIGHT;
3882 cave->frame++; // XXX
3885 void set_initial_cave_speed(GdCave *cave)
3890 // set cave get function; to implement perfect or lineshifting borders
3891 if (cave->lineshift)
3892 cave->getp = getp_shift;
3894 cave->getp = getp_perfect;
3896 // check whether to scan the first and last line
3897 if (cave->border_scan_first_and_last)
3908 for (y = ymin; y <= ymax; y++)
3910 for (x = 0; x < cave->w; x++)
3912 // add the ckdelay correction value for every element seen.
3913 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3917 // update timing calculated by iterating and counting elements
3918 update_cave_speed(cave);