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)
549 // raw values without range correction
550 int raw_x = x + gd_dx[dir];
551 int raw_y = y + gd_dy[dir];
553 // final values with range correction
554 int new_x = getx(cave, raw_x, raw_y);
555 int new_y = gety(cave, raw_x, raw_y);
556 int new_dir = (dir > GD_MV_TWICE ? dir - GD_MV_TWICE : dir);
558 // if tile is moving two steps at once, correct old position
559 if (dir > GD_MV_TWICE)
561 raw_x = x + gd_dx[new_dir];
562 raw_y = y + gd_dy[new_dir];
564 old_x = getx(cave, raw_x, raw_y);
565 old_y = gety(cave, raw_x, raw_y);
568 game_bd.game->dir_buffer_from[old_y][old_x] = new_dir;
569 game_bd.game->dir_buffer_to[new_y][new_x] = new_dir;
572 // store an element at the given position
573 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
575 GdElement *e = getp(cave, x, y);
579 play_sound_of_element(cave, O_LAVA, x, y);
587 // store an element with SCANNED flag turned on
588 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
590 store(cave, x, y, element | SCANNED);
593 // store an element to a neighbouring cell
594 static inline void store_dir(GdCave *cave, const int x, const int y,
595 const GdDirection dir, const GdElement element)
597 store_dir_buffer(cave, x, y, dir);
598 store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED);
601 // store an element to a neighbouring cell
602 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
603 const GdDirection dir, const GdElement element)
605 store_dir_buffer(cave, x, y, dir);
606 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
609 // move element to direction; then place space at x, y
610 static inline void move(GdCave *cave, const int x, const int y,
611 const GdDirection dir, const GdElement e)
613 store_dir(cave, x, y, dir, e);
614 store(cave, x, y, O_SPACE);
617 // increment a cave element; can be used for elements which are one after
618 // the other, for example bladder1, bladder2, bladder3...
619 static inline void next(GdCave *cave, const int x, const int y)
621 (*getp(cave, x, y))++;
624 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
626 if (non_explodable (cave, x, y))
629 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
630 cave->voodoo_touched = TRUE;
632 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
633 // voodoo turns into a time penalty
634 store_sc(cave, x, y, O_TIME_PENALTY);
635 else if (get(cave, x, y) == O_NITRO_PACK ||
636 get(cave, x, y) == O_NITRO_PACK_F)
637 // nitro pack inside an explosion - it is now triggered
638 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
640 // for everything else
641 store_sc(cave, x, y, explode_to);
644 // a creature explodes to a 3x3 something.
645 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
649 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
650 cave->ckdelay += 1200;
651 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
653 for (yy = y - 1; yy <= y + 1; yy++)
654 for (xx = x - 1; xx <= x + 1; xx++)
655 cell_explode(cave, xx, yy, explode_to);
658 static void nitro_explode(GdCave *cave, int x, int y)
662 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
663 cave->ckdelay += 1200;
664 gd_sound_play(cave, GD_S_NITRO_PACK_EXPLODING, get(cave, x, y), x, y);
666 for (yy = y - 1; yy <= y + 1; yy++)
667 for (xx = x - 1; xx <= x + 1; xx++)
668 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
670 // the current cell is explicitly changed into a nitro expl,
671 // as cell_explode changes it to a triggered nitro pack
672 store_sc(cave, x, y, O_NITRO_EXPL_1);
675 // a voodoo explodes, leaving a 3x3 steel and a time penalty behind.
676 static void voodoo_explode(GdCave *cave, int x, int y)
680 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
681 cave->ckdelay += 1000;
683 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
684 if (cave->voodoo_any_hurt_kills_player)
685 cave->voodoo_touched = TRUE;
687 // voodoo explodes to 3x3 steel
688 for (yy = y - 1; yy <= y + 1; yy++)
689 for (xx = x - 1; xx <= x + 1; xx++)
690 store_sc(cave, xx, yy, O_PRE_STEEL_1);
692 // middle is a time penalty (which will be turned into a gravestone)
693 store_sc(cave, x, y, O_TIME_PENALTY);
697 a bomb does not explode the voodoo, neither does the ghost.
698 this function check this, and stores the new element or not.
699 destroying the voodoo is also controlled by the
700 voodoo_disappear_in_explosion flag.
702 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
704 if (non_explodable (cave, x, y))
707 // bomb does not explode voodoo
708 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
711 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
712 cave->voodoo_touched = TRUE;
714 store_sc (cave, x, y, expl);
717 // X shaped ghost explosion; does not touch voodoo!
718 static void ghost_explode(GdCave *cave, const int x, const int y)
720 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
722 // the processing of an explosion took pretty much time: processing 5 elements
723 cave->ckdelay += 650;
725 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
726 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
727 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
728 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
729 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
732 // +shaped bomb explosion; does not touch voodoo!
733 static void bomb_explode(GdCave *cave, const int x, const int y)
735 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
737 // the processing of an explosion took pretty much time: processing 5 elements
738 cave->ckdelay += 650;
740 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
741 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
742 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
743 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
744 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
748 explode an element with the appropriate type of exlposion.
750 static void explode(GdCave *cave, int x, int y)
752 GdElement e = get(cave, x, y) & O_MASK;
757 ghost_explode(cave, x, y);
761 bomb_explode(cave, x, y);
765 voodoo_explode(cave, x, y);
770 case O_NITRO_PACK_EXPLODE:
771 nitro_explode(cave, x, y);
775 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
778 case O_FALLING_WALL_F:
779 creature_explode(cave, x, y, O_EXPLODE_1);
786 creature_explode(cave, x, y, O_EXPLODE_1);
793 creature_explode(cave, x, y, cave->butterfly_explode_to);
800 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
807 creature_explode(cave, x, y, cave->firefly_explode_to);
810 case O_ALT_FIREFLY_1:
811 case O_ALT_FIREFLY_2:
812 case O_ALT_FIREFLY_3:
813 case O_ALT_FIREFLY_4:
814 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
820 case O_PLAYER_STIRRING:
821 case O_PLAYER_ROCKET_LAUNCHER:
822 case O_PLAYER_PNEUMATIC_LEFT:
823 case O_PLAYER_PNEUMATIC_RIGHT:
824 creature_explode(cave, x, y, O_EXPLODE_1);
831 creature_explode(cave, x, y, cave->stonefly_explode_to);
838 creature_explode(cave, x, y, cave->dragonfly_explode_to);
846 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
848 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
852 player eats specified object.
853 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
854 returns other element if something other appears there and he can't move.
855 cave pointer is needed to know the diamond values.
857 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
864 cave->diamond_key_collected = TRUE;
865 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
870 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
875 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
880 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
887 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
894 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
901 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
906 case O_CREATURE_SWITCH: // creatures change direction.
907 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
908 cave->creatures_backwards = !cave->creatures_backwards;
911 case O_EXPANDING_WALL_SWITCH: // expanding wall change direction.
912 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
913 cave->expanding_wall_changed = !cave->expanding_wall_changed;
916 case O_BITER_SWITCH: // biter change delay
917 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
918 cave->biter_delay_frame++;
919 if (cave->biter_delay_frame == 4)
920 cave->biter_delay_frame = 0;
923 case O_REPLICATOR_SWITCH: // replicator on/off switch
924 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
925 cave->replicators_active = !cave->replicators_active;
928 case O_CONVEYOR_SWITCH: // conveyor belts on/off
929 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
930 cave->conveyor_belts_active = !cave->conveyor_belts_active;
933 case O_CONVEYOR_DIR_SWITCH: // conveyor belts switch direction
934 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
935 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
941 case O_STEEL_EATABLE:
942 case O_BRICK_EATABLE:
943 case O_DIRT_SLOPED_UP_RIGHT:
944 case O_DIRT_SLOPED_UP_LEFT:
945 case O_DIRT_SLOPED_DOWN_LEFT:
946 case O_DIRT_SLOPED_DOWN_RIGHT:
949 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
953 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
954 cave->sweet_eaten = TRUE;
957 case O_PNEUMATIC_HAMMER:
958 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
959 cave->got_pneumatic_hammer = TRUE;
964 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
965 cave->time += cave->time_bonus * cave->timing_factor;
966 if (cave->time > cave->max_time * cave->timing_factor)
967 cave->time -= cave->max_time * cave->timing_factor;
968 // no space, rather a dirt remains there...
972 case O_FLYING_DIAMOND:
973 // prevent diamond sounds for O_SKELETON (see below)
974 if (x != -1 && y != -1)
975 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
976 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
978 cave->score += cave->diamond_value;
979 cave->diamonds_collected++;
981 if (cave->diamonds_needed == cave->diamonds_collected)
983 cave->gate_open = TRUE;
985 // extra is worth more points.
986 cave->diamond_value = cave->extra_diamond_value;
988 cave->gate_open_flash = 1;
989 cave->sound3 = GD_S_CRACKING;
990 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
995 cave->skeletons_collected++;
997 // as if player got a diamond
998 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
999 player_get_element(cave, O_DIAMOND, -1, -1);
1001 // _after_ calling get_element for the fake diamonds, so we overwrite its sounds
1002 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
1006 case O_INVIS_OUTBOX:
1007 cave->player_state = GD_PL_EXITED; // player now exits the cave!
1011 case O_LAVA: // player goes into lava, as if it was space
1012 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
1016 // the object will remain there.
1022 process a crazy dream-style teleporter.
1023 called from gd_cave_iterate, for a player or a player_bomb.
1024 player is standing at px, py, and trying to move in the direction player_move,
1025 where there is a teleporter at (tx_start, ty_start). we check the whole cave,
1026 from (tx_start + 1, ty_start), till we get back to (tx_start, ty_start) (by wrapping
1027 around). the first teleporter we find, and which is suitable, will be the destination.
1028 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
1030 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
1032 // start at teleporter position (not at player position!)
1033 int tx_start = px + gd_dx[player_move];
1034 int ty_start = py + gd_dy[player_move];
1040 // jump to next element; wrap around columns and rows.
1052 // if we found a teleporter...
1053 if (get(cave, tx, ty) == O_TELEPORTER &&
1054 is_space_dir(cave, tx, ty, player_move))
1056 // new player appears near teleporter found
1057 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1059 // current player disappears
1060 store(cave, px, py, O_SPACE);
1062 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1064 return TRUE; // return true as teleporter worked
1067 // loop until we get back to original coordinates
1068 while (tx != tx_start || ty != ty_start);
1070 // return false as we did not find any usable teleporter
1075 try to push an element.
1076 returns true if the push is possible; also does move the specified _element_.
1077 up to the caller to move the _player_itself_.
1079 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1082 GdElement what = get_dir(cave, x, y, player_move);
1084 // gravity for falling wall, bladder, ...
1085 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1091 case O_WAITING_STONE:
1094 case O_CHASING_STONE:
1096 case O_FLYING_STONE:
1098 // pushing some kind of stone or nut
1099 // directions possible: 90degrees cw or ccw to current gravity.
1100 // only push if player dir is orthogonal to gravity,
1101 // ie. gravity down, pushing left & right possible
1102 if (player_move == ccw_fourth[cave->gravity] ||
1103 player_move == cw_fourth[cave->gravity])
1109 // different probabilities for different elements.
1112 case O_WAITING_STONE:
1113 // waiting stones are light, can always push
1117 case O_CHASING_STONE:
1118 // chasing can be pushed if player is turbo
1119 if (cave->sweet_eaten)
1124 // mega may(!) be pushed if player is turbo
1125 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1131 case O_FLYING_STONE:
1133 if (cave->sweet_eaten)
1134 prob = cave->pushing_stone_prob_sweet; // probability with sweet
1136 prob = cave->pushing_stone_prob; // probability without sweet.
1143 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1144 gd_rand_int_range(cave->random, 0, 1000000) < prob)
1146 // if decided that he will be able to push,
1147 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1148 play_sound_of_element_pushing(cave, what, x, y);
1163 // pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1164 // not an O_BLADDER_x.
1165 // there is no "delayed" state of a bladder, so we use store_dir_no_scanned!
1167 // first check: we cannot push a bladder "up"
1168 if (player_move != opposite[grav_compat])
1170 // pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check.
1171 // player moving in the direction of gravity.
1175 if (player_move == grav_compat)
1177 // pushing bladder down
1178 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1179 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1180 // if no space to push down, maybe left (down-left to player)
1181 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1183 // left is "down, turned right (cw)"
1184 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1185 // if not, maybe right (down-right to player)
1186 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1187 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1190 // pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check.
1194 else if (player_move == cw_fourth[grav_compat])
1196 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) // pushing it left
1197 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1198 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) // maybe down, and player will move left
1199 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1200 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) // maybe up, and player will move left
1201 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1204 // pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check.
1208 else if (player_move == ccw_fourth[grav_compat])
1210 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) // pushing it right
1211 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1212 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) // maybe down, and player will move right
1213 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1214 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) // maybe up, and player will move right
1215 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1219 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1224 // a box is only pushed with the fire pressed
1227 // but always with 100% probability
1228 switch (player_move)
1234 // pushing in some dir, two steps in that dir - is there space?
1235 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1238 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1240 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1245 // push in no other directions possible
1251 // pushing of other elements not possible
1259 // from the key press booleans, create a direction
1260 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1262 GdDirection player_move;
1264 // from the key press booleans, create a direction
1266 player_move = GD_MV_UP_RIGHT;
1267 else if (down && right)
1268 player_move = GD_MV_DOWN_RIGHT;
1269 else if (down && left)
1270 player_move = GD_MV_DOWN_LEFT;
1271 else if (up && left)
1272 player_move = GD_MV_UP_LEFT;
1274 player_move = GD_MV_UP;
1276 player_move = GD_MV_DOWN;
1278 player_move = GD_MV_LEFT;
1280 player_move = GD_MV_RIGHT;
1282 player_move = GD_MV_STILL;
1287 // clear these to no sound; and they will be set during iteration.
1288 void gd_cave_clear_sounds(GdCave *cave)
1290 cave->sound1 = GD_S_NONE;
1291 cave->sound2 = GD_S_NONE;
1292 cave->sound3 = GD_S_NONE;
1295 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1296 GdElement falling_element)
1298 if (cave->gravity_disabled)
1301 if (is_space_dir(cave, x, y, falling_direction))
1303 // beginning to fall
1304 play_sound_of_element(cave, get(cave, x, y), x, y);
1305 move(cave, x, y, falling_direction, falling_element);
1308 // check if it is on a sloped element, and it can roll.
1309 // for example, sloped wall looks like:
1312 // this is tagged as sloped up&left.
1313 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1314 // then check the direction to roll (left or right)
1315 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1316 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1318 // rolling down, if sitting on a sloped object
1319 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1320 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1321 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1323 // rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1324 // so here we use cw_fourth
1325 play_sound_of_element(cave, get(cave, x, y), x, y);
1326 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1328 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1329 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1330 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1333 play_sound_of_element(cave, get(cave, x, y), x, y);
1334 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1339 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1341 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1342 cave->voodoo_dies_by_stone)
1344 // this is a 1stB-style vodo. explodes by stone, collects diamonds
1345 explode_dir(cave, x, y, fall_dir);
1352 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1354 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1355 cave->voodoo_collects_diamonds)
1357 // this is a 1stB-style voodoo. explodes by stone, collects diamonds
1358 player_get_element(cave, O_DIAMOND, x, y); // as if player got diamond
1359 store(cave, x, y, O_SPACE); // diamond disappears
1366 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1367 GdDirection fall_dir, GdElement bouncing)
1369 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1370 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1373 store(cave, x, y, bouncing);
1374 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1376 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1384 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1385 GdDirection fall_dir, GdElement magic)
1387 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1389 play_sound_of_element(cave, O_DIAMOND, x, y); // always play diamond sound
1391 if (cave->magic_wall_state == GD_MW_DORMANT)
1392 cave->magic_wall_state = GD_MW_ACTIVE;
1394 if (cave->magic_wall_state == GD_MW_ACTIVE &&
1395 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1397 // if magic wall active and place underneath, it turns element
1398 // into anything the effect says to do.
1399 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1402 // active or non-active or anything, element falling in will always disappear
1403 store(cave, x, y, O_SPACE);
1405 if (cave->magic_wall_breakscan && cave->amoeba_state == GD_AM_AWAKE)
1406 cave->convert_amoeba_this_frame = TRUE;
1414 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1416 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1418 explode_dir(cave, x, y, fall_dir);
1425 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1426 GdDirection fall_dir, GdElement bouncing)
1428 if (is_space_dir(cave, x, y, fall_dir))
1431 move(cave, x, y, fall_dir, get(cave, x, y));
1436 // check if it is on a sloped element, and it can roll.
1437 // for example, sloped wall looks like:
1440 // this is tagged as sloped up&left.
1441 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1442 // then check the direction to roll (left or right)
1443 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1445 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1447 // sloped element, falling to left or right
1448 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1449 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1450 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1452 play_sound_of_element(cave, get(cave, x, y), x, y);
1454 // try to roll left first - see O_STONE to understand why cw_fourth
1455 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1457 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1458 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1459 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1461 play_sound_of_element(cave, get(cave, x, y), x, y);
1463 // if not, try to roll right
1464 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1468 // cannot roll in any direction, so it stops
1469 play_sound_of_element(cave, get(cave, x, y), x, y);
1470 store(cave, x, y, bouncing);
1476 // any other element, stops
1477 play_sound_of_element(cave, get(cave, x, y), x, y);
1478 store(cave, x, y, bouncing);
1482 static void update_cave_speed(GdCave *cave)
1484 // update timing calculated by iterating and counting elements which were slow to process on c64
1485 switch (cave->scheduling)
1487 case GD_SCHEDULING_MILLISECONDS:
1488 // cave->speed already contains the milliseconds value, do not touch it
1491 case GD_SCHEDULING_BD1:
1492 if (!cave->intermission)
1493 // non-intermissions
1494 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1496 // intermissions were quicker, as only lines 1-12 were processed by the engine.
1497 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1500 case GD_SCHEDULING_BD1_ATARI:
1501 // about 20ms/frame faster than c64 version
1502 if (!cave->intermission)
1503 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); // non-intermissions
1505 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); // for intermissions
1508 case GD_SCHEDULING_BD2:
1510 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1513 case GD_SCHEDULING_PLCK:
1514 // 65 is totally empty cave in construction kit, with delay = 0)
1515 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1518 case GD_SCHEDULING_BD2_PLCK_ATARI:
1519 // a really fast engine; timing works like c64 plck.
1520 // 40 ms was measured in the construction kit, with delay = 0
1521 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1524 case GD_SCHEDULING_CRDR:
1525 if (cave->hammered_walls_reappear) // this made the engine very slow.
1526 cave->ckdelay += 60000;
1527 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1530 case GD_SCHEDULING_MAX:
1536 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1543 // amoeba found to be enclosed. if not, this is cleared
1544 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1546 // counting the number of amoebas. after scan, check if too much
1547 int amoeba_count, amoeba_2_count;
1549 // cave scan found water - for sound
1550 boolean found_water;
1552 boolean inbox_toggle;
1553 boolean start_signal;
1555 // gravity for falling wall, bladder, ...
1556 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1558 // directions for o_something_1, 2, 3 and 4 (creatures)
1559 static const GdDirection creature_dir[] =
1566 static const GdDirection creature_chdir[] =
1573 int time_decrement_sec;
1575 // biters eating elements preference, they try to go in this order
1576 GdElement biter_try[] =
1583 boolean amoeba_sound, magic_sound;
1585 gd_cave_clear_sounds(cave);
1587 game_bd.player_moving = FALSE;
1588 game_bd.player_snapping = FALSE;
1590 // if diagonal movements not allowed,
1591 // horizontal movements have precedence. [BROADRIBB]
1592 if (!cave->diagonal_movements)
1594 switch (player_move)
1596 case GD_MV_UP_RIGHT:
1597 case GD_MV_DOWN_RIGHT:
1598 player_move = GD_MV_RIGHT;
1602 case GD_MV_DOWN_LEFT:
1603 player_move = GD_MV_LEFT;
1607 // no correction needed
1612 // set cave get function; to implement perfect or lineshifting borders
1613 if (cave->lineshift)
1615 cave->getp = getp_shift;
1616 cave->getx = getx_shift;
1617 cave->gety = gety_shift;
1621 cave->getp = getp_perfect;
1622 cave->getx = getx_perfect;
1623 cave->gety = gety_perfect;
1626 // increment this. if the scan routine comes across player, clears it (sets to zero).
1627 if (cave->player_seen_ago < 100)
1628 cave->player_seen_ago++;
1630 if (cave->pneumatic_hammer_active_delay > 0)
1631 cave->pneumatic_hammer_active_delay--;
1633 // inboxes and outboxes flash with the rhythm of the game, not the display.
1634 // also, a player can be born only from an open, not from a steel-wall-like inbox.
1635 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1636 inbox_toggle = cave->inbox_flash_toggle;
1638 if (cave->gate_open_flash > 0)
1639 cave->gate_open_flash--;
1641 // score collected this frame
1644 // to implement buggy bd1 amoeba+magic wall behaviour
1645 cave->convert_amoeba_this_frame = FALSE;
1647 // suicide only kills the active player
1648 // player_x, player_y was set by the previous iterate routine, or the cave setup.
1649 // we must check if there is a player or not - he may have exploded or something like that
1650 if (suicide && cave->player_state == GD_PL_LIVING &&
1651 is_player(cave, cave->player_x, cave->player_y))
1652 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1654 // check for walls reappearing
1655 if (cave->hammered_reappear)
1657 for (y = 0; y < cave->h; y++)
1659 for (x = 0; x < cave->w; x++)
1661 // timer for the cell > 0?
1662 if (cave->hammered_reappear[y][x] > 0)
1665 cave->hammered_reappear[y][x]--;
1667 // check if it became zero
1668 if (cave->hammered_reappear[y][x] == 0)
1670 store(cave, x, y, O_BRICK);
1671 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1678 // variables to check during the scan
1680 // will be set to false if any of the amoeba is found free.
1681 amoeba_found_enclosed = TRUE;
1682 amoeba_2_found_enclosed = TRUE;
1685 found_water = FALSE;
1687 time_decrement_sec = 0;
1689 // check whether to scan the first and last line
1690 if (cave->border_scan_first_and_last)
1701 // the cave scan routine
1702 for (y = ymin; y <= ymax; y++)
1704 for (x = 0; x < cave->w; x++)
1706 // if we find a scanned element, change it to the normal one, and that's all.
1707 // this is required, for example for chasing stones, which have moved, always passing slime!
1708 if (get(cave, x, y) & SCANNED)
1710 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1715 // add the ckdelay correction value for every element seen.
1716 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1718 switch (get(cave, x, y))
1720 // ============================================================================
1722 // ============================================================================
1725 if (cave->kill_player)
1727 explode (cave, x, y);
1731 cave->player_seen_ago = 0;
1732 // bd4 intermission caves have many players. so if one of them has exited,
1733 // do not change the flag anymore. so this if () is needed
1734 if (cave->player_state != GD_PL_EXITED)
1735 cave->player_state = GD_PL_LIVING;
1737 // check for pneumatic hammer things
1738 // 1) press fire, 2) have pneumatic hammer 4) space on left or right
1739 // for hammer 5) stand on something
1740 if (player_fire && cave->got_pneumatic_hammer &&
1741 is_space_dir(cave, x, y, player_move) &&
1742 !is_space_dir(cave, x, y, GD_MV_DOWN))
1744 if (player_move == GD_MV_LEFT &&
1745 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1747 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1748 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1749 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1753 if (player_move == GD_MV_RIGHT &&
1754 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1756 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1757 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1758 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1763 if (player_move != GD_MV_STILL)
1765 // only do every check if he is not moving
1766 GdElement what = get_dir(cave, x, y, player_move);
1767 GdElement remains = what;
1770 // if we are 'eating' a teleporter, and the function returns true
1771 // (teleporting worked), break here
1772 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1775 // try to push element; if successful, break
1776 push = do_push(cave, x, y, player_move, player_fire);
1786 // if its a bomb, remember he now has one.
1787 // we do not change the "remains" and "what" variables,
1788 // so that part of the code will be ineffective
1789 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1790 store_dir(cave, x, y, player_move, O_SPACE);
1793 store(cave, x, y, O_PLAYER_BOMB);
1795 move(cave, x, y, player_move, O_PLAYER_BOMB);
1798 case O_ROCKET_LAUNCHER:
1799 // if its a rocket launcher, remember he now has one.
1800 // we do not change the "remains" and "what" variables,
1801 // so that part of the code will be ineffective
1802 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1803 store_dir(cave, x, y, player_move, O_SPACE);
1806 store(cave, x, y, O_PLAYER_ROCKET_LAUNCHER);
1808 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
1812 // we do not change the "remains" and "what" variables,
1813 // so that part of the code will be ineffective
1814 if (!player_fire && !cave->gravity_switch_active &&
1815 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1817 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1818 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1819 cave->gravity_disabled = TRUE;
1823 case O_GRAVITY_SWITCH:
1824 // (we cannot use player_get for this as it does not have player_move parameter)
1825 // only allow changing direction if the new dir is not diagonal
1826 if (cave->gravity_switch_active &&
1827 (player_move == GD_MV_LEFT ||
1828 player_move == GD_MV_RIGHT ||
1829 player_move == GD_MV_UP ||
1830 player_move == GD_MV_DOWN))
1832 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1833 cave->gravity_will_change =
1834 cave->gravity_change_time * cave->timing_factor;
1835 cave->gravity_next_direction = player_move;
1836 cave->gravity_switch_active = FALSE;
1841 // get element - process others.
1842 // if cannot get, player_get_element will return the same
1843 remains = player_get_element(cave, what, x, y);
1848 if (remains != what || remains == O_SPACE)
1850 // if anything changed, apply the change.
1852 // if snapping anything and we have snapping explosions set.
1853 // but these is not true for pushing.
1854 if (remains == O_SPACE && player_fire && !push)
1856 remains = cave->snap_element;
1858 game_bd.player_snapping = TRUE;
1861 if (remains != O_SPACE || player_fire)
1863 // if any other element than space, player cannot move.
1864 // also if pressing fire, will not move.
1865 store_dir(cave, x, y, player_move, remains);
1869 // if space remains there, the player moves.
1870 move(cave, x, y, player_move, O_PLAYER);
1872 game_bd.player_moving = TRUE;
1879 // much simpler; cannot steal stones
1880 if (cave->kill_player)
1882 explode(cave, x, y);
1886 cave->player_seen_ago = 0;
1887 // bd4 intermission caves have many players. so if one of them has exited,
1888 // do not change the flag anymore. so this if () is needed
1889 if (cave->player_state != GD_PL_EXITED)
1890 cave->player_state = GD_PL_LIVING;
1892 if (player_move != GD_MV_STILL)
1894 // if the player does not move, nothing to do
1895 GdElement what = get_dir(cave, x, y, player_move);
1896 GdElement remains = what;
1900 // placing a bomb into empty space or dirt
1901 if (is_space_dir(cave, x, y, player_move) ||
1902 is_element_dir(cave, x, y, player_move, O_DIRT))
1904 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1906 // placed bomb, he is normal player again
1907 store(cave, x, y, O_PLAYER);
1908 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1913 // pushing and collecting
1914 // if we are 'eating' a teleporter, and the function returns true
1915 // (teleporting worked), break here
1916 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1919 // player fire is false...
1920 if (do_push(cave, x, y, player_move, FALSE))
1928 case O_GRAVITY_SWITCH:
1929 // (we cannot use player_get for this as it does not have
1930 // player_move parameter)
1931 // only allow changing direction if the new dir is not diagonal
1932 if (cave->gravity_switch_active &&
1933 (player_move == GD_MV_LEFT ||
1934 player_move == GD_MV_RIGHT ||
1935 player_move == GD_MV_UP ||
1936 player_move == GD_MV_DOWN))
1938 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1939 cave->gravity_will_change =
1940 cave->gravity_change_time * cave->timing_factor;
1941 cave->gravity_next_direction = player_move;
1942 cave->gravity_switch_active = FALSE;
1947 // get element. if cannot get, player_get_element will return the same
1948 remains = player_get_element (cave, what, x, y);
1953 // if element changed, OR there is space, move.
1954 if (remains != what || remains == O_SPACE)
1956 // if anything changed, apply the change.
1957 move(cave, x, y, player_move, O_PLAYER_BOMB);
1962 case O_PLAYER_ROCKET_LAUNCHER:
1963 // much simpler; cannot snap-push stones
1964 if (cave->kill_player)
1966 explode(cave, x, y);
1970 cave->player_seen_ago = 0;
1971 // bd4 intermission caves have many players. so if one of them has exited,
1972 // do not change the flag anymore. so this if () is needed
1973 if (cave->player_state != GD_PL_EXITED)
1974 cave->player_state = GD_PL_LIVING;
1977 if (player_move != GD_MV_STILL)
1979 // if the player does not move, nothing to do
1980 GdElement what = get_dir(cave, x, y, player_move);
1981 GdElement remains = what;
1983 // to fire a rocket, diagonal movement should not be allowed.
1984 // so either x or y must be zero
1987 // placing a rocket into empty space
1988 if (is_space_dir(cave, x, y, player_move))
1990 switch (player_move)
1993 store_dir(cave, x, y, player_move, O_ROCKET_1);
1994 if (!cave->infinite_rockets)
1995 store(cave, x, y, O_PLAYER);
1999 store_dir(cave, x, y, player_move, O_ROCKET_2);
2000 if (!cave->infinite_rockets)
2001 store(cave, x, y, O_PLAYER);
2005 store_dir(cave, x, y, player_move, O_ROCKET_3);
2006 if (!cave->infinite_rockets)
2007 store(cave, x, y, O_PLAYER);
2011 store_dir(cave, x, y, player_move, O_ROCKET_4);
2012 if (!cave->infinite_rockets)
2013 store(cave, x, y, O_PLAYER);
2017 // cannot fire in other directions
2021 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
2024 // a player with rocket launcher cannot snap elements, so stop here
2028 // pushing and collecting
2029 // if we are 'eating' a teleporter, and the function returns true
2030 // (teleporting worked), break here
2031 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
2034 // player fire is false...
2035 if (do_push(cave, x, y, player_move, FALSE))
2041 // get element. if cannot get, player_get_element will return the same
2042 remains = player_get_element(cave, what, x, y);
2045 // if something changed, OR there is space, move.
2046 if (remains != what || remains == O_SPACE)
2048 // if anything changed, apply the change.
2049 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
2054 case O_PLAYER_STIRRING:
2055 if (cave->kill_player)
2057 explode(cave, x, y);
2061 // stirring sound, if no other walking sound or explosion
2062 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
2064 cave->player_seen_ago = 0;
2065 // bd4 intermission caves have many players. so if one of them has exited,
2066 // do not change the flag anymore. so this if () is needed
2067 if (cave->player_state != GD_PL_EXITED)
2068 cave->player_state = GD_PL_LIVING;
2072 // player "exits" stirring the pot by pressing fire
2073 cave->gravity_disabled = FALSE;
2074 store(cave, x, y, O_PLAYER);
2075 cave->gravity_switch_active = TRUE;
2079 // player holding pneumatic hammer
2080 case O_PLAYER_PNEUMATIC_LEFT:
2081 case O_PLAYER_PNEUMATIC_RIGHT:
2082 // usual player stuff
2083 if (cave->kill_player)
2085 explode(cave, x, y);
2089 cave->player_seen_ago = 0;
2090 if (cave->player_state != GD_PL_EXITED)
2091 cave->player_state = GD_PL_LIVING;
2093 // if hammering time is up, becomes a normal player again.
2094 if (cave->pneumatic_hammer_active_delay == 0)
2095 store(cave, x, y, O_PLAYER);
2098 // the active pneumatic hammer itself
2099 case O_PNEUMATIC_ACTIVE_RIGHT:
2100 case O_PNEUMATIC_ACTIVE_LEFT:
2101 if (cave->pneumatic_hammer_active_delay == 0)
2105 // pneumatic hammer element disappears
2106 store(cave, x, y, O_SPACE);
2108 // which is the new element which appears after that one is hammered?
2109 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
2111 // if there is a new element, display it
2112 // O_NONE might be returned, for example if the element being
2113 // hammered explodes during hammering (by a nearby explosion)
2114 if (new_elem != O_NONE)
2116 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
2118 // and if walls reappear, remember it in array
2119 if (cave->hammered_walls_reappear)
2123 wall_y = (y + 1) % cave->h;
2124 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
2130 // ============================================================================
2131 // S T O N E S, D I A M O N D S
2132 // ============================================================================
2134 case O_STONE: // standing stone
2135 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
2138 case O_MEGA_STONE: // standing mega_stone
2139 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
2142 case O_DIAMOND: // standing diamond
2143 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
2146 case O_NUT: // standing nut
2147 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
2150 case O_DIRT_BALL: // standing dirt ball
2151 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
2154 case O_DIRT_LOOSE: // standing loose dirt
2155 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
2158 case O_FLYING_STONE: // standing stone
2159 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
2162 case O_FLYING_DIAMOND: // standing diamond
2163 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2166 // ============================================================================
2167 // 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
2168 // ============================================================================
2170 case O_DIRT_BALL_F: // falling dirt ball
2171 if (!cave->gravity_disabled)
2172 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2175 case O_DIRT_LOOSE_F: // falling loose dirt
2176 if (!cave->gravity_disabled)
2177 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2180 case O_STONE_F: // falling stone
2181 if (!cave->gravity_disabled)
2183 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2186 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2189 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2192 if (do_fall_try_crush(cave, x, y, cave->gravity))
2195 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2199 case O_MEGA_STONE_F: // falling mega
2200 if (!cave->gravity_disabled)
2202 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2205 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2208 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2211 if (do_fall_try_crush(cave, x, y, cave->gravity))
2214 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2218 case O_DIAMOND_F: // falling diamond
2219 if (!cave->gravity_disabled)
2221 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2224 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2227 if (do_fall_try_crush(cave, x, y, cave->gravity))
2230 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2234 case O_NUT_F: // falling nut
2235 if (!cave->gravity_disabled)
2237 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2240 if (do_fall_try_crush(cave, x, y, cave->gravity))
2243 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2247 case O_FLYING_STONE_F: // falling stone
2248 if (!cave->gravity_disabled)
2250 GdDirection fall_dir = opposite[cave->gravity];
2252 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2255 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2258 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2261 if (do_fall_try_crush(cave, x, y, fall_dir))
2264 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2268 case O_FLYING_DIAMOND_F: // falling diamond
2269 if (!cave->gravity_disabled)
2271 GdDirection fall_dir = opposite[cave->gravity];
2273 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2276 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2279 if (do_fall_try_crush(cave, x, y, fall_dir))
2282 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2286 // ============================================================================
2287 // N I T R O P A C K
2288 // ============================================================================
2290 case O_NITRO_PACK: // standing nitro pack
2291 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2294 case O_NITRO_PACK_F: // falling nitro pack
2295 if (!cave->gravity_disabled)
2297 if (is_space_dir(cave, x, y, cave->gravity)) // if space, falling further
2298 move(cave, x, y, cave->gravity, get(cave, x, y));
2299 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2301 // try magic wall; if true, function did the work
2303 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2305 // falling on a dirt, it does NOT explode - just stops at its place.
2306 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2307 store(cave, x, y, O_NITRO_PACK);
2310 // falling on any other element it explodes
2311 explode(cave, x, y);
2315 case O_NITRO_PACK_EXPLODE: // a triggered nitro pack
2316 explode(cave, x, y);
2319 // ============================================================================
2320 // C R E A T U R E S
2321 // ============================================================================
2327 // if cannot move in any direction, becomes an enclosed cow
2328 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2329 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2330 store(cave, x, y, O_COW_ENCLOSED_1);
2333 // THIS IS THE CREATURE MOVE thing copied.
2334 const GdDirection *creature_move;
2335 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2336 GdElement base; // base element number (which is like O_***_1)
2337 int dir, dirn, dirp; // direction
2341 dir = get(cave, x, y)-base; // facing where
2342 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2344 // now change direction if backwards
2345 if (cave->creatures_backwards)
2350 dirn = (dir + 3) & 3; // fast turn
2351 dirp = (dir + 1) & 3; // slow turn
2355 dirn = (dir + 1) & 3; // fast turn
2356 dirp = (dir + 3) & 3; // slow turn
2359 if (is_space_dir(cave, x, y, creature_move[dirn]))
2360 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2361 else if (is_space_dir(cave, x, y, creature_move[dir]))
2362 move(cave, x, y, creature_move[dir], base + dir); // go on
2364 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2368 // enclosed cows wait some time before turning to a skeleton
2369 case O_COW_ENCLOSED_1:
2370 case O_COW_ENCLOSED_2:
2371 case O_COW_ENCLOSED_3:
2372 case O_COW_ENCLOSED_4:
2373 case O_COW_ENCLOSED_5:
2374 case O_COW_ENCLOSED_6:
2375 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2376 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2377 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2378 is_space_dir(cave, x, y, GD_MV_DOWN))
2379 store(cave, x, y, O_COW_1);
2384 case O_COW_ENCLOSED_7:
2385 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2386 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2387 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2388 is_space_dir(cave, x, y, GD_MV_DOWN))
2389 store(cave, x, y, O_COW_1);
2391 store(cave, x, y, O_SKELETON);
2398 case O_ALT_FIREFLY_1:
2399 case O_ALT_FIREFLY_2:
2400 case O_ALT_FIREFLY_3:
2401 case O_ALT_FIREFLY_4:
2406 case O_ALT_BUTTER_1:
2407 case O_ALT_BUTTER_2:
2408 case O_ALT_BUTTER_3:
2409 case O_ALT_BUTTER_4:
2414 // check if touches a voodoo
2415 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2416 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2417 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2418 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2419 cave->voodoo_touched = TRUE;
2421 // check if touches something bad and should explode (includes voodoo by the flags)
2422 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2423 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2424 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2425 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2426 explode (cave, x, y);
2430 const GdDirection *creature_move;
2431 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2432 GdElement base = -1; // base element number (which is like O_***_1)
2433 int dir, dirn, dirp; // direction
2435 if (get(cave, x, y) >= O_FIREFLY_1 &&
2436 get(cave, x, y) <= O_FIREFLY_4)
2438 else if (get(cave, x, y) >= O_BUTTER_1 &&
2439 get(cave, x, y) <= O_BUTTER_4)
2441 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2442 get(cave, x, y) <= O_STONEFLY_4)
2443 base = O_STONEFLY_1;
2444 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2445 get(cave, x, y) <= O_ALT_FIREFLY_4)
2446 base = O_ALT_FIREFLY_1;
2447 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2448 get(cave, x, y) <= O_ALT_BUTTER_4)
2449 base = O_ALT_BUTTER_1;
2451 dir = get(cave, x, y) - base; // facing where
2452 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2454 // now change direction if backwards
2455 if (cave->creatures_backwards)
2460 dirn = (dir + 3) & 3; // fast turn
2461 dirp = (dir + 1) & 3; // slow turn
2465 dirn = (dir + 1) & 3; // fast turn
2466 dirp = (dir + 3) & 3; // slow turn
2469 if (is_space_dir(cave, x, y, creature_move[dirn]))
2470 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2471 else if (is_space_dir(cave, x, y, creature_move[dir]))
2472 move(cave, x, y, creature_move[dir], base + dir); // go on
2474 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2478 case O_WAITING_STONE:
2479 if (is_space_dir(cave, x, y, grav_compat))
2481 // beginning to fall
2483 move(cave, x, y, grav_compat, O_CHASING_STONE);
2485 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2487 // rolling down a brick wall or a stone
2488 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2489 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2490 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2492 // maybe rolling left - see case O_STONE to understand why we use cw_fourth here
2493 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2495 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2496 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2497 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2500 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2505 case O_CHASING_STONE:
2507 int px = cave->px[0];
2508 int py = cave->py[0];
2509 boolean horizontal = gd_rand_boolean(cave->random);
2510 boolean dont_move = FALSE;
2518 // ------------------------------------------------------------
2519 // check for a horizontal movement
2520 // ------------------------------------------------------------
2523 // if coordinates are the same
2525 horizontal = !horizontal;
2532 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2534 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2538 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2540 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2549 horizontal = !horizontal;
2557 // ------------------------------------------------------------
2558 // check for a vertical movement
2559 // ------------------------------------------------------------
2562 // if coordinates are the same
2564 horizontal = !horizontal;
2570 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2572 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2576 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2578 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2587 horizontal = !horizontal;
2600 // if we should move in both directions, but can not move in any, stop.
2605 // check for horizontal
2608 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2609 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2610 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2611 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2612 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2613 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2617 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2618 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2619 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2620 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2621 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2622 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2627 // check for vertical
2630 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2631 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2632 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2633 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2634 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2635 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2639 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2640 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2641 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2642 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2643 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2644 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2652 if (cave->replicators_wait_frame == 0 &&
2653 cave->replicators_active &&
2654 !cave->gravity_disabled)
2656 // only replicate, if space is under it.
2657 // do not replicate players!
2658 // also obeys gravity settings.
2659 // only replicate element if it is not a scanned one
2660 // do not replicate space... that condition looks like it
2661 // makes no sense, but otherwise it generates SCANNED spaces,
2662 // which cannot be "collected" by the player, so he cannot run
2663 // under a replicator
2664 if (is_space_dir(cave, x, y, cave->gravity) &&
2665 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2666 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2668 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2669 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2678 if (cave->biters_wait_frame == 0)
2680 static GdDirection biter_move[] =
2688 // direction, last two bits 0..3
2689 int dir = get(cave, x, y) - O_BITER_1;
2690 int dirn = (dir + 3) & 3;
2691 int dirp = (dir + 1) & 3;
2693 GdElement made_sound_of = O_NONE;
2695 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2697 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2699 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2700 if (biter_try[i] != O_SPACE)
2701 made_sound_of = O_BITER_1; // sound of a biter eating
2704 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2706 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2707 if (biter_try[i] != O_SPACE)
2708 made_sound_of = O_BITER_1; // sound of a biter eating
2711 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2713 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2714 if (biter_try[i] != O_SPACE)
2715 made_sound_of = O_BITER_1; // sound of a biter eating
2720 if (i == ARRAY_SIZE(biter_try))
2721 // i = number of elements in array: could not move, so just turn
2722 store(cave, x, y, O_BITER_1 + dirp);
2723 else if (biter_try[i] == O_STONE)
2725 // if there was a stone there, where we moved...
2726 // do not eat stones, just throw them back
2727 store(cave, x, y, O_STONE);
2728 made_sound_of = O_STONE;
2731 // if biter did move, we had sound. play it.
2732 if (made_sound_of != O_NONE)
2733 play_sound_of_element(cave, made_sound_of, x, y);
2741 // check if touches a voodoo
2742 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2743 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2744 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2745 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2746 cave->voodoo_touched = TRUE;
2748 // check if touches something bad and should explode (includes voodoo by the flags)
2749 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2750 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2751 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2752 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2753 explode (cave, x, y);
2757 const GdDirection *creature_move;
2758 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2759 GdElement base = O_DRAGONFLY_1; // base element number (which is like O_***_1)
2760 int dir, dirn; // direction
2762 dir = get(cave, x, y)-base; // facing where
2763 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2765 // now change direction if backwards
2766 if (cave->creatures_backwards)
2770 dirn = (dir + 3) & 3; // fast turn
2772 dirn = (dir + 1) & 3; // fast turn
2774 // if can move forward, does so.
2775 if (is_space_dir(cave, x, y, creature_move[dir]))
2776 move(cave, x, y, creature_move[dir], base + dir);
2778 // otherwise turns 90 degrees in place.
2779 store(cave, x, y, base + dirn);
2784 store(cave, x, y, O_BLADDER_1);
2795 // bladder with any delay state: try to convert to clock.
2796 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2797 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))
2799 // if touches the specified element, let it be a clock
2800 store(cave, x, y, O_PRE_CLOCK_1);
2802 // plays the bladder convert sound
2803 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2807 // is space over the bladder?
2808 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2810 if (get(cave, x, y) == O_BLADDER_8)
2812 // if it is a bladder 8, really move up
2813 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2814 play_sound_of_element(cave, O_BLADDER, x, y);
2817 // if smaller delay, just increase delay.
2821 // if not space, is something sloped over the bladder?
2822 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2823 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2825 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2826 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2827 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2829 // rolling up, to left
2830 if (get(cave, x, y) == O_BLADDER_8)
2832 // if it is a bladder 8, really roll
2833 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2834 play_sound_of_element(cave, O_BLADDER, x, y);
2837 // if smaller delay, just increase delay.
2840 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2841 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2842 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2844 // rolling up, to left
2845 if (get(cave, x, y) == O_BLADDER_8)
2847 // if it is a bladder 8, really roll
2848 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2849 play_sound_of_element(cave, O_BLADDER, x, y);
2852 // if smaller delay, just increase delay.
2857 // no space, no sloped thing over it - store bladder 1 and that is for now.
2859 store(cave, x, y, O_BLADDER_1);
2864 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2865 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2866 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2867 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2868 explode (cave, x, y);
2873 // the ghost is given four possibilities to move.
2874 for (i = 0; i < 4; i++)
2876 static GdDirection dirs[] =
2883 GdDirection random_dir;
2885 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2886 if (is_space_dir(cave, x, y, random_dir))
2888 move(cave, x, y, random_dir, O_GHOST);
2889 break; // ghost did move -> exit loop
2895 // ============================================================================
2896 // A C T I V E E L E M E N T S
2897 // ============================================================================
2900 // emulating BD1 amoeba+magic wall bug
2901 if (cave->convert_amoeba_this_frame && amoeba_found_enclosed)
2903 store(cave, x, y, cave->amoeba_enclosed_effect);
2908 switch (cave->amoeba_state)
2911 store(cave, x, y, cave->amoeba_too_big_effect);
2914 case GD_AM_ENCLOSED:
2915 store(cave, x, y, cave->amoeba_enclosed_effect);
2918 case GD_AM_SLEEPING:
2920 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2921 if (amoeba_found_enclosed)
2922 // if still found enclosed, check all four directions, if this one is able to grow.
2923 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2924 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2925 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2926 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2928 // not enclosed. this is a local (per scan) flag!
2929 amoeba_found_enclosed = FALSE;
2930 cave->amoeba_state = GD_AM_AWAKE;
2933 // if alive, check in which dir to grow (or not)
2934 if (cave->amoeba_state == GD_AM_AWAKE)
2936 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2938 switch (gd_rand_int_range(cave->random, 0, 4))
2940 // decided to grow, choose a random direction.
2941 case 0: // let this be up. numbers indifferent.
2942 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2943 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2947 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2948 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2952 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2953 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2957 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2958 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2970 // check if it is touching an amoeba, and explosion is enabled
2971 if (cave->amoeba_2_explodes_by_amoeba &&
2972 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2973 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2974 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2975 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2976 explode (cave, x, y);
2978 switch (cave->amoeba_2_state)
2981 store(cave, x, y, cave->amoeba_2_too_big_effect);
2984 case GD_AM_ENCLOSED:
2985 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2988 case GD_AM_SLEEPING:
2990 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2991 if (amoeba_2_found_enclosed)
2992 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2993 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2994 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2995 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2997 // not enclosed. this is a local (per scan) flag!
2998 amoeba_2_found_enclosed = FALSE;
2999 cave->amoeba_2_state = GD_AM_AWAKE;
3002 // if it is alive, decide if it attempts to grow
3003 if (cave->amoeba_2_state == GD_AM_AWAKE)
3004 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
3006 switch (gd_rand_int_range(cave->random, 0, 4))
3008 // decided to grow, choose a random direction.
3009 case 0: // let this be up. numbers indifferent.
3010 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
3011 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
3015 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
3016 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
3020 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
3021 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
3025 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
3026 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
3036 // choose randomly, if it spreads
3037 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
3039 // the current one explodes
3040 store(cave, x, y, cave->acid_turns_to);
3042 // and if neighbours are eaten, put acid there.
3043 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
3045 play_sound_of_element(cave, O_ACID, x, y);
3046 store_dir(cave, x, y, GD_MV_UP, O_ACID);
3049 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
3051 play_sound_of_element(cave, O_ACID, x, y);
3052 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
3055 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
3057 play_sound_of_element(cave, O_ACID, x, y);
3058 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
3061 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
3063 play_sound_of_element(cave, O_ACID, x, y);
3064 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
3071 if (!cave->water_does_not_flow_down &&
3072 is_space_dir(cave, x, y, GD_MV_DOWN))
3073 // emulating the odd behaviour in crdr
3074 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
3076 if (is_space_dir(cave, x, y, GD_MV_UP))
3077 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
3079 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3080 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
3082 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3083 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
3087 store(cave, x, y, O_WATER);
3090 case O_H_EXPANDING_WALL:
3091 case O_V_EXPANDING_WALL:
3092 case O_H_EXPANDING_STEEL_WALL:
3093 case O_V_EXPANDING_STEEL_WALL:
3094 // checks first if direction is changed.
3095 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
3096 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
3097 !cave->expanding_wall_changed) ||
3098 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
3099 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
3100 cave->expanding_wall_changed))
3102 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3104 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3105 play_sound_of_element(cave, get(cave, x, y), x, y);
3108 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3109 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3110 play_sound_of_element(cave, get(cave, x, y), x, y);
3115 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3116 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3117 play_sound_of_element(cave, get(cave, x, y), x, y);
3120 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3121 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3122 play_sound_of_element(cave, get(cave, x, y), x, y);
3127 case O_EXPANDING_WALL:
3128 case O_EXPANDING_STEEL_WALL:
3129 // the wall which grows in all four directions.
3130 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3132 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3133 play_sound_of_element(cave, get(cave, x, y), x, y);
3136 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3137 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3138 play_sound_of_element(cave, get(cave, x, y), x, y);
3141 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3142 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3143 play_sound_of_element(cave, get(cave, x, y), x, y);
3146 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3147 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3148 play_sound_of_element(cave, get(cave, x, y), x, y);
3154 ; // to make compilers happy ...
3156 Info("Step[%03d]", cave->frame); // XXX
3158 int rrr = gd_cave_c64_random(cave);
3161 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
3162 (rrr & cave->slime_permeability_c64) == 0);
3165 * unpredictable: gd_rand_int
3166 * predictable: c64 predictable random generator.
3167 * for predictable, a random number is generated,
3168 * whether or not it is even possible that the stone will be able to pass.
3170 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
3172 GdDirection grav = cave->gravity;
3173 GdDirection oppos = opposite[cave->gravity];
3175 // space under the slime? elements may pass from top to bottom then.
3176 if (is_space_dir(cave, x, y, grav))
3178 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3180 // output a falling xy under
3181 store_dir(cave, x, y, grav, cave->slime_converts_1);
3183 store_dir(cave, x, y, oppos, O_SPACE);
3184 play_sound_of_element(cave, O_SLIME, x, y);
3186 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3188 store_dir(cave, x, y, grav, cave->slime_converts_2);
3189 store_dir(cave, x, y, oppos, O_SPACE);
3190 play_sound_of_element(cave, O_SLIME, x, y);
3192 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3194 store_dir(cave, x, y, grav, cave->slime_converts_3);
3195 store_dir(cave, x, y, oppos, O_SPACE);
3196 play_sound_of_element(cave, O_SLIME, x, y);
3198 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3200 // waiting stones pass without awakening
3201 store_dir(cave, x, y, grav, O_WAITING_STONE);
3202 store_dir(cave, x, y, oppos, O_SPACE);
3203 play_sound_of_element(cave, O_SLIME, x, y);
3205 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3207 // chasing stones pass
3208 store_dir(cave, x, y, grav, O_CHASING_STONE);
3209 store_dir(cave, x, y, oppos, O_SPACE);
3210 play_sound_of_element(cave, O_SLIME, x, y);
3215 // or space over the slime? elements may pass from bottom to up then.
3216 if (is_space_dir(cave, x, y, oppos))
3218 if (get_dir(cave, x, y, grav) == O_BLADDER)
3220 // bladders move UP the slime
3221 store_dir(cave, x, y, grav, O_SPACE);
3222 store_dir(cave, x, y, oppos, O_BLADDER_1);
3223 play_sound_of_element(cave, O_SLIME, x, y);
3225 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3227 store_dir(cave, x, y, grav, O_SPACE);
3228 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3229 play_sound_of_element(cave, O_SLIME, x, y);
3231 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3233 store_dir(cave, x, y, grav, O_SPACE);
3234 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3235 play_sound_of_element(cave, O_SLIME, x, y);
3242 case O_FALLING_WALL:
3243 if (is_space_dir(cave, x, y, grav_compat))
3245 // try falling if space under.
3248 for (yy = y + 1; yy < y + cave->h; yy++)
3249 // yy < y + cave->h is to check everything OVER the wall - since caves wrap around !!
3250 if (get(cave, x, yy) != O_SPACE)
3251 // stop cycle when other than space
3254 // if scanning stopped by a player... start falling!
3255 if (get(cave, x, yy) == O_PLAYER ||
3256 get(cave, x, yy) == O_PLAYER_GLUED ||
3257 get(cave, x, yy) == O_PLAYER_BOMB)
3259 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3260 // no sound when the falling wall starts falling!
3265 case O_FALLING_WALL_F:
3266 switch (get_dir(cave, x, y, grav_compat))
3269 case O_PLAYER_GLUED:
3271 // if player under, it explodes - the falling wall, not the player!
3272 explode(cave, x, y);
3277 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3282 play_sound_of_element(cave, get(cave, x, y), x, y);
3283 store(cave, x, y, O_FALLING_WALL);
3288 // ============================================================================
3289 // C O N V E Y O R B E L T S
3290 // ============================================================================
3292 case O_CONVEYOR_RIGHT:
3293 case O_CONVEYOR_LEFT:
3294 // only works if gravity is up or down!!!
3295 // first, check for gravity and running belts.
3296 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3298 const GdDirection *dir;
3302 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3303 if (cave->conveyor_belts_direction_changed)
3305 dir = left ? ccw_eighth : cw_eighth;
3307 // CHECK IF IT CONVEYS THE ELEMENT ABOVE IT
3308 // if gravity is normal, and the conveyor belt has something
3309 // ABOVE which can be moved
3311 // the gravity is up, so anything that should float now goes
3312 // DOWN and touches the conveyor
3313 if ((cave->gravity == GD_MV_DOWN &&
3314 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3315 (cave->gravity == GD_MV_UP &&
3316 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3318 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3319 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3321 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move
3322 store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space.
3326 // CHECK IF IT CONVEYS THE ELEMENT BELOW IT
3327 if ((cave->gravity == GD_MV_UP &&
3328 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3329 (cave->gravity == GD_MV_DOWN &&
3330 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3332 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3333 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3335 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move
3336 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear.
3342 // ============================================================================
3344 // ============================================================================
3347 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3348 move(cave, x, y, GD_MV_RIGHT, O_ROCKET_1);
3350 explode(cave, x, y);
3354 if (is_space_dir(cave, x, y, GD_MV_UP))
3355 move(cave, x, y, GD_MV_UP, O_ROCKET_2);
3357 explode(cave, x, y);
3361 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3362 move(cave, x, y, GD_MV_LEFT, O_ROCKET_3);
3364 explode(cave, x, y);
3368 if (is_space_dir(cave, x, y, GD_MV_DOWN))
3369 move(cave, x, y, GD_MV_DOWN, O_ROCKET_4);
3371 explode(cave, x, y);
3374 // ============================================================================
3375 // S I M P L E C H A N G I N G; E X P L O S I O N S
3376 // ============================================================================
3379 store(cave, x, y, cave->explosion_effect);
3383 store(cave, x, y, O_DIAMOND);
3387 store(cave, x, y, cave->diamond_birth_effect);
3391 store(cave, x, y, O_STONE);
3394 case O_NITRO_EXPL_4:
3395 store(cave, x, y, cave->nitro_explosion_effect);
3399 store(cave, x, y, cave->bomb_explosion_effect);
3402 case O_AMOEBA_2_EXPL_4:
3403 store(cave, x, y, cave->amoeba_2_explosion_effect);
3406 case O_GHOST_EXPL_4:
3408 static GdElement ghost_explode[] =
3410 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3411 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3412 O_WAITING_STONE, O_BITER_1
3415 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3420 store(cave, x, y, O_STEEL);
3424 store(cave, x, y, O_CLOCK);
3428 explode(cave, x, y);
3431 case O_TRAPPED_DIAMOND:
3432 if (cave->diamond_key_collected)
3433 store(cave, x, y, O_DIAMOND);
3437 if (cave->gate_open) // if no more diamonds needed
3438 store(cave, x, y, O_OUTBOX); // open outbox
3441 case O_PRE_INVIS_OUTBOX:
3442 if (cave->gate_open) // if no more diamonds needed
3443 store(cave, x, y, O_INVIS_OUTBOX); // open outbox. invisible one :P
3447 if (cave->hatched && !inbox_toggle) // if it is time of birth
3448 store(cave, x, y, O_PRE_PL_1);
3449 inbox_toggle = !inbox_toggle;
3453 store(cave, x, y, O_PLAYER);
3478 case O_GHOST_EXPL_1:
3479 case O_GHOST_EXPL_2:
3480 case O_GHOST_EXPL_3:
3490 case O_NITRO_EXPL_1:
3491 case O_NITRO_EXPL_2:
3492 case O_NITRO_EXPL_3:
3493 case O_AMOEBA_2_EXPL_1:
3494 case O_AMOEBA_2_EXPL_2:
3495 case O_AMOEBA_2_EXPL_3:
3496 // simply the next identifier
3515 found_water = TRUE; // for sound
3516 // simply the next identifier
3520 case O_BLADDER_SPENDER:
3521 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3523 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3524 store(cave, x, y, O_PRE_STEEL_1);
3525 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3530 // other inanimate elements that do nothing
3537 // ============================================================================
3539 // ============================================================================
3541 // another scan-like routine:
3542 // short explosions (for example, in bd1) started with explode_2.
3543 // internally we use explode_1; and change it to explode_2 if needed.
3544 if (cave->short_explosions)
3546 for (y = 0; y < cave->h; y++)
3548 for (x = 0; x < cave->w; x++)
3550 if (is_first_stage_of_explosion(cave, x, y))
3552 next(cave, x, y); // select next frame of explosion
3554 // forget scanned flag immediately
3555 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3561 // finally: forget "scanned" flags for objects.
3562 // also, check for time penalties.
3563 // these is something like an effect table, but we do not really use one.
3564 for (y = 0; y < cave->h; y++)
3566 for (x = 0; x < cave->w; x++)
3568 if (get(cave, x, y) & SCANNED)
3569 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3571 if (get(cave, x, y) == O_TIME_PENALTY)
3573 store(cave, x, y, O_GRAVESTONE);
3575 // there is time penalty for destroying the voodoo
3576 time_decrement_sec += cave->time_penalty;
3581 // this loop finds the coordinates of the player. needed for scrolling and chasing stone.
3582 // but we only do this, if a living player was found. if not yet, the setup
3583 // routine coordinates are used
3584 if (cave->player_state == GD_PL_LIVING)
3586 if (cave->active_is_first_found)
3588 // to be 1stb compatible, we do everything backwards.
3589 for (y = cave->h - 1; y >= 0; y--)
3591 for (x = cave->w - 1; x >= 0; x--)
3593 if (is_player(cave, x, y))
3595 // here we remember the coordinates.
3604 // as in the original: look for the last one
3605 for (y = 0; y < cave->h; y++)
3607 for (x = 0; x < cave->w; x++)
3609 if (is_player(cave, x, y))
3611 // here we remember the coordinates.
3620 // record coordinates of player for chasing stone
3621 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3623 cave->px[i] = cave->px[i + 1];
3624 cave->py[i] = cave->py[i + 1];
3627 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3628 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3632 // update timing calculated by iterating and counting elements
3633 update_cave_speed(cave);
3635 // cave 3 sounds. precedence is controlled by the sound_play function.
3636 // but we have to check amoeba&magic together as they had a different gritty sound when mixed
3638 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3640 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3641 cave->magic_wall_sound);
3643 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3644 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3645 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3647 if (amoeba_sound && magic_sound)
3649 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3654 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3655 else if (magic_sound)
3656 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3661 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3662 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3663 play_sound_of_element(cave, O_AMOEBA, -1, -1);
3666 // pneumatic hammer sound - overrides everything.
3667 if (cave->pneumatic_hammer_active_delay > 0)
3668 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3671 // ============================================================================
3673 // ============================================================================
3677 // check if player is alive.
3678 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
3679 cave->player_state = GD_PL_DIED;
3681 // check if any voodoo exploded, and kill players the next scan if that happended.
3682 if (cave->voodoo_touched)
3683 cave->kill_player = TRUE;
3687 // check flags after evaluating.
3688 if (cave->amoeba_state == GD_AM_AWAKE)
3690 if (amoeba_count >= cave->amoeba_max_count)
3691 cave->amoeba_state = GD_AM_TOO_BIG;
3692 if (amoeba_found_enclosed)
3693 cave->amoeba_state = GD_AM_ENCLOSED;
3696 // amoeba can also be turned into diamond by magic wall
3697 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3698 cave->amoeba_state = GD_AM_ENCLOSED;
3701 if (cave->amoeba_2_state == GD_AM_AWAKE)
3703 // check flags after evaluating.
3704 if (amoeba_2_count >= cave->amoeba_2_max_count)
3705 cave->amoeba_2_state = GD_AM_TOO_BIG;
3707 if (amoeba_2_found_enclosed)
3708 cave->amoeba_2_state = GD_AM_ENCLOSED;
3711 // amoeba 2 can also be turned into diamond by magic wall
3712 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3713 cave->amoeba_2_state = GD_AM_ENCLOSED;
3715 // now check times. ---------------------------
3716 // decrement time if a voodoo was killed.
3717 cave->time -= time_decrement_sec * cave->timing_factor;
3721 // only decrement time when player is already born.
3724 int secondsbefore, secondsafter;
3726 secondsbefore = cave->time / cave->timing_factor;
3727 cave->time -= cave->speed;
3728 if (cave->time <= 0)
3731 secondsafter = cave->time / cave->timing_factor;
3732 if (cave->time / cave->timing_factor < 10)
3733 // if less than 10 seconds, no walking sound, but play explosion sound
3734 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3736 if (secondsbefore != secondsafter)
3737 gd_cave_set_seconds_sound(cave);
3740 // a gravity switch was activated; seconds counting down
3741 if (cave->gravity_will_change > 0)
3743 cave->gravity_will_change -= cave->speed;
3744 if (cave->gravity_will_change < 0)
3745 cave->gravity_will_change = 0;
3747 if (cave->gravity_will_change == 0)
3749 cave->gravity = cave->gravity_next_direction;
3750 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound
3754 // creatures direction automatically change
3755 if (cave->creatures_direction_will_change > 0)
3757 cave->creatures_direction_will_change -= cave->speed;
3758 if (cave->creatures_direction_will_change < 0)
3759 cave->creatures_direction_will_change = 0;
3761 if (cave->creatures_direction_will_change == 0)
3763 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3765 cave->creatures_backwards = !cave->creatures_backwards;
3766 cave->creatures_direction_will_change =
3767 cave->creatures_direction_auto_change_time * cave->timing_factor;
3771 // magic wall; if active&wait or not wait for hatching
3772 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3773 (cave->hatched || !cave->magic_timer_wait_for_hatching) &&
3774 !(cave->magic_wall_time == 0 && cave->magic_timer_zero_is_infinite))
3776 cave->magic_wall_time -= cave->speed;
3777 if (cave->magic_wall_time < 0)
3778 cave->magic_wall_time = 0;
3779 if (cave->magic_wall_time == 0)
3780 cave->magic_wall_state = GD_MW_EXPIRED;
3783 // we may wait for hatching, when starting amoeba
3784 if (cave->amoeba_timer_started_immediately ||
3785 (cave->amoeba_state == GD_AM_AWAKE &&
3786 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3788 cave->amoeba_time -= cave->speed;
3789 if (cave->amoeba_time < 0)
3790 cave->amoeba_time = 0;
3791 if (cave->amoeba_time == 0)
3792 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3795 // we may wait for hatching, when starting amoeba
3796 if (cave->amoeba_timer_started_immediately ||
3797 (cave->amoeba_2_state == GD_AM_AWAKE &&
3798 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3800 cave->amoeba_2_time -= cave->speed;
3801 if (cave->amoeba_2_time < 0)
3802 cave->amoeba_2_time = 0;
3803 if (cave->amoeba_2_time == 0)
3804 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3807 // check for player hatching.
3808 start_signal = FALSE;
3810 // if not the c64 scheduling, but the correct frametime is used,
3811 // hatching delay should always be decremented.
3812 // otherwise, the if (millisecs...) condition below will set this.
3813 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3815 // NON-C64 scheduling
3816 if (cave->hatching_delay_frame > 0)
3818 // for milliseconds-based, non-c64 schedulings, hatching delay means frames.
3819 cave->hatching_delay_frame--;
3820 if (cave->hatching_delay_frame == 0)
3821 start_signal = TRUE;
3827 if (cave->hatching_delay_time > 0)
3829 // for c64 schedulings, hatching delay means milliseconds.
3830 cave->hatching_delay_time -= cave->speed;
3831 if (cave->hatching_delay_time <= 0)
3833 cave->hatching_delay_time = 0;
3834 start_signal = TRUE;
3839 // if decremented hatching, and it became zero:
3842 // THIS IS THE CAVE START SIGNAL
3844 // record that now the cave is in its normal state
3845 cave->hatched = TRUE;
3847 // if diamonds needed is below zero, we count the available diamonds now.
3848 gd_cave_count_diamonds(cave);
3850 // setup direction auto change
3851 if (cave->creatures_direction_auto_change_time)
3853 cave->creatures_direction_will_change =
3854 cave->creatures_direction_auto_change_time * cave->timing_factor;
3856 if (cave->creatures_direction_auto_change_on_start)
3857 cave->creatures_backwards = !cave->creatures_backwards;
3860 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3864 if (cave->biters_wait_frame == 0)
3865 cave->biters_wait_frame = cave->biter_delay_frame;
3867 cave->biters_wait_frame--;
3869 // replicators delay
3870 if (cave->replicators_wait_frame == 0)
3871 cave->replicators_wait_frame = cave->replicator_delay_frame;
3873 cave->replicators_wait_frame--;
3876 // ============================================================================
3878 // ============================================================================
3881 // check if cave failed by timeout is done in main game engine
3883 // check if cave failed by timeout
3884 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3886 gd_cave_clear_sounds(cave);
3887 cave->player_state = GD_PL_TIMEOUT;
3888 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3892 // set these for drawing.
3893 cave->last_direction = player_move;
3895 // here we remember last movements for animation. this is needed here,
3896 // as animation is in sync with the game, not the keyboard directly.
3897 // (for example, after exiting the cave, the player was "running" in the
3898 // original, till bonus points were counted for remaining time and so on.
3899 if (player_move == GD_MV_LEFT ||
3900 player_move == GD_MV_UP_LEFT ||
3901 player_move == GD_MV_DOWN_LEFT)
3902 cave->last_horizontal_direction = GD_MV_LEFT;
3904 if (player_move == GD_MV_RIGHT ||
3905 player_move == GD_MV_UP_RIGHT ||
3906 player_move == GD_MV_DOWN_RIGHT)
3907 cave->last_horizontal_direction = GD_MV_RIGHT;
3909 cave->frame++; // XXX
3912 void set_initial_cave_speed(GdCave *cave)
3917 // set cave get function; to implement perfect or lineshifting borders
3918 if (cave->lineshift)
3919 cave->getp = getp_shift;
3921 cave->getp = getp_perfect;
3923 // check whether to scan the first and last line
3924 if (cave->border_scan_first_and_last)
3935 for (y = ymin; y <= ymax; y++)
3937 for (x = 0; x < cave->w; x++)
3939 // add the ckdelay correction value for every element seen.
3940 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3944 // update timing calculated by iterating and counting elements
3945 update_cave_speed(cave);