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) && !use_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 game_bd.player_moving = FALSE;
1574 game_bd.player_snapping = FALSE;
1576 // if diagonal movements not allowed,
1577 // horizontal movements have precedence. [BROADRIBB]
1578 if (!cave->diagonal_movements)
1580 switch (player_move)
1582 case GD_MV_UP_RIGHT:
1583 case GD_MV_DOWN_RIGHT:
1584 player_move = GD_MV_RIGHT;
1588 case GD_MV_DOWN_LEFT:
1589 player_move = GD_MV_LEFT;
1593 // no correction needed
1598 // set cave get function; to implement perfect or lineshifting borders
1599 if (cave->lineshift)
1601 cave->getp = getp_shift;
1602 cave->getx = getx_shift;
1603 cave->gety = gety_shift;
1607 cave->getp = getp_perfect;
1608 cave->getx = getx_perfect;
1609 cave->gety = gety_perfect;
1612 // increment this. if the scan routine comes across player, clears it (sets to zero).
1613 if (cave->player_seen_ago < 100)
1614 cave->player_seen_ago++;
1616 if (cave->pneumatic_hammer_active_delay > 0)
1617 cave->pneumatic_hammer_active_delay--;
1619 // inboxes and outboxes flash with the rhythm of the game, not the display.
1620 // also, a player can be born only from an open, not from a steel-wall-like inbox.
1621 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1622 inbox_toggle = cave->inbox_flash_toggle;
1624 if (cave->gate_open_flash > 0)
1625 cave->gate_open_flash--;
1627 // score collected this frame
1630 // to implement buggy bd1 amoeba+magic wall behaviour
1631 cave->convert_amoeba_this_frame = FALSE;
1633 // suicide only kills the active player
1634 // player_x, player_y was set by the previous iterate routine, or the cave setup.
1635 // we must check if there is a player or not - he may have exploded or something like that
1636 if (suicide && cave->player_state == GD_PL_LIVING &&
1637 is_player(cave, cave->player_x, cave->player_y))
1638 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1640 // check for walls reappearing
1641 if (cave->hammered_reappear)
1643 for (y = 0; y < cave->h; y++)
1645 for (x = 0; x < cave->w; x++)
1647 // timer for the cell > 0?
1648 if (cave->hammered_reappear[y][x] > 0)
1651 cave->hammered_reappear[y][x]--;
1653 // check if it became zero
1654 if (cave->hammered_reappear[y][x] == 0)
1656 store(cave, x, y, O_BRICK);
1657 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1664 // variables to check during the scan
1666 // will be set to false if any of the amoeba is found free.
1667 amoeba_found_enclosed = TRUE;
1668 amoeba_2_found_enclosed = TRUE;
1671 found_water = FALSE;
1673 time_decrement_sec = 0;
1675 // check whether to scan the first and last line
1676 if (cave->border_scan_first_and_last)
1687 // the cave scan routine
1688 for (y = ymin; y <= ymax; y++)
1690 for (x = 0; x < cave->w; x++)
1692 // if we find a scanned element, change it to the normal one, and that's all.
1693 // this is required, for example for chasing stones, which have moved, always passing slime!
1694 if (get(cave, x, y) & SCANNED)
1696 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1701 // add the ckdelay correction value for every element seen.
1702 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1704 switch (get(cave, x, y))
1706 // ============================================================================
1708 // ============================================================================
1711 if (cave->kill_player)
1713 explode (cave, x, y);
1717 cave->player_seen_ago = 0;
1718 // bd4 intermission caves have many players. so if one of them has exited,
1719 // do not change the flag anymore. so this if () is needed
1720 if (cave->player_state != GD_PL_EXITED)
1721 cave->player_state = GD_PL_LIVING;
1723 // check for pneumatic hammer things
1724 // 1) press fire, 2) have pneumatic hammer 4) space on left or right
1725 // for hammer 5) stand on something
1726 if (player_fire && cave->got_pneumatic_hammer &&
1727 is_space_dir(cave, x, y, player_move) &&
1728 !is_space_dir(cave, x, y, GD_MV_DOWN))
1730 if (player_move == GD_MV_LEFT &&
1731 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1733 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1734 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1735 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1739 if (player_move == GD_MV_RIGHT &&
1740 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1742 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1743 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1744 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1749 if (player_move != GD_MV_STILL)
1751 // only do every check if he is not moving
1752 GdElement what = get_dir(cave, x, y, player_move);
1753 GdElement remains = what;
1756 // if we are 'eating' a teleporter, and the function returns true
1757 // (teleporting worked), break here
1758 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1761 // try to push element; if successful, break
1762 push = do_push(cave, x, y, player_move, player_fire);
1772 // if its a bomb, remember he now has one.
1773 // we do not change the "remains" and "what" variables,
1774 // so that part of the code will be ineffective
1775 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1776 store_dir(cave, x, y, player_move, O_SPACE);
1779 store(cave, x, y, O_PLAYER_BOMB);
1781 move(cave, x, y, player_move, O_PLAYER_BOMB);
1784 case O_ROCKET_LAUNCHER:
1785 // if its a rocket launcher, remember he now has one.
1786 // we do not change the "remains" and "what" variables,
1787 // so that part of the code will be ineffective
1788 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1789 store_dir(cave, x, y, player_move, O_SPACE);
1792 store(cave, x, y, O_PLAYER_ROCKET_LAUNCHER);
1794 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
1798 // we do not change the "remains" and "what" variables,
1799 // so that part of the code will be ineffective
1800 if (!player_fire && !cave->gravity_switch_active &&
1801 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1803 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1804 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1805 cave->gravity_disabled = TRUE;
1809 case O_GRAVITY_SWITCH:
1810 // (we cannot use player_get for this as it does not have player_move parameter)
1811 // only allow changing direction if the new dir is not diagonal
1812 if (cave->gravity_switch_active &&
1813 (player_move == GD_MV_LEFT ||
1814 player_move == GD_MV_RIGHT ||
1815 player_move == GD_MV_UP ||
1816 player_move == GD_MV_DOWN))
1818 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1819 cave->gravity_will_change =
1820 cave->gravity_change_time * cave->timing_factor;
1821 cave->gravity_next_direction = player_move;
1822 cave->gravity_switch_active = FALSE;
1827 // get element - process others.
1828 // if cannot get, player_get_element will return the same
1829 remains = player_get_element(cave, what, x, y);
1834 if (remains != what || remains == O_SPACE)
1836 // if anything changed, apply the change.
1838 // if snapping anything and we have snapping explosions set.
1839 // but these is not true for pushing.
1840 if (remains == O_SPACE && player_fire && !push)
1842 remains = cave->snap_element;
1844 game_bd.player_snapping = TRUE;
1847 if (remains != O_SPACE || player_fire)
1849 // if any other element than space, player cannot move.
1850 // also if pressing fire, will not move.
1851 store_dir(cave, x, y, player_move, remains);
1855 // if space remains there, the player moves.
1856 move(cave, x, y, player_move, O_PLAYER);
1858 game_bd.player_moving = TRUE;
1865 // much simpler; cannot steal stones
1866 if (cave->kill_player)
1868 explode(cave, x, y);
1872 cave->player_seen_ago = 0;
1873 // bd4 intermission caves have many players. so if one of them has exited,
1874 // do not change the flag anymore. so this if () is needed
1875 if (cave->player_state != GD_PL_EXITED)
1876 cave->player_state = GD_PL_LIVING;
1878 if (player_move != GD_MV_STILL)
1880 // if the player does not move, nothing to do
1881 GdElement what = get_dir(cave, x, y, player_move);
1882 GdElement remains = what;
1886 // placing a bomb into empty space or dirt
1887 if (is_space_dir(cave, x, y, player_move) ||
1888 is_element_dir(cave, x, y, player_move, O_DIRT))
1890 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1892 // placed bomb, he is normal player again
1893 store(cave, x, y, O_PLAYER);
1894 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1899 // pushing and collecting
1900 // if we are 'eating' a teleporter, and the function returns true
1901 // (teleporting worked), break here
1902 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1905 // player fire is false...
1906 if (do_push(cave, x, y, player_move, FALSE))
1914 case O_GRAVITY_SWITCH:
1915 // (we cannot use player_get for this as it does not have
1916 // player_move parameter)
1917 // only allow changing direction if the new dir is not diagonal
1918 if (cave->gravity_switch_active &&
1919 (player_move == GD_MV_LEFT ||
1920 player_move == GD_MV_RIGHT ||
1921 player_move == GD_MV_UP ||
1922 player_move == GD_MV_DOWN))
1924 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1925 cave->gravity_will_change =
1926 cave->gravity_change_time * cave->timing_factor;
1927 cave->gravity_next_direction = player_move;
1928 cave->gravity_switch_active = FALSE;
1933 // get element. if cannot get, player_get_element will return the same
1934 remains = player_get_element (cave, what, x, y);
1939 // if element changed, OR there is space, move.
1940 if (remains != what || remains == O_SPACE)
1942 // if anything changed, apply the change.
1943 move(cave, x, y, player_move, O_PLAYER_BOMB);
1948 case O_PLAYER_ROCKET_LAUNCHER:
1949 // much simpler; cannot snap-push stones
1950 if (cave->kill_player)
1952 explode(cave, x, y);
1956 cave->player_seen_ago = 0;
1957 // bd4 intermission caves have many players. so if one of them has exited,
1958 // do not change the flag anymore. so this if () is needed
1959 if (cave->player_state != GD_PL_EXITED)
1960 cave->player_state = GD_PL_LIVING;
1963 if (player_move != GD_MV_STILL)
1965 // if the player does not move, nothing to do
1966 GdElement what = get_dir(cave, x, y, player_move);
1967 GdElement remains = what;
1969 // to fire a rocket, diagonal movement should not be allowed.
1970 // so either x or y must be zero
1973 // placing a rocket into empty space
1974 if (is_space_dir(cave, x, y, player_move))
1976 switch (player_move)
1979 store_dir(cave, x, y, player_move, O_ROCKET_1);
1980 if (!cave->infinite_rockets)
1981 store(cave, x, y, O_PLAYER);
1985 store_dir(cave, x, y, player_move, O_ROCKET_2);
1986 if (!cave->infinite_rockets)
1987 store(cave, x, y, O_PLAYER);
1991 store_dir(cave, x, y, player_move, O_ROCKET_3);
1992 if (!cave->infinite_rockets)
1993 store(cave, x, y, O_PLAYER);
1997 store_dir(cave, x, y, player_move, O_ROCKET_4);
1998 if (!cave->infinite_rockets)
1999 store(cave, x, y, O_PLAYER);
2003 // cannot fire in other directions
2007 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
2010 // a player with rocket launcher cannot snap elements, so stop here
2014 // pushing and collecting
2015 // if we are 'eating' a teleporter, and the function returns true
2016 // (teleporting worked), break here
2017 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
2020 // player fire is false...
2021 if (do_push(cave, x, y, player_move, FALSE))
2027 // get element. if cannot get, player_get_element will return the same
2028 remains = player_get_element(cave, what, x, y);
2031 // if something changed, OR there is space, move.
2032 if (remains != what || remains == O_SPACE)
2034 // if anything changed, apply the change.
2035 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
2040 case O_PLAYER_STIRRING:
2041 if (cave->kill_player)
2043 explode(cave, x, y);
2047 // stirring sound, if no other walking sound or explosion
2048 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
2050 cave->player_seen_ago = 0;
2051 // bd4 intermission caves have many players. so if one of them has exited,
2052 // do not change the flag anymore. so this if () is needed
2053 if (cave->player_state != GD_PL_EXITED)
2054 cave->player_state = GD_PL_LIVING;
2058 // player "exits" stirring the pot by pressing fire
2059 cave->gravity_disabled = FALSE;
2060 store(cave, x, y, O_PLAYER);
2061 cave->gravity_switch_active = TRUE;
2065 // player holding pneumatic hammer
2066 case O_PLAYER_PNEUMATIC_LEFT:
2067 case O_PLAYER_PNEUMATIC_RIGHT:
2068 // usual player stuff
2069 if (cave->kill_player)
2071 explode(cave, x, y);
2075 cave->player_seen_ago = 0;
2076 if (cave->player_state != GD_PL_EXITED)
2077 cave->player_state = GD_PL_LIVING;
2079 // if hammering time is up, becomes a normal player again.
2080 if (cave->pneumatic_hammer_active_delay == 0)
2081 store(cave, x, y, O_PLAYER);
2084 // the active pneumatic hammer itself
2085 case O_PNEUMATIC_ACTIVE_RIGHT:
2086 case O_PNEUMATIC_ACTIVE_LEFT:
2087 if (cave->pneumatic_hammer_active_delay == 0)
2091 // pneumatic hammer element disappears
2092 store(cave, x, y, O_SPACE);
2094 // which is the new element which appears after that one is hammered?
2095 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
2097 // if there is a new element, display it
2098 // O_NONE might be returned, for example if the element being
2099 // hammered explodes during hammering (by a nearby explosion)
2100 if (new_elem != O_NONE)
2102 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
2104 // and if walls reappear, remember it in array
2105 if (cave->hammered_walls_reappear)
2109 wall_y = (y + 1) % cave->h;
2110 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
2116 // ============================================================================
2117 // S T O N E S, D I A M O N D S
2118 // ============================================================================
2120 case O_STONE: // standing stone
2121 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
2124 case O_MEGA_STONE: // standing mega_stone
2125 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
2128 case O_DIAMOND: // standing diamond
2129 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
2132 case O_NUT: // standing nut
2133 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
2136 case O_DIRT_BALL: // standing dirt ball
2137 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
2140 case O_DIRT_LOOSE: // standing loose dirt
2141 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
2144 case O_FLYING_STONE: // standing stone
2145 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
2148 case O_FLYING_DIAMOND: // standing diamond
2149 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2152 // ============================================================================
2153 // 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
2154 // ============================================================================
2156 case O_DIRT_BALL_F: // falling dirt ball
2157 if (!cave->gravity_disabled)
2158 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2161 case O_DIRT_LOOSE_F: // falling loose dirt
2162 if (!cave->gravity_disabled)
2163 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2166 case O_STONE_F: // falling stone
2167 if (!cave->gravity_disabled)
2169 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2172 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2175 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2178 if (do_fall_try_crush(cave, x, y, cave->gravity))
2181 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2185 case O_MEGA_STONE_F: // falling mega
2186 if (!cave->gravity_disabled)
2188 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2191 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2194 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2197 if (do_fall_try_crush(cave, x, y, cave->gravity))
2200 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2204 case O_DIAMOND_F: // falling diamond
2205 if (!cave->gravity_disabled)
2207 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2210 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2213 if (do_fall_try_crush(cave, x, y, cave->gravity))
2216 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2220 case O_NUT_F: // falling nut
2221 if (!cave->gravity_disabled)
2223 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2226 if (do_fall_try_crush(cave, x, y, cave->gravity))
2229 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2233 case O_FLYING_STONE_F: // falling stone
2234 if (!cave->gravity_disabled)
2236 GdDirection fall_dir = opposite[cave->gravity];
2238 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2241 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2244 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2247 if (do_fall_try_crush(cave, x, y, fall_dir))
2250 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2254 case O_FLYING_DIAMOND_F: // falling diamond
2255 if (!cave->gravity_disabled)
2257 GdDirection fall_dir = opposite[cave->gravity];
2259 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2262 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2265 if (do_fall_try_crush(cave, x, y, fall_dir))
2268 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2272 // ============================================================================
2273 // N I T R O P A C K
2274 // ============================================================================
2276 case O_NITRO_PACK: // standing nitro pack
2277 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2280 case O_NITRO_PACK_F: // falling nitro pack
2281 if (!cave->gravity_disabled)
2283 if (is_space_dir(cave, x, y, cave->gravity)) // if space, falling further
2284 move(cave, x, y, cave->gravity, get(cave, x, y));
2285 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2287 // try magic wall; if true, function did the work
2289 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2291 // falling on a dirt, it does NOT explode - just stops at its place.
2292 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2293 store(cave, x, y, O_NITRO_PACK);
2296 // falling on any other element it explodes
2297 explode(cave, x, y);
2301 case O_NITRO_PACK_EXPLODE: // a triggered nitro pack
2302 explode(cave, x, y);
2305 // ============================================================================
2306 // C R E A T U R E S
2307 // ============================================================================
2313 // if cannot move in any direction, becomes an enclosed cow
2314 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2315 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2316 store(cave, x, y, O_COW_ENCLOSED_1);
2319 // THIS IS THE CREATURE MOVE thing copied.
2320 const GdDirection *creature_move;
2321 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2322 GdElement base; // base element number (which is like O_***_1)
2323 int dir, dirn, dirp; // direction
2327 dir = get(cave, x, y)-base; // facing where
2328 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2330 // now change direction if backwards
2331 if (cave->creatures_backwards)
2336 dirn = (dir + 3) & 3; // fast turn
2337 dirp = (dir + 1) & 3; // slow turn
2341 dirn = (dir + 1) & 3; // fast turn
2342 dirp = (dir + 3) & 3; // slow turn
2345 if (is_space_dir(cave, x, y, creature_move[dirn]))
2346 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2347 else if (is_space_dir(cave, x, y, creature_move[dir]))
2348 move(cave, x, y, creature_move[dir], base + dir); // go on
2350 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2354 // enclosed cows wait some time before turning to a skeleton
2355 case O_COW_ENCLOSED_1:
2356 case O_COW_ENCLOSED_2:
2357 case O_COW_ENCLOSED_3:
2358 case O_COW_ENCLOSED_4:
2359 case O_COW_ENCLOSED_5:
2360 case O_COW_ENCLOSED_6:
2361 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2362 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2363 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2364 is_space_dir(cave, x, y, GD_MV_DOWN))
2365 store(cave, x, y, O_COW_1);
2370 case O_COW_ENCLOSED_7:
2371 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2372 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2373 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2374 is_space_dir(cave, x, y, GD_MV_DOWN))
2375 store(cave, x, y, O_COW_1);
2377 store(cave, x, y, O_SKELETON);
2384 case O_ALT_FIREFLY_1:
2385 case O_ALT_FIREFLY_2:
2386 case O_ALT_FIREFLY_3:
2387 case O_ALT_FIREFLY_4:
2392 case O_ALT_BUTTER_1:
2393 case O_ALT_BUTTER_2:
2394 case O_ALT_BUTTER_3:
2395 case O_ALT_BUTTER_4:
2400 // check if touches a voodoo
2401 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2402 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2403 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2404 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2405 cave->voodoo_touched = TRUE;
2407 // check if touches something bad and should explode (includes voodoo by the flags)
2408 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2409 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2410 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2411 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2412 explode (cave, x, y);
2416 const GdDirection *creature_move;
2417 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2418 GdElement base = -1; // base element number (which is like O_***_1)
2419 int dir, dirn, dirp; // direction
2421 if (get(cave, x, y) >= O_FIREFLY_1 &&
2422 get(cave, x, y) <= O_FIREFLY_4)
2424 else if (get(cave, x, y) >= O_BUTTER_1 &&
2425 get(cave, x, y) <= O_BUTTER_4)
2427 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2428 get(cave, x, y) <= O_STONEFLY_4)
2429 base = O_STONEFLY_1;
2430 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2431 get(cave, x, y) <= O_ALT_FIREFLY_4)
2432 base = O_ALT_FIREFLY_1;
2433 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2434 get(cave, x, y) <= O_ALT_BUTTER_4)
2435 base = O_ALT_BUTTER_1;
2437 dir = get(cave, x, y) - base; // facing where
2438 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2440 // now change direction if backwards
2441 if (cave->creatures_backwards)
2446 dirn = (dir + 3) & 3; // fast turn
2447 dirp = (dir + 1) & 3; // slow turn
2451 dirn = (dir + 1) & 3; // fast turn
2452 dirp = (dir + 3) & 3; // slow turn
2455 if (is_space_dir(cave, x, y, creature_move[dirn]))
2456 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2457 else if (is_space_dir(cave, x, y, creature_move[dir]))
2458 move(cave, x, y, creature_move[dir], base + dir); // go on
2460 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2464 case O_WAITING_STONE:
2465 if (is_space_dir(cave, x, y, grav_compat))
2467 // beginning to fall
2469 move(cave, x, y, grav_compat, O_CHASING_STONE);
2471 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2473 // rolling down a brick wall or a stone
2474 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2475 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2476 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2478 // maybe rolling left - see case O_STONE to understand why we use cw_fourth here
2479 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2481 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2482 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2483 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2486 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2491 case O_CHASING_STONE:
2493 int px = cave->px[0];
2494 int py = cave->py[0];
2495 boolean horizontal = gd_rand_boolean(cave->random);
2496 boolean dont_move = FALSE;
2504 // ------------------------------------------------------------
2505 // check for a horizontal movement
2506 // ------------------------------------------------------------
2509 // if coordinates are the same
2511 horizontal = !horizontal;
2518 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2520 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2524 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2526 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2535 horizontal = !horizontal;
2543 // ------------------------------------------------------------
2544 // check for a vertical movement
2545 // ------------------------------------------------------------
2548 // if coordinates are the same
2550 horizontal = !horizontal;
2556 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2558 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2562 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2564 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2573 horizontal = !horizontal;
2586 // if we should move in both directions, but can not move in any, stop.
2591 // check for horizontal
2594 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2595 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2596 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2597 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2598 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2599 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2603 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2604 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2605 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2606 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2607 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2608 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2613 // check for vertical
2616 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2617 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2618 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2619 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2620 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2621 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2625 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2626 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2627 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2628 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2629 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2630 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2638 if (cave->replicators_wait_frame == 0 &&
2639 cave->replicators_active &&
2640 !cave->gravity_disabled)
2642 // only replicate, if space is under it.
2643 // do not replicate players!
2644 // also obeys gravity settings.
2645 // only replicate element if it is not a scanned one
2646 // do not replicate space... that condition looks like it
2647 // makes no sense, but otherwise it generates SCANNED spaces,
2648 // which cannot be "collected" by the player, so he cannot run
2649 // under a replicator
2650 if (is_space_dir(cave, x, y, cave->gravity) &&
2651 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2652 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2654 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2655 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2664 if (cave->biters_wait_frame == 0)
2666 static GdDirection biter_move[] =
2674 // direction, last two bits 0..3
2675 int dir = get(cave, x, y) - O_BITER_1;
2676 int dirn = (dir + 3) & 3;
2677 int dirp = (dir + 1) & 3;
2679 GdElement made_sound_of = O_NONE;
2681 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2683 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2685 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2686 if (biter_try[i] != O_SPACE)
2687 made_sound_of = O_BITER_1; // sound of a biter eating
2690 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2692 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2693 if (biter_try[i] != O_SPACE)
2694 made_sound_of = O_BITER_1; // sound of a biter eating
2697 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2699 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2700 if (biter_try[i] != O_SPACE)
2701 made_sound_of = O_BITER_1; // sound of a biter eating
2706 if (i == ARRAY_SIZE(biter_try))
2707 // i = number of elements in array: could not move, so just turn
2708 store(cave, x, y, O_BITER_1 + dirp);
2709 else if (biter_try[i] == O_STONE)
2711 // if there was a stone there, where we moved...
2712 // do not eat stones, just throw them back
2713 store(cave, x, y, O_STONE);
2714 made_sound_of = O_STONE;
2717 // if biter did move, we had sound. play it.
2718 if (made_sound_of != O_NONE)
2719 play_sound_of_element(cave, made_sound_of, x, y);
2727 // check if touches a voodoo
2728 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2729 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2730 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2731 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2732 cave->voodoo_touched = TRUE;
2734 // check if touches something bad and should explode (includes voodoo by the flags)
2735 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2736 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2737 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2738 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2739 explode (cave, x, y);
2743 const GdDirection *creature_move;
2744 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2745 GdElement base = O_DRAGONFLY_1; // base element number (which is like O_***_1)
2746 int dir, dirn; // direction
2748 dir = get(cave, x, y)-base; // facing where
2749 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2751 // now change direction if backwards
2752 if (cave->creatures_backwards)
2756 dirn = (dir + 3) & 3; // fast turn
2758 dirn = (dir + 1) & 3; // fast turn
2760 // if can move forward, does so.
2761 if (is_space_dir(cave, x, y, creature_move[dir]))
2762 move(cave, x, y, creature_move[dir], base + dir);
2764 // otherwise turns 90 degrees in place.
2765 store(cave, x, y, base + dirn);
2770 store(cave, x, y, O_BLADDER_1);
2781 // bladder with any delay state: try to convert to clock.
2782 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2783 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))
2785 // if touches the specified element, let it be a clock
2786 store(cave, x, y, O_PRE_CLOCK_1);
2788 // plays the bladder convert sound
2789 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2793 // is space over the bladder?
2794 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2796 if (get(cave, x, y) == O_BLADDER_8)
2798 // if it is a bladder 8, really move up
2799 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2800 play_sound_of_element(cave, O_BLADDER, x, y);
2803 // if smaller delay, just increase delay.
2807 // if not space, is something sloped over the bladder?
2808 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2809 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2811 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2812 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2813 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2815 // rolling up, to left
2816 if (get(cave, x, y) == O_BLADDER_8)
2818 // if it is a bladder 8, really roll
2819 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2820 play_sound_of_element(cave, O_BLADDER, x, y);
2823 // if smaller delay, just increase delay.
2826 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2827 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2828 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2830 // rolling up, to left
2831 if (get(cave, x, y) == O_BLADDER_8)
2833 // if it is a bladder 8, really roll
2834 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2835 play_sound_of_element(cave, O_BLADDER, x, y);
2838 // if smaller delay, just increase delay.
2843 // no space, no sloped thing over it - store bladder 1 and that is for now.
2845 store(cave, x, y, O_BLADDER_1);
2850 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2851 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2852 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2853 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2854 explode (cave, x, y);
2859 // the ghost is given four possibilities to move.
2860 for (i = 0; i < 4; i++)
2862 static GdDirection dirs[] =
2869 GdDirection random_dir;
2871 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2872 if (is_space_dir(cave, x, y, random_dir))
2874 move(cave, x, y, random_dir, O_GHOST);
2875 break; // ghost did move -> exit loop
2881 // ============================================================================
2882 // A C T I V E E L E M E N T S
2883 // ============================================================================
2886 // emulating BD1 amoeba+magic wall bug
2887 if (cave->convert_amoeba_this_frame && amoeba_found_enclosed)
2889 store(cave, x, y, cave->amoeba_enclosed_effect);
2894 switch (cave->amoeba_state)
2897 store(cave, x, y, cave->amoeba_too_big_effect);
2900 case GD_AM_ENCLOSED:
2901 store(cave, x, y, cave->amoeba_enclosed_effect);
2904 case GD_AM_SLEEPING:
2906 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2907 if (amoeba_found_enclosed)
2908 // if still found enclosed, check all four directions, if this one is able to grow.
2909 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2910 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2911 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2912 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2914 // not enclosed. this is a local (per scan) flag!
2915 amoeba_found_enclosed = FALSE;
2916 cave->amoeba_state = GD_AM_AWAKE;
2919 // if alive, check in which dir to grow (or not)
2920 if (cave->amoeba_state == GD_AM_AWAKE)
2922 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2924 switch (gd_rand_int_range(cave->random, 0, 4))
2926 // decided to grow, choose a random direction.
2927 case 0: // let this be up. numbers indifferent.
2928 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2929 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2933 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2934 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2938 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2939 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2943 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2944 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2956 // check if it is touching an amoeba, and explosion is enabled
2957 if (cave->amoeba_2_explodes_by_amoeba &&
2958 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2959 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2960 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2961 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2962 explode (cave, x, y);
2964 switch (cave->amoeba_2_state)
2967 store(cave, x, y, cave->amoeba_2_too_big_effect);
2970 case GD_AM_ENCLOSED:
2971 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2974 case GD_AM_SLEEPING:
2976 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2977 if (amoeba_2_found_enclosed)
2978 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2979 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2980 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2981 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2983 // not enclosed. this is a local (per scan) flag!
2984 amoeba_2_found_enclosed = FALSE;
2985 cave->amoeba_2_state = GD_AM_AWAKE;
2988 // if it is alive, decide if it attempts to grow
2989 if (cave->amoeba_2_state == GD_AM_AWAKE)
2990 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2992 switch (gd_rand_int_range(cave->random, 0, 4))
2994 // decided to grow, choose a random direction.
2995 case 0: // let this be up. numbers indifferent.
2996 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2997 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
3001 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
3002 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
3006 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
3007 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
3011 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
3012 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
3022 // choose randomly, if it spreads
3023 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
3025 // the current one explodes
3026 store(cave, x, y, cave->acid_turns_to);
3028 // and if neighbours are eaten, put acid there.
3029 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
3031 play_sound_of_element(cave, O_ACID, x, y);
3032 store_dir(cave, x, y, GD_MV_UP, O_ACID);
3035 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
3037 play_sound_of_element(cave, O_ACID, x, y);
3038 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
3041 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
3043 play_sound_of_element(cave, O_ACID, x, y);
3044 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
3047 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
3049 play_sound_of_element(cave, O_ACID, x, y);
3050 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
3057 if (!cave->water_does_not_flow_down &&
3058 is_space_dir(cave, x, y, GD_MV_DOWN))
3059 // emulating the odd behaviour in crdr
3060 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
3062 if (is_space_dir(cave, x, y, GD_MV_UP))
3063 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
3065 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3066 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
3068 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3069 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
3073 store(cave, x, y, O_WATER);
3076 case O_H_EXPANDING_WALL:
3077 case O_V_EXPANDING_WALL:
3078 case O_H_EXPANDING_STEEL_WALL:
3079 case O_V_EXPANDING_STEEL_WALL:
3080 // checks first if direction is changed.
3081 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
3082 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
3083 !cave->expanding_wall_changed) ||
3084 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
3085 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
3086 cave->expanding_wall_changed))
3088 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3090 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3091 play_sound_of_element(cave, get(cave, x, y), x, y);
3094 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3095 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3096 play_sound_of_element(cave, get(cave, x, y), x, y);
3101 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3102 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3103 play_sound_of_element(cave, get(cave, x, y), x, y);
3106 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3107 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3108 play_sound_of_element(cave, get(cave, x, y), x, y);
3113 case O_EXPANDING_WALL:
3114 case O_EXPANDING_STEEL_WALL:
3115 // the wall which grows in all four directions.
3116 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3118 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3119 play_sound_of_element(cave, get(cave, x, y), x, y);
3122 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3123 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3124 play_sound_of_element(cave, get(cave, x, y), x, y);
3127 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3128 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3129 play_sound_of_element(cave, get(cave, x, y), x, y);
3132 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3133 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3134 play_sound_of_element(cave, get(cave, x, y), x, y);
3140 ; // to make compilers happy ...
3142 Info("Step[%03d]", cave->frame); // XXX
3144 int rrr = gd_cave_c64_random(cave);
3147 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
3148 (rrr & cave->slime_permeability_c64) == 0);
3151 * unpredictable: gd_rand_int
3152 * predictable: c64 predictable random generator.
3153 * for predictable, a random number is generated,
3154 * whether or not it is even possible that the stone will be able to pass.
3156 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
3158 GdDirection grav = cave->gravity;
3159 GdDirection oppos = opposite[cave->gravity];
3161 // space under the slime? elements may pass from top to bottom then.
3162 if (is_space_dir(cave, x, y, grav))
3164 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3166 // output a falling xy under
3167 store_dir(cave, x, y, grav, cave->slime_converts_1);
3169 store_dir(cave, x, y, oppos, O_SPACE);
3170 play_sound_of_element(cave, O_SLIME, x, y);
3172 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3174 store_dir(cave, x, y, grav, cave->slime_converts_2);
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) == cave->slime_eats_3)
3180 store_dir(cave, x, y, grav, cave->slime_converts_3);
3181 store_dir(cave, x, y, oppos, O_SPACE);
3182 play_sound_of_element(cave, O_SLIME, x, y);
3184 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3186 // waiting stones pass without awakening
3187 store_dir(cave, x, y, grav, O_WAITING_STONE);
3188 store_dir(cave, x, y, oppos, O_SPACE);
3189 play_sound_of_element(cave, O_SLIME, x, y);
3191 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3193 // chasing stones pass
3194 store_dir(cave, x, y, grav, O_CHASING_STONE);
3195 store_dir(cave, x, y, oppos, O_SPACE);
3196 play_sound_of_element(cave, O_SLIME, x, y);
3201 // or space over the slime? elements may pass from bottom to up then.
3202 if (is_space_dir(cave, x, y, oppos))
3204 if (get_dir(cave, x, y, grav) == O_BLADDER)
3206 // bladders move UP the slime
3207 store_dir(cave, x, y, grav, O_SPACE);
3208 store_dir(cave, x, y, oppos, O_BLADDER_1);
3209 play_sound_of_element(cave, O_SLIME, x, y);
3211 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3213 store_dir(cave, x, y, grav, O_SPACE);
3214 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3215 play_sound_of_element(cave, O_SLIME, x, y);
3217 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3219 store_dir(cave, x, y, grav, O_SPACE);
3220 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3221 play_sound_of_element(cave, O_SLIME, x, y);
3228 case O_FALLING_WALL:
3229 if (is_space_dir(cave, x, y, grav_compat))
3231 // try falling if space under.
3234 for (yy = y + 1; yy < y + cave->h; yy++)
3235 // yy < y + cave->h is to check everything OVER the wall - since caves wrap around !!
3236 if (get(cave, x, yy) != O_SPACE)
3237 // stop cycle when other than space
3240 // if scanning stopped by a player... start falling!
3241 if (get(cave, x, yy) == O_PLAYER ||
3242 get(cave, x, yy) == O_PLAYER_GLUED ||
3243 get(cave, x, yy) == O_PLAYER_BOMB)
3245 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3246 // no sound when the falling wall starts falling!
3251 case O_FALLING_WALL_F:
3252 switch (get_dir(cave, x, y, grav_compat))
3255 case O_PLAYER_GLUED:
3257 // if player under, it explodes - the falling wall, not the player!
3258 explode(cave, x, y);
3263 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3268 play_sound_of_element(cave, get(cave, x, y), x, y);
3269 store(cave, x, y, O_FALLING_WALL);
3274 // ============================================================================
3275 // C O N V E Y O R B E L T S
3276 // ============================================================================
3278 case O_CONVEYOR_RIGHT:
3279 case O_CONVEYOR_LEFT:
3280 // only works if gravity is up or down!!!
3281 // first, check for gravity and running belts.
3282 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3284 const GdDirection *dir;
3288 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3289 if (cave->conveyor_belts_direction_changed)
3291 dir = left ? ccw_eighth : cw_eighth;
3293 // CHECK IF IT CONVEYS THE ELEMENT ABOVE IT
3294 // if gravity is normal, and the conveyor belt has something
3295 // ABOVE which can be moved
3297 // the gravity is up, so anything that should float now goes
3298 // DOWN and touches the conveyor
3299 if ((cave->gravity == GD_MV_DOWN &&
3300 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3301 (cave->gravity == GD_MV_UP &&
3302 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3304 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3305 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3307 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move
3308 store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space.
3312 // CHECK IF IT CONVEYS THE ELEMENT BELOW IT
3313 if ((cave->gravity == GD_MV_UP &&
3314 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3315 (cave->gravity == GD_MV_DOWN &&
3316 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3318 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3319 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3321 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move
3322 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear.
3328 // ============================================================================
3330 // ============================================================================
3333 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3334 move(cave, x, y, GD_MV_RIGHT, O_ROCKET_1);
3336 explode(cave, x, y);
3340 if (is_space_dir(cave, x, y, GD_MV_UP))
3341 move(cave, x, y, GD_MV_UP, O_ROCKET_2);
3343 explode(cave, x, y);
3347 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3348 move(cave, x, y, GD_MV_LEFT, O_ROCKET_3);
3350 explode(cave, x, y);
3354 if (is_space_dir(cave, x, y, GD_MV_DOWN))
3355 move(cave, x, y, GD_MV_DOWN, O_ROCKET_4);
3357 explode(cave, x, y);
3360 // ============================================================================
3361 // S I M P L E C H A N G I N G; E X P L O S I O N S
3362 // ============================================================================
3365 store(cave, x, y, cave->explosion_effect);
3369 store(cave, x, y, O_DIAMOND);
3373 store(cave, x, y, cave->diamond_birth_effect);
3377 store(cave, x, y, O_STONE);
3380 case O_NITRO_EXPL_4:
3381 store(cave, x, y, cave->nitro_explosion_effect);
3385 store(cave, x, y, cave->bomb_explosion_effect);
3388 case O_AMOEBA_2_EXPL_4:
3389 store(cave, x, y, cave->amoeba_2_explosion_effect);
3392 case O_GHOST_EXPL_4:
3394 static GdElement ghost_explode[] =
3396 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3397 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3398 O_WAITING_STONE, O_BITER_1
3401 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3406 store(cave, x, y, O_STEEL);
3410 store(cave, x, y, O_CLOCK);
3414 explode(cave, x, y);
3417 case O_TRAPPED_DIAMOND:
3418 if (cave->diamond_key_collected)
3419 store(cave, x, y, O_DIAMOND);
3423 if (cave->gate_open) // if no more diamonds needed
3424 store(cave, x, y, O_OUTBOX); // open outbox
3427 case O_PRE_INVIS_OUTBOX:
3428 if (cave->gate_open) // if no more diamonds needed
3429 store(cave, x, y, O_INVIS_OUTBOX); // open outbox. invisible one :P
3433 if (cave->hatched && !inbox_toggle) // if it is time of birth
3434 store(cave, x, y, O_PRE_PL_1);
3435 inbox_toggle = !inbox_toggle;
3439 store(cave, x, y, O_PLAYER);
3464 case O_GHOST_EXPL_1:
3465 case O_GHOST_EXPL_2:
3466 case O_GHOST_EXPL_3:
3476 case O_NITRO_EXPL_1:
3477 case O_NITRO_EXPL_2:
3478 case O_NITRO_EXPL_3:
3479 case O_AMOEBA_2_EXPL_1:
3480 case O_AMOEBA_2_EXPL_2:
3481 case O_AMOEBA_2_EXPL_3:
3482 // simply the next identifier
3501 found_water = TRUE; // for sound
3502 // simply the next identifier
3506 case O_BLADDER_SPENDER:
3507 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3509 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3510 store(cave, x, y, O_PRE_STEEL_1);
3511 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3516 // other inanimate elements that do nothing
3523 // ============================================================================
3525 // ============================================================================
3527 // another scan-like routine:
3528 // short explosions (for example, in bd1) started with explode_2.
3529 // internally we use explode_1; and change it to explode_2 if needed.
3530 if (cave->short_explosions)
3532 for (y = 0; y < cave->h; y++)
3534 for (x = 0; x < cave->w; x++)
3536 if (is_first_stage_of_explosion(cave, x, y))
3538 next(cave, x, y); // select next frame of explosion
3540 // forget scanned flag immediately
3541 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3547 // finally: forget "scanned" flags for objects.
3548 // also, check for time penalties.
3549 // these is something like an effect table, but we do not really use one.
3550 for (y = 0; y < cave->h; y++)
3552 for (x = 0; x < cave->w; x++)
3554 if (get(cave, x, y) & SCANNED)
3555 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3557 if (get(cave, x, y) == O_TIME_PENALTY)
3559 store(cave, x, y, O_GRAVESTONE);
3561 // there is time penalty for destroying the voodoo
3562 time_decrement_sec += cave->time_penalty;
3567 // this loop finds the coordinates of the player. needed for scrolling and chasing stone.
3568 // but we only do this, if a living player was found. if not yet, the setup
3569 // routine coordinates are used
3570 if (cave->player_state == GD_PL_LIVING)
3572 if (cave->active_is_first_found)
3574 // to be 1stb compatible, we do everything backwards.
3575 for (y = cave->h - 1; y >= 0; y--)
3577 for (x = cave->w - 1; x >= 0; x--)
3579 if (is_player(cave, x, y))
3581 // here we remember the coordinates.
3590 // as in the original: look for the last one
3591 for (y = 0; y < cave->h; y++)
3593 for (x = 0; x < cave->w; x++)
3595 if (is_player(cave, x, y))
3597 // here we remember the coordinates.
3606 // record coordinates of player for chasing stone
3607 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3609 cave->px[i] = cave->px[i + 1];
3610 cave->py[i] = cave->py[i + 1];
3613 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3614 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3618 // update timing calculated by iterating and counting elements
3619 update_cave_speed(cave);
3621 // cave 3 sounds. precedence is controlled by the sound_play function.
3622 // but we have to check amoeba&magic together as they had a different gritty sound when mixed
3624 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3626 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3627 cave->magic_wall_sound);
3629 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3630 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3631 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3633 if (amoeba_sound && magic_sound)
3635 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3640 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3641 else if (magic_sound)
3642 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3647 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3648 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3649 play_sound_of_element(cave, O_AMOEBA, -1, -1);
3652 // pneumatic hammer sound - overrides everything.
3653 if (cave->pneumatic_hammer_active_delay > 0)
3654 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3657 // ============================================================================
3659 // ============================================================================
3663 // check if player is alive.
3664 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
3665 cave->player_state = GD_PL_DIED;
3667 // check if any voodoo exploded, and kill players the next scan if that happended.
3668 if (cave->voodoo_touched)
3669 cave->kill_player = TRUE;
3673 // check flags after evaluating.
3674 if (cave->amoeba_state == GD_AM_AWAKE)
3676 if (amoeba_count >= cave->amoeba_max_count)
3677 cave->amoeba_state = GD_AM_TOO_BIG;
3678 if (amoeba_found_enclosed)
3679 cave->amoeba_state = GD_AM_ENCLOSED;
3682 // amoeba can also be turned into diamond by magic wall
3683 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3684 cave->amoeba_state = GD_AM_ENCLOSED;
3687 if (cave->amoeba_2_state == GD_AM_AWAKE)
3689 // check flags after evaluating.
3690 if (amoeba_2_count >= cave->amoeba_2_max_count)
3691 cave->amoeba_2_state = GD_AM_TOO_BIG;
3693 if (amoeba_2_found_enclosed)
3694 cave->amoeba_2_state = GD_AM_ENCLOSED;
3697 // amoeba 2 can also be turned into diamond by magic wall
3698 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3699 cave->amoeba_2_state = GD_AM_ENCLOSED;
3701 // now check times. ---------------------------
3702 // decrement time if a voodoo was killed.
3703 cave->time -= time_decrement_sec * cave->timing_factor;
3707 // only decrement time when player is already born.
3710 int secondsbefore, secondsafter;
3712 secondsbefore = cave->time / cave->timing_factor;
3713 cave->time -= cave->speed;
3714 if (cave->time <= 0)
3717 secondsafter = cave->time / cave->timing_factor;
3718 if (cave->time / cave->timing_factor < 10)
3719 // if less than 10 seconds, no walking sound, but play explosion sound
3720 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3722 if (secondsbefore != secondsafter)
3723 gd_cave_set_seconds_sound(cave);
3726 // a gravity switch was activated; seconds counting down
3727 if (cave->gravity_will_change > 0)
3729 cave->gravity_will_change -= cave->speed;
3730 if (cave->gravity_will_change < 0)
3731 cave->gravity_will_change = 0;
3733 if (cave->gravity_will_change == 0)
3735 cave->gravity = cave->gravity_next_direction;
3736 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound
3740 // creatures direction automatically change
3741 if (cave->creatures_direction_will_change > 0)
3743 cave->creatures_direction_will_change -= cave->speed;
3744 if (cave->creatures_direction_will_change < 0)
3745 cave->creatures_direction_will_change = 0;
3747 if (cave->creatures_direction_will_change == 0)
3749 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3751 cave->creatures_backwards = !cave->creatures_backwards;
3752 cave->creatures_direction_will_change =
3753 cave->creatures_direction_auto_change_time * cave->timing_factor;
3757 // magic wall; if active&wait or not wait for hatching
3758 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3759 (cave->hatched || !cave->magic_timer_wait_for_hatching) &&
3760 !(cave->magic_wall_time == 0 && cave->magic_timer_zero_is_infinite))
3762 cave->magic_wall_time -= cave->speed;
3763 if (cave->magic_wall_time < 0)
3764 cave->magic_wall_time = 0;
3765 if (cave->magic_wall_time == 0)
3766 cave->magic_wall_state = GD_MW_EXPIRED;
3769 // we may wait for hatching, when starting amoeba
3770 if (cave->amoeba_timer_started_immediately ||
3771 (cave->amoeba_state == GD_AM_AWAKE &&
3772 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3774 cave->amoeba_time -= cave->speed;
3775 if (cave->amoeba_time < 0)
3776 cave->amoeba_time = 0;
3777 if (cave->amoeba_time == 0)
3778 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3781 // we may wait for hatching, when starting amoeba
3782 if (cave->amoeba_timer_started_immediately ||
3783 (cave->amoeba_2_state == GD_AM_AWAKE &&
3784 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3786 cave->amoeba_2_time -= cave->speed;
3787 if (cave->amoeba_2_time < 0)
3788 cave->amoeba_2_time = 0;
3789 if (cave->amoeba_2_time == 0)
3790 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3793 // check for player hatching.
3794 start_signal = FALSE;
3796 // if not the c64 scheduling, but the correct frametime is used,
3797 // hatching delay should always be decremented.
3798 // otherwise, the if (millisecs...) condition below will set this.
3799 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3801 // NON-C64 scheduling
3802 if (cave->hatching_delay_frame > 0)
3804 // for milliseconds-based, non-c64 schedulings, hatching delay means frames.
3805 cave->hatching_delay_frame--;
3806 if (cave->hatching_delay_frame == 0)
3807 start_signal = TRUE;
3813 if (cave->hatching_delay_time > 0)
3815 // for c64 schedulings, hatching delay means milliseconds.
3816 cave->hatching_delay_time -= cave->speed;
3817 if (cave->hatching_delay_time <= 0)
3819 cave->hatching_delay_time = 0;
3820 start_signal = TRUE;
3825 // if decremented hatching, and it became zero:
3828 // THIS IS THE CAVE START SIGNAL
3830 // record that now the cave is in its normal state
3831 cave->hatched = TRUE;
3833 // if diamonds needed is below zero, we count the available diamonds now.
3834 gd_cave_count_diamonds(cave);
3836 // setup direction auto change
3837 if (cave->creatures_direction_auto_change_time)
3839 cave->creatures_direction_will_change =
3840 cave->creatures_direction_auto_change_time * cave->timing_factor;
3842 if (cave->creatures_direction_auto_change_on_start)
3843 cave->creatures_backwards = !cave->creatures_backwards;
3846 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3850 if (cave->biters_wait_frame == 0)
3851 cave->biters_wait_frame = cave->biter_delay_frame;
3853 cave->biters_wait_frame--;
3855 // replicators delay
3856 if (cave->replicators_wait_frame == 0)
3857 cave->replicators_wait_frame = cave->replicator_delay_frame;
3859 cave->replicators_wait_frame--;
3862 // ============================================================================
3864 // ============================================================================
3867 // check if cave failed by timeout is done in main game engine
3869 // check if cave failed by timeout
3870 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3872 gd_cave_clear_sounds(cave);
3873 cave->player_state = GD_PL_TIMEOUT;
3874 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3878 // set these for drawing.
3879 cave->last_direction = player_move;
3881 // here we remember last movements for animation. this is needed here,
3882 // as animation is in sync with the game, not the keyboard directly.
3883 // (for example, after exiting the cave, the player was "running" in the
3884 // original, till bonus points were counted for remaining time and so on.
3885 if (player_move == GD_MV_LEFT ||
3886 player_move == GD_MV_UP_LEFT ||
3887 player_move == GD_MV_DOWN_LEFT)
3888 cave->last_horizontal_direction = GD_MV_LEFT;
3890 if (player_move == GD_MV_RIGHT ||
3891 player_move == GD_MV_UP_RIGHT ||
3892 player_move == GD_MV_DOWN_RIGHT)
3893 cave->last_horizontal_direction = GD_MV_RIGHT;
3895 cave->frame++; // XXX
3898 void set_initial_cave_speed(GdCave *cave)
3903 // set cave get function; to implement perfect or lineshifting borders
3904 if (cave->lineshift)
3905 cave->getp = getp_shift;
3907 cave->getp = getp_perfect;
3909 // check whether to scan the first and last line
3910 if (cave->border_scan_first_and_last)
3921 for (y = ymin; y <= ymax; y++)
3923 for (x = 0; x < cave->w; x++)
3925 // add the ckdelay correction value for every element seen.
3926 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3930 // update timing calculated by iterating and counting elements
3931 update_cave_speed(cave);