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 // play diamond or stone sound of given element.
130 static void play_sound_of_element(GdCave *cave, GdElement element, int x, int y)
132 // stone and diamond fall sounds.
136 gd_sound_play(cave, GD_S_NUT_FALLING, element, x, y);
140 gd_sound_play(cave, GD_S_NUT_IMPACT, element, x, y);
144 gd_sound_play(cave, GD_S_STONE_FALLING, element, x, y);
148 gd_sound_play(cave, GD_S_STONE_IMPACT, element, x, y);
152 gd_sound_play(cave, GD_S_FLYING_STONE_FALLING, element, x, y);
155 case O_FLYING_STONE_F:
156 gd_sound_play(cave, GD_S_FLYING_STONE_IMPACT, element, x, y);
160 gd_sound_play(cave, GD_S_MEGA_STONE_FALLING, element, x, y);
164 gd_sound_play(cave, GD_S_MEGA_STONE_IMPACT, element, x, y);
168 gd_sound_play(cave, GD_S_NITRO_PACK_FALLING, element, x, y);
172 gd_sound_play(cave, GD_S_NITRO_PACK_IMPACT, element, x, y);
176 gd_sound_play(cave, GD_S_FALLING_WALL_FALLING, element, x, y);
179 case O_FALLING_WALL_F:
180 gd_sound_play(cave, GD_S_FALLING_WALL_IMPACT, element, x, y);
183 case O_H_EXPANDING_WALL:
184 case O_V_EXPANDING_WALL:
185 case O_EXPANDING_WALL:
186 case O_H_EXPANDING_STEEL_WALL:
187 case O_V_EXPANDING_STEEL_WALL:
188 case O_EXPANDING_STEEL_WALL:
189 gd_sound_play(cave, GD_S_EXPANDING_WALL, element, x, y);
193 gd_sound_play(cave, GD_S_DIAMOND_FALLING_RANDOM, element, x, y);
197 gd_sound_play(cave, GD_S_DIAMOND_IMPACT_RANDOM, element, x, y);
200 case O_FLYING_DIAMOND:
201 gd_sound_play(cave, GD_S_FLYING_DIAMOND_FALLING_RANDOM, element, x, y);
204 case O_FLYING_DIAMOND_F:
205 gd_sound_play(cave, GD_S_FLYING_DIAMOND_IMPACT_RANDOM, element, x, y);
208 case O_BLADDER_SPENDER:
209 gd_sound_play(cave, GD_S_BLADDER_SPENDER, element, x, y);
213 gd_sound_play(cave, GD_S_BLADDER_CONVERTING, element, x, y);
217 gd_sound_play(cave, GD_S_SLIME, element, x, y);
221 gd_sound_play(cave, GD_S_LAVA, element, x, y);
225 gd_sound_play(cave, GD_S_ACID_SPREADING, element, x, y);
229 gd_sound_play(cave, GD_S_BLADDER_MOVING, element, x, y);
236 gd_sound_play(cave, GD_S_BITER_EATING, element, x, y);
240 gd_sound_play(cave, GD_S_DIRT_BALL_FALLING, element, x, y);
244 gd_sound_play(cave, GD_S_DIRT_BALL_IMPACT, element, x, y);
248 gd_sound_play(cave, GD_S_DIRT_LOOSE_FALLING, element, x, y);
252 gd_sound_play(cave, GD_S_DIRT_LOOSE_IMPACT, element, x, y);
261 // play sound of given element being pushed.
262 static void play_sound_of_element_pushing(GdCave *cave, GdElement element, int x, int y)
267 gd_sound_play(cave, GD_S_NUT_PUSHING, element, x, y);
271 gd_sound_play(cave, GD_S_STONE_PUSHING, element, x, y);
275 gd_sound_play(cave, GD_S_FLYING_STONE_PUSHING, element, x, y);
279 gd_sound_play(cave, GD_S_MEGA_STONE_PUSHING, element, x, y);
282 case O_WAITING_STONE:
283 gd_sound_play(cave, GD_S_WAITING_STONE_PUSHING, element, x, y);
286 case O_CHASING_STONE:
287 gd_sound_play(cave, GD_S_CHASING_STONE_PUSHING, element, x, y);
291 gd_sound_play(cave, GD_S_NITRO_PACK_PUSHING, element, x, y);
295 gd_sound_play(cave, GD_S_BLADDER_PUSHING, element, x, y);
304 static inline int getx(const GdCave *cave, const int x, const int y)
306 return cave->getx(cave, x, y);
309 static inline int gety(const GdCave *cave, const int x, const int y)
311 return cave->gety(cave, x, y);
314 // perfect (non-lineshifting) GET x/y functions; returns range corrected x/y position
315 static inline int getx_perfect(const GdCave *cave, const int x, const int y)
317 return (x + cave->w) % cave->w;
320 static inline int gety_perfect(const GdCave *cave, const int x, const int y)
322 return (y + cave->h) % cave->h;
325 // line shifting GET x/y function; returns range corrected x/y position
326 static inline int getx_shift(const GdCave *cave, int x, int y)
328 return (x + cave->w) % cave->w;
331 static inline int gety_shift(const GdCave *cave, int x, int y)
333 return ((x < 0 ? y - 1 : x >= cave->w ? y + 1 : y) + cave->h) % cave->h;
336 static inline GdElement *getp(const GdCave *cave, const int x, const int y)
338 return cave->getp(cave, x, y);
342 perfect (non-lineshifting) GET function.
343 returns a pointer to a selected cave element by its coordinates.
345 static inline GdElement *getp_perfect(const GdCave *cave, const int x, const int y)
347 // (x + n) mod n: this works also for x >= n and -n + 1 < x < 0
348 return &(cave->map[(y + cave->h) % cave->h][(x + cave->w) % cave->w]);
352 line shifting GET function; returns a pointer to the selected cave element.
353 this is used to emulate the line-shifting behaviour of original games, so that
354 the player entering one side will appear one row above or below on the other.
356 static inline GdElement *getp_shift(const GdCave *cave, int x, int y)
369 y = (y + cave->h) % cave->h;
371 return &(cave->map[y][x]);
374 static inline GdElement get(const GdCave *cave, const int x, const int y)
376 return *getp(cave, x, y);
379 // returns an element which is somewhere near x,y
380 static inline GdElement get_dir(const GdCave *cave, const int x, const int y,
381 const GdDirection dir)
383 return get(cave, x + gd_dx[dir], y + gd_dy[dir]);
386 static inline boolean explodes_by_hit_dir(const GdCave *cave, const int x,
387 const int y, GdDirection dir)
389 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_EXPLODES_BY_HIT) != 0;
392 // returns true if the element is not explodable, for example the steel wall
393 static inline boolean non_explodable(const GdCave *cave, const int x, const int y)
395 return (gd_elements[get(cave, x,y) & O_MASK].properties & P_NON_EXPLODABLE) != 0;
398 // returns true if the element can be eaten by the amoeba, eg. space and dirt.
399 static inline boolean amoeba_eats_dir(const GdCave *cave, const int x, const int y,
400 const GdDirection dir)
402 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_AMOEBA_CONSUMES) != 0;
405 // returns true if the element is sloped, so stones and diamonds roll down on it.
406 // for example a stone or brick wall
407 static inline boolean sloped_dir(const GdCave *cave, const int x, const int y,
408 const GdDirection dir, const GdDirection slop)
413 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_LEFT) != 0;
416 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_RIGHT) != 0;
419 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_UP) != 0;
422 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_DOWN) != 0;
431 // returns true if the element is sloped for bladder movement
432 // (brick = yes, diamond = no, for example)
433 static inline boolean sloped_for_bladder_dir (const GdCave *cave, const int x, const int y,
434 const GdDirection dir)
436 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLADDER_SLOPED) != 0;
439 static inline boolean blows_up_flies_dir(const GdCave *cave, const int x, const int y,
440 const GdDirection dir)
442 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLOWS_UP_FLIES) != 0;
445 // returns true if the element is a counter-clockwise creature
446 static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
448 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_CCW) != 0;
451 // returns true if the element is a player
452 boolean is_player(const GdCave *cave, const int x, const int y)
454 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
457 // returns true if the element is a player
458 static inline boolean is_player_dir(const GdCave *cave, const int x, const int y,
459 const GdDirection dir)
461 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PLAYER) != 0;
464 static inline boolean can_be_hammered_dir(const GdCave *cave, const int x, const int y,
465 const GdDirection dir)
467 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
470 // returns true if the element can be pushed
471 boolean can_be_pushed_dir(const GdCave *cave, const int x, const int y,
472 const GdDirection dir)
474 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PUSHABLE) != 0;
477 // returns true if the element is explodable and explodes to space, for example the player
478 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
480 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
483 // returns true if the element is moved by the conveyor belt
484 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
485 const GdDirection dir)
487 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
490 // returns true if the element is moved by the conveyor belt
491 static inline boolean moved_by_conveyor_bottom_dir(const GdCave *cave, const int x, const int y,
492 const GdDirection dir)
494 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_BOTTOM) != 0;
497 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
498 const GdDirection dir)
500 return (get_dir(cave, x, y, dir) & SCANNED) != 0;
503 // returns true if neighbouring element is "e"
504 // treats dirt specially
505 // treats lava specially
506 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
507 const GdDirection dir, GdElement e)
509 GdElement examined = get_dir(cave, x, y, dir);
511 // if it is a dirt-like, change to dirt, so equality will evaluate to true
512 if (gd_elements[examined & O_MASK].properties & P_DIRT)
515 if (gd_elements[e & O_MASK].properties & P_DIRT)
518 // if the element on the map is a lava, it should be like space
519 if (examined == O_LAVA)
522 return (e == examined);
525 // returns true if neighbouring element is space
526 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
527 const GdDirection dir)
529 GdElement e = get_dir(cave, x, y, dir) & O_MASK;
531 return (e == O_SPACE || e == O_LAVA);
534 static inline void store_dir_buffer(GdCave *cave, const int x, const int y, const GdDirection dir)
536 // raw values without range correction
537 int raw_x = x + gd_dx[dir];
538 int raw_y = y + gd_dy[dir];
540 // final values with range correction
541 int new_x = getx(cave, raw_x, raw_y);
542 int new_y = gety(cave, raw_x, raw_y);
543 int new_dir = (dir > GD_MV_TWICE ? dir - GD_MV_TWICE : dir);
545 game_bd.game->dir_buffer[new_y][new_x] = new_dir;
548 // store an element at the given position
549 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
551 GdElement *e = getp(cave, x, y);
555 play_sound_of_element(cave, O_LAVA, x, y);
563 // store an element with SCANNED flag turned on
564 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
566 store(cave, x, y, element | SCANNED);
569 // store an element to a neighbouring cell
570 static inline void store_dir(GdCave *cave, const int x, const int y,
571 const GdDirection dir, const GdElement element)
573 store_dir_buffer(cave, x, y, dir);
574 store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED);
577 // store an element to a neighbouring cell
578 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
579 const GdDirection dir, const GdElement element)
581 store_dir_buffer(cave, x, y, dir);
582 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
585 // move element to direction; then place space at x, y
586 static inline void move(GdCave *cave, const int x, const int y,
587 const GdDirection dir, const GdElement e)
589 store_dir(cave, x, y, dir, e);
590 store(cave, x, y, O_SPACE);
593 // increment a cave element; can be used for elements which are one after
594 // the other, for example bladder1, bladder2, bladder3...
595 static inline void next(GdCave *cave, const int x, const int y)
597 (*getp(cave, x, y))++;
600 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
602 if (non_explodable (cave, x, y))
605 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
606 cave->voodoo_touched = TRUE;
608 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
609 // voodoo turns into a time penalty
610 store_sc(cave, x, y, O_TIME_PENALTY);
611 else if (get(cave, x, y) == O_NITRO_PACK ||
612 get(cave, x, y) == O_NITRO_PACK_F)
613 // nitro pack inside an explosion - it is now triggered
614 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
616 // for everything else
617 store_sc(cave, x, y, explode_to);
620 // a creature explodes to a 3x3 something.
621 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
625 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
626 cave->ckdelay += 1200;
627 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
629 for (yy = y - 1; yy <= y + 1; yy++)
630 for (xx = x - 1; xx <= x + 1; xx++)
631 cell_explode(cave, xx, yy, explode_to);
634 static void nitro_explode(GdCave *cave, int x, int y)
638 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
639 cave->ckdelay += 1200;
640 gd_sound_play(cave, GD_S_NITRO_PACK_EXPLODING, get(cave, x, y), x, y);
642 for (yy = y - 1; yy <= y + 1; yy++)
643 for (xx = x - 1; xx <= x + 1; xx++)
644 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
646 // the current cell is explicitly changed into a nitro expl,
647 // as cell_explode changes it to a triggered nitro pack
648 store_sc(cave, x, y, O_NITRO_EXPL_1);
651 // a voodoo explodes, leaving a 3x3 steel and a time penalty behind.
652 static void voodoo_explode(GdCave *cave, int x, int y)
656 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
657 cave->ckdelay += 1000;
659 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
660 if (cave->voodoo_any_hurt_kills_player)
661 cave->voodoo_touched = TRUE;
663 // voodoo explodes to 3x3 steel
664 for (yy = y - 1; yy <= y + 1; yy++)
665 for (xx = x - 1; xx <= x + 1; xx++)
666 store_sc(cave, xx, yy, O_PRE_STEEL_1);
668 // middle is a time penalty (which will be turned into a gravestone)
669 store_sc(cave, x, y, O_TIME_PENALTY);
673 a bomb does not explode the voodoo, neither does the ghost.
674 this function check this, and stores the new element or not.
675 destroying the voodoo is also controlled by the
676 voodoo_disappear_in_explosion flag.
678 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
680 if (non_explodable (cave, x, y))
683 // bomb does not explode voodoo
684 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
687 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
688 cave->voodoo_touched = TRUE;
690 store_sc (cave, x, y, expl);
693 // X shaped ghost explosion; does not touch voodoo!
694 static void ghost_explode(GdCave *cave, const int x, const int y)
696 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
698 // the processing of an explosion took pretty much time: processing 5 elements
699 cave->ckdelay += 650;
701 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
702 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
703 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
704 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
705 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
708 // +shaped bomb explosion; does not touch voodoo!
709 static void bomb_explode(GdCave *cave, const int x, const int y)
711 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
713 // the processing of an explosion took pretty much time: processing 5 elements
714 cave->ckdelay += 650;
716 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
717 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
718 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
719 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
720 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
724 explode an element with the appropriate type of exlposion.
726 static void explode(GdCave *cave, int x, int y)
728 GdElement e = get(cave, x, y) & O_MASK;
733 ghost_explode(cave, x, y);
737 bomb_explode(cave, x, y);
741 voodoo_explode(cave, x, y);
746 case O_NITRO_PACK_EXPLODE:
747 nitro_explode(cave, x, y);
751 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
754 case O_FALLING_WALL_F:
755 creature_explode(cave, x, y, O_EXPLODE_1);
762 creature_explode(cave, x, y, O_EXPLODE_1);
769 creature_explode(cave, x, y, cave->butterfly_explode_to);
776 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
783 creature_explode(cave, x, y, cave->firefly_explode_to);
786 case O_ALT_FIREFLY_1:
787 case O_ALT_FIREFLY_2:
788 case O_ALT_FIREFLY_3:
789 case O_ALT_FIREFLY_4:
790 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
796 case O_PLAYER_STIRRING:
797 case O_PLAYER_ROCKET_LAUNCHER:
798 case O_PLAYER_PNEUMATIC_LEFT:
799 case O_PLAYER_PNEUMATIC_RIGHT:
800 creature_explode(cave, x, y, O_EXPLODE_1);
807 creature_explode(cave, x, y, cave->stonefly_explode_to);
814 creature_explode(cave, x, y, cave->dragonfly_explode_to);
822 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
824 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
828 player eats specified object.
829 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
830 returns other element if something other appears there and he can't move.
831 cave pointer is needed to know the diamond values.
833 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
840 cave->diamond_key_collected = TRUE;
841 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
846 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
851 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
856 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
863 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
870 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
877 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
882 case O_CREATURE_SWITCH: // creatures change direction.
883 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
884 cave->creatures_backwards = !cave->creatures_backwards;
887 case O_EXPANDING_WALL_SWITCH: // expanding wall change direction.
888 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
889 cave->expanding_wall_changed = !cave->expanding_wall_changed;
892 case O_BITER_SWITCH: // biter change delay
893 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
894 cave->biter_delay_frame++;
895 if (cave->biter_delay_frame == 4)
896 cave->biter_delay_frame = 0;
899 case O_REPLICATOR_SWITCH: // replicator on/off switch
900 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
901 cave->replicators_active = !cave->replicators_active;
904 case O_CONVEYOR_SWITCH: // conveyor belts on/off
905 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
906 cave->conveyor_belts_active = !cave->conveyor_belts_active;
909 case O_CONVEYOR_DIR_SWITCH: // conveyor belts switch direction
910 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
911 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
917 case O_STEEL_EATABLE:
918 case O_BRICK_EATABLE:
919 case O_DIRT_SLOPED_UP_RIGHT:
920 case O_DIRT_SLOPED_UP_LEFT:
921 case O_DIRT_SLOPED_DOWN_LEFT:
922 case O_DIRT_SLOPED_DOWN_RIGHT:
925 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
929 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
930 cave->sweet_eaten = TRUE;
933 case O_PNEUMATIC_HAMMER:
934 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
935 cave->got_pneumatic_hammer = TRUE;
940 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
941 cave->time += cave->time_bonus * cave->timing_factor;
942 if (cave->time > cave->max_time * cave->timing_factor)
943 cave->time -= cave->max_time * cave->timing_factor;
944 // no space, rather a dirt remains there...
948 case O_FLYING_DIAMOND:
949 // prevent diamond sounds for O_SKELETON (see below)
950 if (x != -1 && y != -1)
951 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
952 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
954 cave->score += cave->diamond_value;
955 cave->diamonds_collected++;
957 if (cave->diamonds_needed == cave->diamonds_collected)
959 cave->gate_open = TRUE;
961 // extra is worth more points.
962 cave->diamond_value = cave->extra_diamond_value;
964 cave->gate_open_flash = 1;
965 cave->sound3 = GD_S_CRACKING;
966 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
971 cave->skeletons_collected++;
973 // as if player got a diamond
974 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
975 player_get_element(cave, O_DIAMOND, -1, -1);
977 // _after_ calling get_element for the fake diamonds, so we overwrite its sounds
978 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
983 cave->player_state = GD_PL_EXITED; // player now exits the cave!
987 case O_LAVA: // player goes into lava, as if it was space
988 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
992 // the object will remain there.
998 process a crazy dream-style teleporter.
999 called from gd_cave_iterate, for a player or a player_bomb.
1000 player is standing at px, py, and trying to move in the direction player_move,
1001 where there is a teleporter at (tx_start, ty_start). we check the whole cave,
1002 from (tx_start + 1, ty_start), till we get back to (tx_start, ty_start) (by wrapping
1003 around). the first teleporter we find, and which is suitable, will be the destination.
1004 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
1006 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
1008 // start at teleporter position (not at player position!)
1009 int tx_start = px + gd_dx[player_move];
1010 int ty_start = py + gd_dy[player_move];
1016 // jump to next element; wrap around columns and rows.
1028 // if we found a teleporter...
1029 if (get(cave, tx, ty) == O_TELEPORTER &&
1030 is_space_dir(cave, tx, ty, player_move))
1032 // new player appears near teleporter found
1033 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1035 // current player disappears
1036 store(cave, px, py, O_SPACE);
1038 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1040 return TRUE; // return true as teleporter worked
1043 // loop until we get back to original coordinates
1044 while (tx != tx_start || ty != ty_start);
1046 // return false as we did not find any usable teleporter
1051 try to push an element.
1052 returns true if the push is possible; also does move the specified _element_.
1053 up to the caller to move the _player_itself_.
1055 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1058 GdElement what = get_dir(cave, x, y, player_move);
1060 // gravity for falling wall, bladder, ...
1061 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1067 case O_WAITING_STONE:
1070 case O_CHASING_STONE:
1072 case O_FLYING_STONE:
1074 // pushing some kind of stone or nut
1075 // directions possible: 90degrees cw or ccw to current gravity.
1076 // only push if player dir is orthogonal to gravity,
1077 // ie. gravity down, pushing left & right possible
1078 if (player_move == ccw_fourth[cave->gravity] ||
1079 player_move == cw_fourth[cave->gravity])
1085 // different probabilities for different elements.
1088 case O_WAITING_STONE:
1089 // waiting stones are light, can always push
1093 case O_CHASING_STONE:
1094 // chasing can be pushed if player is turbo
1095 if (cave->sweet_eaten)
1100 // mega may(!) be pushed if player is turbo
1101 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1107 case O_FLYING_STONE:
1109 if (cave->sweet_eaten)
1110 prob = cave->pushing_stone_prob_sweet; // probability with sweet
1112 prob = cave->pushing_stone_prob; // probability without sweet.
1119 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1120 gd_rand_int_range(cave->random, 0, 1000000) < prob)
1122 // if decided that he will be able to push,
1123 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1124 play_sound_of_element_pushing(cave, what, x, y);
1139 // pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1140 // not an O_BLADDER_x.
1141 // there is no "delayed" state of a bladder, so we use store_dir_no_scanned!
1143 // first check: we cannot push a bladder "up"
1144 if (player_move != opposite[grav_compat])
1146 // pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check.
1147 // player moving in the direction of gravity.
1151 if (player_move == grav_compat)
1153 // pushing bladder down
1154 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1155 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1156 // if no space to push down, maybe left (down-left to player)
1157 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1159 // left is "down, turned right (cw)"
1160 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1161 // if not, maybe right (down-right to player)
1162 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1163 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1166 // pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check.
1170 else if (player_move == cw_fourth[grav_compat])
1172 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) // pushing it left
1173 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1174 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) // maybe down, and player will move left
1175 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1176 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) // maybe up, and player will move left
1177 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1180 // pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check.
1184 else if (player_move == ccw_fourth[grav_compat])
1186 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) // pushing it right
1187 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1188 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) // maybe down, and player will move right
1189 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1190 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) // maybe up, and player will move right
1191 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1195 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1200 // a box is only pushed with the fire pressed
1203 // but always with 100% probability
1204 switch (player_move)
1210 // pushing in some dir, two steps in that dir - is there space?
1211 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1214 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1216 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1221 // push in no other directions possible
1227 // pushing of other elements not possible
1235 // from the key press booleans, create a direction
1236 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1238 GdDirection player_move;
1240 // from the key press booleans, create a direction
1242 player_move = GD_MV_UP_RIGHT;
1243 else if (down && right)
1244 player_move = GD_MV_DOWN_RIGHT;
1245 else if (down && left)
1246 player_move = GD_MV_DOWN_LEFT;
1247 else if (up && left)
1248 player_move = GD_MV_UP_LEFT;
1250 player_move = GD_MV_UP;
1252 player_move = GD_MV_DOWN;
1254 player_move = GD_MV_LEFT;
1256 player_move = GD_MV_RIGHT;
1258 player_move = GD_MV_STILL;
1263 // clear these to no sound; and they will be set during iteration.
1264 void gd_cave_clear_sounds(GdCave *cave)
1266 cave->sound1 = GD_S_NONE;
1267 cave->sound2 = GD_S_NONE;
1268 cave->sound3 = GD_S_NONE;
1271 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1272 GdElement falling_element)
1274 if (cave->gravity_disabled)
1277 if (is_space_dir(cave, x, y, falling_direction))
1279 // beginning to fall
1280 play_sound_of_element(cave, get(cave, x, y), x, y);
1281 move(cave, x, y, falling_direction, falling_element);
1284 // check if it is on a sloped element, and it can roll.
1285 // for example, sloped wall looks like:
1288 // this is tagged as sloped up&left.
1289 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1290 // then check the direction to roll (left or right)
1291 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1292 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1294 // rolling down, if sitting on a sloped object
1295 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1296 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1297 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1299 // rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1300 // so here we use cw_fourth
1301 play_sound_of_element(cave, get(cave, x, y), x, y);
1302 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1304 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1305 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1306 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1309 play_sound_of_element(cave, get(cave, x, y), x, y);
1310 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1315 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1317 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1318 cave->voodoo_dies_by_stone)
1320 // this is a 1stB-style vodo. explodes by stone, collects diamonds
1321 explode_dir(cave, x, y, fall_dir);
1328 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1330 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1331 cave->voodoo_collects_diamonds)
1333 // this is a 1stB-style voodoo. explodes by stone, collects diamonds
1334 player_get_element(cave, O_DIAMOND, x, y); // as if player got diamond
1335 store(cave, x, y, O_SPACE); // diamond disappears
1342 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1343 GdDirection fall_dir, GdElement bouncing)
1345 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1346 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1349 store(cave, x, y, bouncing);
1350 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1352 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1360 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1361 GdDirection fall_dir, GdElement magic)
1363 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1365 play_sound_of_element(cave, O_DIAMOND, x, y); // always play diamond sound
1367 if (cave->magic_wall_state == GD_MW_DORMANT)
1368 cave->magic_wall_state = GD_MW_ACTIVE;
1370 if (cave->magic_wall_state == GD_MW_ACTIVE &&
1371 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1373 // if magic wall active and place underneath, it turns element
1374 // into anything the effect says to do.
1375 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1378 // active or non-active or anything, element falling in will always disappear
1379 store(cave, x, y, O_SPACE);
1381 if (cave->magic_wall_breakscan && cave->amoeba_state == GD_AM_AWAKE)
1382 cave->convert_amoeba_this_frame = TRUE;
1390 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1392 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1394 explode_dir(cave, x, y, fall_dir);
1401 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1402 GdDirection fall_dir, GdElement bouncing)
1404 if (is_space_dir(cave, x, y, fall_dir))
1407 move(cave, x, y, fall_dir, get(cave, x, y));
1412 // check if it is on a sloped element, and it can roll.
1413 // for example, sloped wall looks like:
1416 // this is tagged as sloped up&left.
1417 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1418 // then check the direction to roll (left or right)
1419 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1421 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1423 // sloped element, falling to left or right
1424 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1425 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1426 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1428 play_sound_of_element(cave, get(cave, x, y), x, y);
1430 // try to roll left first - see O_STONE to understand why cw_fourth
1431 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1433 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1434 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1435 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1437 play_sound_of_element(cave, get(cave, x, y), x, y);
1439 // if not, try to roll right
1440 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1444 // cannot roll in any direction, so it stops
1445 play_sound_of_element(cave, get(cave, x, y), x, y);
1446 store(cave, x, y, bouncing);
1452 // any other element, stops
1453 play_sound_of_element(cave, get(cave, x, y), x, y);
1454 store(cave, x, y, bouncing);
1458 static void update_cave_speed(GdCave *cave)
1460 // update timing calculated by iterating and counting elements which were slow to process on c64
1461 switch (cave->scheduling)
1463 case GD_SCHEDULING_MILLISECONDS:
1464 // cave->speed already contains the milliseconds value, do not touch it
1467 case GD_SCHEDULING_BD1:
1468 if (!cave->intermission)
1469 // non-intermissions
1470 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1472 // intermissions were quicker, as only lines 1-12 were processed by the engine.
1473 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1476 case GD_SCHEDULING_BD1_ATARI:
1477 // about 20ms/frame faster than c64 version
1478 if (!cave->intermission)
1479 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); // non-intermissions
1481 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); // for intermissions
1484 case GD_SCHEDULING_BD2:
1486 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1489 case GD_SCHEDULING_PLCK:
1490 // 65 is totally empty cave in construction kit, with delay = 0)
1491 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1494 case GD_SCHEDULING_BD2_PLCK_ATARI:
1495 // a really fast engine; timing works like c64 plck.
1496 // 40 ms was measured in the construction kit, with delay = 0
1497 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1500 case GD_SCHEDULING_CRDR:
1501 if (cave->hammered_walls_reappear) // this made the engine very slow.
1502 cave->ckdelay += 60000;
1503 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1506 case GD_SCHEDULING_MAX:
1512 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1519 // amoeba found to be enclosed. if not, this is cleared
1520 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1522 // counting the number of amoebas. after scan, check if too much
1523 int amoeba_count, amoeba_2_count;
1525 // cave scan found water - for sound
1526 boolean found_water;
1528 boolean inbox_toggle;
1529 boolean start_signal;
1531 // gravity for falling wall, bladder, ...
1532 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1534 // directions for o_something_1, 2, 3 and 4 (creatures)
1535 static const GdDirection creature_dir[] =
1542 static const GdDirection creature_chdir[] =
1549 int time_decrement_sec;
1551 // biters eating elements preference, they try to go in this order
1552 GdElement biter_try[] =
1559 boolean amoeba_sound, magic_sound;
1561 gd_cave_clear_sounds(cave);
1563 // if diagonal movements not allowed,
1564 // horizontal movements have precedence. [BROADRIBB]
1565 if (!cave->diagonal_movements)
1567 switch (player_move)
1569 case GD_MV_UP_RIGHT:
1570 case GD_MV_DOWN_RIGHT:
1571 player_move = GD_MV_RIGHT;
1575 case GD_MV_DOWN_LEFT:
1576 player_move = GD_MV_LEFT;
1580 // no correction needed
1585 // set cave get function; to implement perfect or lineshifting borders
1586 if (cave->lineshift)
1588 cave->getp = getp_shift;
1589 cave->getx = getx_shift;
1590 cave->gety = gety_shift;
1594 cave->getp = getp_perfect;
1595 cave->getx = getx_perfect;
1596 cave->gety = gety_perfect;
1599 // increment this. if the scan routine comes across player, clears it (sets to zero).
1600 if (cave->player_seen_ago < 100)
1601 cave->player_seen_ago++;
1603 if (cave->pneumatic_hammer_active_delay > 0)
1604 cave->pneumatic_hammer_active_delay--;
1606 // inboxes and outboxes flash with the rhythm of the game, not the display.
1607 // also, a player can be born only from an open, not from a steel-wall-like inbox.
1608 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1609 inbox_toggle = cave->inbox_flash_toggle;
1611 if (cave->gate_open_flash > 0)
1612 cave->gate_open_flash--;
1614 // score collected this frame
1617 // to implement buggy bd1 amoeba+magic wall behaviour
1618 cave->convert_amoeba_this_frame = FALSE;
1620 // suicide only kills the active player
1621 // player_x, player_y was set by the previous iterate routine, or the cave setup.
1622 // we must check if there is a player or not - he may have exploded or something like that
1623 if (suicide && cave->player_state == GD_PL_LIVING &&
1624 is_player(cave, cave->player_x, cave->player_y))
1625 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1627 // check for walls reappearing
1628 if (cave->hammered_reappear)
1630 for (y = 0; y < cave->h; y++)
1632 for (x = 0; x < cave->w; x++)
1634 // timer for the cell > 0?
1635 if (cave->hammered_reappear[y][x] > 0)
1638 cave->hammered_reappear[y][x]--;
1640 // check if it became zero
1641 if (cave->hammered_reappear[y][x] == 0)
1643 store(cave, x, y, O_BRICK);
1644 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1651 // variables to check during the scan
1653 // will be set to false if any of the amoeba is found free.
1654 amoeba_found_enclosed = TRUE;
1655 amoeba_2_found_enclosed = TRUE;
1658 found_water = FALSE;
1660 time_decrement_sec = 0;
1662 // check whether to scan the first and last line
1663 if (cave->border_scan_first_and_last)
1674 // the cave scan routine
1675 for (y = ymin; y <= ymax; y++)
1677 for (x = 0; x < cave->w; x++)
1679 // if we find a scanned element, change it to the normal one, and that's all.
1680 // this is required, for example for chasing stones, which have moved, always passing slime!
1681 if (get(cave, x, y) & SCANNED)
1683 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1688 // add the ckdelay correction value for every element seen.
1689 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1691 switch (get(cave, x, y))
1693 // ============================================================================
1695 // ============================================================================
1698 if (cave->kill_player)
1700 explode (cave, x, y);
1704 cave->player_seen_ago = 0;
1705 // bd4 intermission caves have many players. so if one of them has exited,
1706 // do not change the flag anymore. so this if () is needed
1707 if (cave->player_state != GD_PL_EXITED)
1708 cave->player_state = GD_PL_LIVING;
1710 // check for pneumatic hammer things
1711 // 1) press fire, 2) have pneumatic hammer 4) space on left or right
1712 // for hammer 5) stand on something
1713 if (player_fire && cave->got_pneumatic_hammer &&
1714 is_space_dir(cave, x, y, player_move) &&
1715 !is_space_dir(cave, x, y, GD_MV_DOWN))
1717 if (player_move == GD_MV_LEFT &&
1718 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1720 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1721 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1722 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1726 if (player_move == GD_MV_RIGHT &&
1727 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1729 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1730 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1731 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1736 if (player_move != GD_MV_STILL)
1738 // only do every check if he is not moving
1739 GdElement what = get_dir(cave, x, y, player_move);
1740 GdElement remains = what;
1743 // if we are 'eating' a teleporter, and the function returns true
1744 // (teleporting worked), break here
1745 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1748 // try to push element; if successful, break
1749 push = do_push(cave, x, y, player_move, player_fire);
1759 // if its a bomb, remember he now has one.
1760 // we do not change the "remains" and "what" variables,
1761 // so that part of the code will be ineffective
1762 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1763 store_dir(cave, x, y, player_move, O_SPACE);
1766 store(cave, x, y, O_PLAYER_BOMB);
1768 move(cave, x, y, player_move, O_PLAYER_BOMB);
1771 case O_ROCKET_LAUNCHER:
1772 // if its a rocket launcher, remember he now has one.
1773 // we do not change the "remains" and "what" variables,
1774 // so that part of the code will be ineffective
1775 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1776 store_dir(cave, x, y, player_move, O_SPACE);
1779 store(cave, x, y, O_PLAYER_ROCKET_LAUNCHER);
1781 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
1785 // we do not change the "remains" and "what" variables,
1786 // so that part of the code will be ineffective
1787 if (!player_fire && !cave->gravity_switch_active &&
1788 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1790 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1791 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1792 cave->gravity_disabled = TRUE;
1796 case O_GRAVITY_SWITCH:
1797 // (we cannot use player_get for this as it does not have player_move parameter)
1798 // only allow changing direction if the new dir is not diagonal
1799 if (cave->gravity_switch_active &&
1800 (player_move == GD_MV_LEFT ||
1801 player_move == GD_MV_RIGHT ||
1802 player_move == GD_MV_UP ||
1803 player_move == GD_MV_DOWN))
1805 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1806 cave->gravity_will_change =
1807 cave->gravity_change_time * cave->timing_factor;
1808 cave->gravity_next_direction = player_move;
1809 cave->gravity_switch_active = FALSE;
1814 // get element - process others.
1815 // if cannot get, player_get_element will return the same
1816 remains = player_get_element(cave, what, x, y);
1821 if (remains != what || remains == O_SPACE)
1823 // if anything changed, apply the change.
1825 // if snapping anything and we have snapping explosions set.
1826 // but these is not true for pushing.
1827 if (remains == O_SPACE && player_fire && !push)
1828 remains = cave->snap_element;
1830 if (remains != O_SPACE || player_fire)
1831 // if any other element than space, player cannot move.
1832 // also if pressing fire, will not move.
1833 store_dir(cave, x, y, player_move, remains);
1835 // if space remains there, the player moves.
1836 move(cave, x, y, player_move, O_PLAYER);
1842 // much simpler; cannot steal stones
1843 if (cave->kill_player)
1845 explode(cave, x, y);
1849 cave->player_seen_ago = 0;
1850 // bd4 intermission caves have many players. so if one of them has exited,
1851 // do not change the flag anymore. so this if () is needed
1852 if (cave->player_state != GD_PL_EXITED)
1853 cave->player_state = GD_PL_LIVING;
1855 if (player_move != GD_MV_STILL)
1857 // if the player does not move, nothing to do
1858 GdElement what = get_dir(cave, x, y, player_move);
1859 GdElement remains = what;
1863 // placing a bomb into empty space or dirt
1864 if (is_space_dir(cave, x, y, player_move) ||
1865 is_element_dir(cave, x, y, player_move, O_DIRT))
1867 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1869 // placed bomb, he is normal player again
1870 store(cave, x, y, O_PLAYER);
1871 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1876 // pushing and collecting
1877 // if we are 'eating' a teleporter, and the function returns true
1878 // (teleporting worked), break here
1879 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1882 // player fire is false...
1883 if (do_push(cave, x, y, player_move, FALSE))
1891 case O_GRAVITY_SWITCH:
1892 // (we cannot use player_get for this as it does not have
1893 // player_move parameter)
1894 // only allow changing direction if the new dir is not diagonal
1895 if (cave->gravity_switch_active &&
1896 (player_move == GD_MV_LEFT ||
1897 player_move == GD_MV_RIGHT ||
1898 player_move == GD_MV_UP ||
1899 player_move == GD_MV_DOWN))
1901 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1902 cave->gravity_will_change =
1903 cave->gravity_change_time * cave->timing_factor;
1904 cave->gravity_next_direction = player_move;
1905 cave->gravity_switch_active = FALSE;
1910 // get element. if cannot get, player_get_element will return the same
1911 remains = player_get_element (cave, what, x, y);
1916 // if element changed, OR there is space, move.
1917 if (remains != what || remains == O_SPACE)
1919 // if anything changed, apply the change.
1920 move(cave, x, y, player_move, O_PLAYER_BOMB);
1925 case O_PLAYER_ROCKET_LAUNCHER:
1926 // much simpler; cannot snap-push stones
1927 if (cave->kill_player)
1929 explode(cave, x, y);
1933 cave->player_seen_ago = 0;
1934 // bd4 intermission caves have many players. so if one of them has exited,
1935 // do not change the flag anymore. so this if () is needed
1936 if (cave->player_state != GD_PL_EXITED)
1937 cave->player_state = GD_PL_LIVING;
1940 if (player_move != GD_MV_STILL)
1942 // if the player does not move, nothing to do
1943 GdElement what = get_dir(cave, x, y, player_move);
1944 GdElement remains = what;
1946 // to fire a rocket, diagonal movement should not be allowed.
1947 // so either x or y must be zero
1950 // placing a rocket into empty space
1951 if (is_space_dir(cave, x, y, player_move))
1953 switch (player_move)
1956 store_dir(cave, x, y, player_move, O_ROCKET_1);
1957 if (!cave->infinite_rockets)
1958 store(cave, x, y, O_PLAYER);
1962 store_dir(cave, x, y, player_move, O_ROCKET_2);
1963 if (!cave->infinite_rockets)
1964 store(cave, x, y, O_PLAYER);
1968 store_dir(cave, x, y, player_move, O_ROCKET_3);
1969 if (!cave->infinite_rockets)
1970 store(cave, x, y, O_PLAYER);
1974 store_dir(cave, x, y, player_move, O_ROCKET_4);
1975 if (!cave->infinite_rockets)
1976 store(cave, x, y, O_PLAYER);
1980 // cannot fire in other directions
1984 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1987 // a player with rocket launcher cannot snap elements, so stop here
1991 // pushing and collecting
1992 // if we are 'eating' a teleporter, and the function returns true
1993 // (teleporting worked), break here
1994 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1997 // player fire is false...
1998 if (do_push(cave, x, y, player_move, FALSE))
2004 // get element. if cannot get, player_get_element will return the same
2005 remains = player_get_element(cave, what, x, y);
2008 // if something changed, OR there is space, move.
2009 if (remains != what || remains == O_SPACE)
2011 // if anything changed, apply the change.
2012 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
2017 case O_PLAYER_STIRRING:
2018 if (cave->kill_player)
2020 explode(cave, x, y);
2024 // stirring sound, if no other walking sound or explosion
2025 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
2027 cave->player_seen_ago = 0;
2028 // bd4 intermission caves have many players. so if one of them has exited,
2029 // do not change the flag anymore. so this if () is needed
2030 if (cave->player_state != GD_PL_EXITED)
2031 cave->player_state = GD_PL_LIVING;
2035 // player "exits" stirring the pot by pressing fire
2036 cave->gravity_disabled = FALSE;
2037 store(cave, x, y, O_PLAYER);
2038 cave->gravity_switch_active = TRUE;
2042 // player holding pneumatic hammer
2043 case O_PLAYER_PNEUMATIC_LEFT:
2044 case O_PLAYER_PNEUMATIC_RIGHT:
2045 // usual player stuff
2046 if (cave->kill_player)
2048 explode(cave, x, y);
2052 cave->player_seen_ago = 0;
2053 if (cave->player_state != GD_PL_EXITED)
2054 cave->player_state = GD_PL_LIVING;
2056 // if hammering time is up, becomes a normal player again.
2057 if (cave->pneumatic_hammer_active_delay == 0)
2058 store(cave, x, y, O_PLAYER);
2061 // the active pneumatic hammer itself
2062 case O_PNEUMATIC_ACTIVE_RIGHT:
2063 case O_PNEUMATIC_ACTIVE_LEFT:
2064 if (cave->pneumatic_hammer_active_delay == 0)
2068 // pneumatic hammer element disappears
2069 store(cave, x, y, O_SPACE);
2071 // which is the new element which appears after that one is hammered?
2072 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
2074 // if there is a new element, display it
2075 // O_NONE might be returned, for example if the element being
2076 // hammered explodes during hammering (by a nearby explosion)
2077 if (new_elem != O_NONE)
2079 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
2081 // and if walls reappear, remember it in array
2082 if (cave->hammered_walls_reappear)
2086 wall_y = (y + 1) % cave->h;
2087 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
2093 // ============================================================================
2094 // S T O N E S, D I A M O N D S
2095 // ============================================================================
2097 case O_STONE: // standing stone
2098 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
2101 case O_MEGA_STONE: // standing mega_stone
2102 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
2105 case O_DIAMOND: // standing diamond
2106 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
2109 case O_NUT: // standing nut
2110 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
2113 case O_DIRT_BALL: // standing dirt ball
2114 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
2117 case O_DIRT_LOOSE: // standing loose dirt
2118 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
2121 case O_FLYING_STONE: // standing stone
2122 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
2125 case O_FLYING_DIAMOND: // standing diamond
2126 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2129 // ============================================================================
2130 // 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
2131 // ============================================================================
2133 case O_DIRT_BALL_F: // falling dirt ball
2134 if (!cave->gravity_disabled)
2135 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2138 case O_DIRT_LOOSE_F: // falling loose dirt
2139 if (!cave->gravity_disabled)
2140 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2143 case O_STONE_F: // falling stone
2144 if (!cave->gravity_disabled)
2146 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2149 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2152 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2155 if (do_fall_try_crush(cave, x, y, cave->gravity))
2158 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2162 case O_MEGA_STONE_F: // falling mega
2163 if (!cave->gravity_disabled)
2165 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2168 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2171 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2174 if (do_fall_try_crush(cave, x, y, cave->gravity))
2177 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2181 case O_DIAMOND_F: // falling diamond
2182 if (!cave->gravity_disabled)
2184 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2187 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2190 if (do_fall_try_crush(cave, x, y, cave->gravity))
2193 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2197 case O_NUT_F: // falling nut
2198 if (!cave->gravity_disabled)
2200 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2203 if (do_fall_try_crush(cave, x, y, cave->gravity))
2206 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2210 case O_FLYING_STONE_F: // falling stone
2211 if (!cave->gravity_disabled)
2213 GdDirection fall_dir = opposite[cave->gravity];
2215 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2218 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2221 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2224 if (do_fall_try_crush(cave, x, y, fall_dir))
2227 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2231 case O_FLYING_DIAMOND_F: // falling diamond
2232 if (!cave->gravity_disabled)
2234 GdDirection fall_dir = opposite[cave->gravity];
2236 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2239 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2242 if (do_fall_try_crush(cave, x, y, fall_dir))
2245 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2249 // ============================================================================
2250 // N I T R O P A C K
2251 // ============================================================================
2253 case O_NITRO_PACK: // standing nitro pack
2254 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2257 case O_NITRO_PACK_F: // falling nitro pack
2258 if (!cave->gravity_disabled)
2260 if (is_space_dir(cave, x, y, cave->gravity)) // if space, falling further
2261 move(cave, x, y, cave->gravity, get(cave, x, y));
2262 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2264 // try magic wall; if true, function did the work
2266 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2268 // falling on a dirt, it does NOT explode - just stops at its place.
2269 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2270 store(cave, x, y, O_NITRO_PACK);
2273 // falling on any other element it explodes
2274 explode(cave, x, y);
2278 case O_NITRO_PACK_EXPLODE: // a triggered nitro pack
2279 explode(cave, x, y);
2282 // ============================================================================
2283 // C R E A T U R E S
2284 // ============================================================================
2290 // if cannot move in any direction, becomes an enclosed cow
2291 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2292 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2293 store(cave, x, y, O_COW_ENCLOSED_1);
2296 // THIS IS THE CREATURE MOVE thing copied.
2297 const GdDirection *creature_move;
2298 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2299 GdElement base; // base element number (which is like O_***_1)
2300 int dir, dirn, dirp; // direction
2304 dir = get(cave, x, y)-base; // facing where
2305 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2307 // now change direction if backwards
2308 if (cave->creatures_backwards)
2313 dirn = (dir + 3) & 3; // fast turn
2314 dirp = (dir + 1) & 3; // slow turn
2318 dirn = (dir + 1) & 3; // fast turn
2319 dirp = (dir + 3) & 3; // slow turn
2322 if (is_space_dir(cave, x, y, creature_move[dirn]))
2323 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2324 else if (is_space_dir(cave, x, y, creature_move[dir]))
2325 move(cave, x, y, creature_move[dir], base + dir); // go on
2327 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2331 // enclosed cows wait some time before turning to a skeleton
2332 case O_COW_ENCLOSED_1:
2333 case O_COW_ENCLOSED_2:
2334 case O_COW_ENCLOSED_3:
2335 case O_COW_ENCLOSED_4:
2336 case O_COW_ENCLOSED_5:
2337 case O_COW_ENCLOSED_6:
2338 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2339 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2340 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2341 is_space_dir(cave, x, y, GD_MV_DOWN))
2342 store(cave, x, y, O_COW_1);
2347 case O_COW_ENCLOSED_7:
2348 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2349 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2350 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2351 is_space_dir(cave, x, y, GD_MV_DOWN))
2352 store(cave, x, y, O_COW_1);
2354 store(cave, x, y, O_SKELETON);
2361 case O_ALT_FIREFLY_1:
2362 case O_ALT_FIREFLY_2:
2363 case O_ALT_FIREFLY_3:
2364 case O_ALT_FIREFLY_4:
2369 case O_ALT_BUTTER_1:
2370 case O_ALT_BUTTER_2:
2371 case O_ALT_BUTTER_3:
2372 case O_ALT_BUTTER_4:
2377 // check if touches a voodoo
2378 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2379 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2380 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2381 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2382 cave->voodoo_touched = TRUE;
2384 // check if touches something bad and should explode (includes voodoo by the flags)
2385 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2386 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2387 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2388 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2389 explode (cave, x, y);
2393 const GdDirection *creature_move;
2394 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2395 GdElement base = -1; // base element number (which is like O_***_1)
2396 int dir, dirn, dirp; // direction
2398 if (get(cave, x, y) >= O_FIREFLY_1 &&
2399 get(cave, x, y) <= O_FIREFLY_4)
2401 else if (get(cave, x, y) >= O_BUTTER_1 &&
2402 get(cave, x, y) <= O_BUTTER_4)
2404 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2405 get(cave, x, y) <= O_STONEFLY_4)
2406 base = O_STONEFLY_1;
2407 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2408 get(cave, x, y) <= O_ALT_FIREFLY_4)
2409 base = O_ALT_FIREFLY_1;
2410 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2411 get(cave, x, y) <= O_ALT_BUTTER_4)
2412 base = O_ALT_BUTTER_1;
2414 dir = get(cave, x, y) - base; // facing where
2415 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2417 // now change direction if backwards
2418 if (cave->creatures_backwards)
2423 dirn = (dir + 3) & 3; // fast turn
2424 dirp = (dir + 1) & 3; // slow turn
2428 dirn = (dir + 1) & 3; // fast turn
2429 dirp = (dir + 3) & 3; // slow turn
2432 if (is_space_dir(cave, x, y, creature_move[dirn]))
2433 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2434 else if (is_space_dir(cave, x, y, creature_move[dir]))
2435 move(cave, x, y, creature_move[dir], base + dir); // go on
2437 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2441 case O_WAITING_STONE:
2442 if (is_space_dir(cave, x, y, grav_compat))
2444 // beginning to fall
2446 move(cave, x, y, grav_compat, O_CHASING_STONE);
2448 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2450 // rolling down a brick wall or a stone
2451 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2452 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2453 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2455 // maybe rolling left - see case O_STONE to understand why we use cw_fourth here
2456 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2458 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2459 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2460 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2463 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2468 case O_CHASING_STONE:
2470 int px = cave->px[0];
2471 int py = cave->py[0];
2472 boolean horizontal = gd_rand_boolean(cave->random);
2473 boolean dont_move = FALSE;
2481 // ------------------------------------------------------------
2482 // check for a horizontal movement
2483 // ------------------------------------------------------------
2486 // if coordinates are the same
2488 horizontal = !horizontal;
2495 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2497 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2501 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2503 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2512 horizontal = !horizontal;
2520 // ------------------------------------------------------------
2521 // check for a vertical movement
2522 // ------------------------------------------------------------
2525 // if coordinates are the same
2527 horizontal = !horizontal;
2533 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2535 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2539 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2541 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2550 horizontal = !horizontal;
2563 // if we should move in both directions, but can not move in any, stop.
2568 // check for horizontal
2571 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2572 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2573 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2574 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2575 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2576 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2580 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2581 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2582 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2583 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2584 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2585 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2590 // check for vertical
2593 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2594 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2595 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2596 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2597 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2598 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2602 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2603 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2604 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2605 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2606 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2607 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2615 if (cave->replicators_wait_frame == 0 &&
2616 cave->replicators_active &&
2617 !cave->gravity_disabled)
2619 // only replicate, if space is under it.
2620 // do not replicate players!
2621 // also obeys gravity settings.
2622 // only replicate element if it is not a scanned one
2623 // do not replicate space... that condition looks like it
2624 // makes no sense, but otherwise it generates SCANNED spaces,
2625 // which cannot be "collected" by the player, so he cannot run
2626 // under a replicator
2627 if (is_space_dir(cave, x, y, cave->gravity) &&
2628 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2629 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2631 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2632 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2641 if (cave->biters_wait_frame == 0)
2643 static GdDirection biter_move[] =
2651 // direction, last two bits 0..3
2652 int dir = get(cave, x, y) - O_BITER_1;
2653 int dirn = (dir + 3) & 3;
2654 int dirp = (dir + 1) & 3;
2656 GdElement made_sound_of = O_NONE;
2658 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2660 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2662 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2663 if (biter_try[i] != O_SPACE)
2664 made_sound_of = O_BITER_1; // sound of a biter eating
2667 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2669 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2670 if (biter_try[i] != O_SPACE)
2671 made_sound_of = O_BITER_1; // sound of a biter eating
2674 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2676 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2677 if (biter_try[i] != O_SPACE)
2678 made_sound_of = O_BITER_1; // sound of a biter eating
2683 if (i == ARRAY_SIZE(biter_try))
2684 // i = number of elements in array: could not move, so just turn
2685 store(cave, x, y, O_BITER_1 + dirp);
2686 else if (biter_try[i] == O_STONE)
2688 // if there was a stone there, where we moved...
2689 // do not eat stones, just throw them back
2690 store(cave, x, y, O_STONE);
2691 made_sound_of = O_STONE;
2694 // if biter did move, we had sound. play it.
2695 if (made_sound_of != O_NONE)
2696 play_sound_of_element(cave, made_sound_of, x, y);
2704 // check if touches a voodoo
2705 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2706 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2707 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2708 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2709 cave->voodoo_touched = TRUE;
2711 // check if touches something bad and should explode (includes voodoo by the flags)
2712 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2713 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2714 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2715 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2716 explode (cave, x, y);
2720 const GdDirection *creature_move;
2721 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2722 GdElement base = O_DRAGONFLY_1; // base element number (which is like O_***_1)
2723 int dir, dirn; // direction
2725 dir = get(cave, x, y)-base; // facing where
2726 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2728 // now change direction if backwards
2729 if (cave->creatures_backwards)
2733 dirn = (dir + 3) & 3; // fast turn
2735 dirn = (dir + 1) & 3; // fast turn
2737 // if can move forward, does so.
2738 if (is_space_dir(cave, x, y, creature_move[dir]))
2739 move(cave, x, y, creature_move[dir], base + dir);
2741 // otherwise turns 90 degrees in place.
2742 store(cave, x, y, base + dirn);
2747 store(cave, x, y, O_BLADDER_1);
2758 // bladder with any delay state: try to convert to clock.
2759 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2760 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))
2762 // if touches the specified element, let it be a clock
2763 store(cave, x, y, O_PRE_CLOCK_1);
2765 // plays the bladder convert sound
2766 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2770 // is space over the bladder?
2771 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2773 if (get(cave, x, y) == O_BLADDER_8)
2775 // if it is a bladder 8, really move up
2776 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2777 play_sound_of_element(cave, O_BLADDER, x, y);
2780 // if smaller delay, just increase delay.
2784 // if not space, is something sloped over the bladder?
2785 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2786 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2788 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2789 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2790 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2792 // rolling up, to left
2793 if (get(cave, x, y) == O_BLADDER_8)
2795 // if it is a bladder 8, really roll
2796 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2797 play_sound_of_element(cave, O_BLADDER, x, y);
2800 // if smaller delay, just increase delay.
2803 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2804 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2805 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2807 // rolling up, to left
2808 if (get(cave, x, y) == O_BLADDER_8)
2810 // if it is a bladder 8, really roll
2811 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2812 play_sound_of_element(cave, O_BLADDER, x, y);
2815 // if smaller delay, just increase delay.
2820 // no space, no sloped thing over it - store bladder 1 and that is for now.
2822 store(cave, x, y, O_BLADDER_1);
2827 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2828 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2829 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2830 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2831 explode (cave, x, y);
2836 // the ghost is given four possibilities to move.
2837 for (i = 0; i < 4; i++)
2839 static GdDirection dirs[] =
2846 GdDirection random_dir;
2848 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2849 if (is_space_dir(cave, x, y, random_dir))
2851 move(cave, x, y, random_dir, O_GHOST);
2852 break; // ghost did move -> exit loop
2858 // ============================================================================
2859 // A C T I V E E L E M E N T S
2860 // ============================================================================
2863 // emulating BD1 amoeba+magic wall bug
2864 if (cave->convert_amoeba_this_frame && amoeba_found_enclosed)
2866 store(cave, x, y, cave->amoeba_enclosed_effect);
2871 switch (cave->amoeba_state)
2874 store(cave, x, y, cave->amoeba_too_big_effect);
2877 case GD_AM_ENCLOSED:
2878 store(cave, x, y, cave->amoeba_enclosed_effect);
2881 case GD_AM_SLEEPING:
2883 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2884 if (amoeba_found_enclosed)
2885 // if still found enclosed, check all four directions, if this one is able to grow.
2886 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2887 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2888 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2889 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2891 // not enclosed. this is a local (per scan) flag!
2892 amoeba_found_enclosed = FALSE;
2893 cave->amoeba_state = GD_AM_AWAKE;
2896 // if alive, check in which dir to grow (or not)
2897 if (cave->amoeba_state == GD_AM_AWAKE)
2899 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2901 switch (gd_rand_int_range(cave->random, 0, 4))
2903 // decided to grow, choose a random direction.
2904 case 0: // let this be up. numbers indifferent.
2905 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2906 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2910 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2911 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2915 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2916 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2920 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2921 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2933 // check if it is touching an amoeba, and explosion is enabled
2934 if (cave->amoeba_2_explodes_by_amoeba &&
2935 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2936 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2937 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2938 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2939 explode (cave, x, y);
2941 switch (cave->amoeba_2_state)
2944 store(cave, x, y, cave->amoeba_2_too_big_effect);
2947 case GD_AM_ENCLOSED:
2948 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2951 case GD_AM_SLEEPING:
2953 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2954 if (amoeba_2_found_enclosed)
2955 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2956 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2957 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2958 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2960 // not enclosed. this is a local (per scan) flag!
2961 amoeba_2_found_enclosed = FALSE;
2962 cave->amoeba_2_state = GD_AM_AWAKE;
2965 // if it is alive, decide if it attempts to grow
2966 if (cave->amoeba_2_state == GD_AM_AWAKE)
2967 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2969 switch (gd_rand_int_range(cave->random, 0, 4))
2971 // decided to grow, choose a random direction.
2972 case 0: // let this be up. numbers indifferent.
2973 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2974 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2978 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2979 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2983 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2984 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2988 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2989 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2999 // choose randomly, if it spreads
3000 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
3002 // the current one explodes
3003 store(cave, x, y, cave->acid_turns_to);
3005 // and if neighbours are eaten, put acid there.
3006 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
3008 play_sound_of_element(cave, O_ACID, x, y);
3009 store_dir(cave, x, y, GD_MV_UP, O_ACID);
3012 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
3014 play_sound_of_element(cave, O_ACID, x, y);
3015 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
3018 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
3020 play_sound_of_element(cave, O_ACID, x, y);
3021 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
3024 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
3026 play_sound_of_element(cave, O_ACID, x, y);
3027 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
3034 if (!cave->water_does_not_flow_down &&
3035 is_space_dir(cave, x, y, GD_MV_DOWN))
3036 // emulating the odd behaviour in crdr
3037 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
3039 if (is_space_dir(cave, x, y, GD_MV_UP))
3040 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
3042 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3043 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
3045 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3046 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
3050 store(cave, x, y, O_WATER);
3053 case O_H_EXPANDING_WALL:
3054 case O_V_EXPANDING_WALL:
3055 case O_H_EXPANDING_STEEL_WALL:
3056 case O_V_EXPANDING_STEEL_WALL:
3057 // checks first if direction is changed.
3058 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
3059 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
3060 !cave->expanding_wall_changed) ||
3061 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
3062 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
3063 cave->expanding_wall_changed))
3065 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3067 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3068 play_sound_of_element(cave, get(cave, x, y), x, y);
3071 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3072 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3073 play_sound_of_element(cave, get(cave, x, y), x, y);
3078 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3079 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3080 play_sound_of_element(cave, get(cave, x, y), x, y);
3083 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3084 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3085 play_sound_of_element(cave, get(cave, x, y), x, y);
3090 case O_EXPANDING_WALL:
3091 case O_EXPANDING_STEEL_WALL:
3092 // the wall which grows in all four directions.
3093 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3095 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3096 play_sound_of_element(cave, get(cave, x, y), x, y);
3099 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3100 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3101 play_sound_of_element(cave, get(cave, x, y), x, y);
3104 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3105 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3106 play_sound_of_element(cave, get(cave, x, y), x, y);
3109 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3110 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3111 play_sound_of_element(cave, get(cave, x, y), x, y);
3117 ; // to make compilers happy ...
3119 Info("Step[%03d]", cave->frame); // XXX
3121 int rrr = gd_cave_c64_random(cave);
3124 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
3125 (rrr & cave->slime_permeability_c64) == 0);
3128 * unpredictable: gd_rand_int
3129 * predictable: c64 predictable random generator.
3130 * for predictable, a random number is generated,
3131 * whether or not it is even possible that the stone will be able to pass.
3133 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
3135 GdDirection grav = cave->gravity;
3136 GdDirection oppos = opposite[cave->gravity];
3138 // space under the slime? elements may pass from top to bottom then.
3139 if (is_space_dir(cave, x, y, grav))
3141 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3143 // output a falling xy under
3144 store_dir(cave, x, y, grav, cave->slime_converts_1);
3146 store_dir(cave, x, y, oppos, O_SPACE);
3147 play_sound_of_element(cave, O_SLIME, x, y);
3149 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3151 store_dir(cave, x, y, grav, cave->slime_converts_2);
3152 store_dir(cave, x, y, oppos, O_SPACE);
3153 play_sound_of_element(cave, O_SLIME, x, y);
3155 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3157 store_dir(cave, x, y, grav, cave->slime_converts_3);
3158 store_dir(cave, x, y, oppos, O_SPACE);
3159 play_sound_of_element(cave, O_SLIME, x, y);
3161 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3163 // waiting stones pass without awakening
3164 store_dir(cave, x, y, grav, O_WAITING_STONE);
3165 store_dir(cave, x, y, oppos, O_SPACE);
3166 play_sound_of_element(cave, O_SLIME, x, y);
3168 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3170 // chasing stones pass
3171 store_dir(cave, x, y, grav, O_CHASING_STONE);
3172 store_dir(cave, x, y, oppos, O_SPACE);
3173 play_sound_of_element(cave, O_SLIME, x, y);
3178 // or space over the slime? elements may pass from bottom to up then.
3179 if (is_space_dir(cave, x, y, oppos))
3181 if (get_dir(cave, x, y, grav) == O_BLADDER)
3183 // bladders move UP the slime
3184 store_dir(cave, x, y, grav, O_SPACE);
3185 store_dir(cave, x, y, oppos, O_BLADDER_1);
3186 play_sound_of_element(cave, O_SLIME, x, y);
3188 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3190 store_dir(cave, x, y, grav, O_SPACE);
3191 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3192 play_sound_of_element(cave, O_SLIME, x, y);
3194 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3196 store_dir(cave, x, y, grav, O_SPACE);
3197 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3198 play_sound_of_element(cave, O_SLIME, x, y);
3205 case O_FALLING_WALL:
3206 if (is_space_dir(cave, x, y, grav_compat))
3208 // try falling if space under.
3211 for (yy = y + 1; yy < y + cave->h; yy++)
3212 // yy < y + cave->h is to check everything OVER the wall - since caves wrap around !!
3213 if (get(cave, x, yy) != O_SPACE)
3214 // stop cycle when other than space
3217 // if scanning stopped by a player... start falling!
3218 if (get(cave, x, yy) == O_PLAYER ||
3219 get(cave, x, yy) == O_PLAYER_GLUED ||
3220 get(cave, x, yy) == O_PLAYER_BOMB)
3222 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3223 // no sound when the falling wall starts falling!
3228 case O_FALLING_WALL_F:
3229 switch (get_dir(cave, x, y, grav_compat))
3232 case O_PLAYER_GLUED:
3234 // if player under, it explodes - the falling wall, not the player!
3235 explode(cave, x, y);
3240 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3245 play_sound_of_element(cave, get(cave, x, y), x, y);
3246 store(cave, x, y, O_FALLING_WALL);
3251 // ============================================================================
3252 // C O N V E Y O R B E L T S
3253 // ============================================================================
3255 case O_CONVEYOR_RIGHT:
3256 case O_CONVEYOR_LEFT:
3257 // only works if gravity is up or down!!!
3258 // first, check for gravity and running belts.
3259 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3261 const GdDirection *dir;
3265 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3266 if (cave->conveyor_belts_direction_changed)
3268 dir = left ? ccw_eighth : cw_eighth;
3270 // CHECK IF IT CONVEYS THE ELEMENT ABOVE IT
3271 // if gravity is normal, and the conveyor belt has something
3272 // ABOVE which can be moved
3274 // the gravity is up, so anything that should float now goes
3275 // DOWN and touches the conveyor
3276 if ((cave->gravity == GD_MV_DOWN &&
3277 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3278 (cave->gravity == GD_MV_UP &&
3279 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3281 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3282 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3284 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move
3285 store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space.
3289 // CHECK IF IT CONVEYS THE ELEMENT BELOW IT
3290 if ((cave->gravity == GD_MV_UP &&
3291 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3292 (cave->gravity == GD_MV_DOWN &&
3293 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3295 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3296 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3298 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move
3299 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear.
3305 // ============================================================================
3307 // ============================================================================
3310 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3311 move(cave, x, y, GD_MV_RIGHT, O_ROCKET_1);
3313 explode(cave, x, y);
3317 if (is_space_dir(cave, x, y, GD_MV_UP))
3318 move(cave, x, y, GD_MV_UP, O_ROCKET_2);
3320 explode(cave, x, y);
3324 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3325 move(cave, x, y, GD_MV_LEFT, O_ROCKET_3);
3327 explode(cave, x, y);
3331 if (is_space_dir(cave, x, y, GD_MV_DOWN))
3332 move(cave, x, y, GD_MV_DOWN, O_ROCKET_4);
3334 explode(cave, x, y);
3337 // ============================================================================
3338 // S I M P L E C H A N G I N G; E X P L O S I O N S
3339 // ============================================================================
3342 store(cave, x, y, cave->explosion_effect);
3346 store(cave, x, y, O_DIAMOND);
3350 store(cave, x, y, cave->diamond_birth_effect);
3354 store(cave, x, y, O_STONE);
3357 case O_NITRO_EXPL_4:
3358 store(cave, x, y, cave->nitro_explosion_effect);
3362 store(cave, x, y, cave->bomb_explosion_effect);
3365 case O_AMOEBA_2_EXPL_4:
3366 store(cave, x, y, cave->amoeba_2_explosion_effect);
3369 case O_GHOST_EXPL_4:
3371 static GdElement ghost_explode[] =
3373 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3374 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3375 O_WAITING_STONE, O_BITER_1
3378 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3383 store(cave, x, y, O_STEEL);
3387 store(cave, x, y, O_CLOCK);
3391 explode(cave, x, y);
3394 case O_TRAPPED_DIAMOND:
3395 if (cave->diamond_key_collected)
3396 store(cave, x, y, O_DIAMOND);
3400 if (cave->gate_open) // if no more diamonds needed
3401 store(cave, x, y, O_OUTBOX); // open outbox
3404 case O_PRE_INVIS_OUTBOX:
3405 if (cave->gate_open) // if no more diamonds needed
3406 store(cave, x, y, O_INVIS_OUTBOX); // open outbox. invisible one :P
3410 if (cave->hatched && !inbox_toggle) // if it is time of birth
3411 store(cave, x, y, O_PRE_PL_1);
3412 inbox_toggle = !inbox_toggle;
3416 store(cave, x, y, O_PLAYER);
3441 case O_GHOST_EXPL_1:
3442 case O_GHOST_EXPL_2:
3443 case O_GHOST_EXPL_3:
3453 case O_NITRO_EXPL_1:
3454 case O_NITRO_EXPL_2:
3455 case O_NITRO_EXPL_3:
3456 case O_AMOEBA_2_EXPL_1:
3457 case O_AMOEBA_2_EXPL_2:
3458 case O_AMOEBA_2_EXPL_3:
3459 // simply the next identifier
3478 found_water = TRUE; // for sound
3479 // simply the next identifier
3483 case O_BLADDER_SPENDER:
3484 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3486 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3487 store(cave, x, y, O_PRE_STEEL_1);
3488 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3493 // other inanimate elements that do nothing
3500 // ============================================================================
3502 // ============================================================================
3504 // another scan-like routine:
3505 // short explosions (for example, in bd1) started with explode_2.
3506 // internally we use explode_1; and change it to explode_2 if needed.
3507 if (cave->short_explosions)
3509 for (y = 0; y < cave->h; y++)
3511 for (x = 0; x < cave->w; x++)
3513 if (is_first_stage_of_explosion(cave, x, y))
3515 next(cave, x, y); // select next frame of explosion
3517 // forget scanned flag immediately
3518 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3524 // finally: forget "scanned" flags for objects.
3525 // also, check for time penalties.
3526 // these is something like an effect table, but we do not really use one.
3527 for (y = 0; y < cave->h; y++)
3529 for (x = 0; x < cave->w; x++)
3531 if (get(cave, x, y) & SCANNED)
3532 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3534 if (get(cave, x, y) == O_TIME_PENALTY)
3536 store(cave, x, y, O_GRAVESTONE);
3538 // there is time penalty for destroying the voodoo
3539 time_decrement_sec += cave->time_penalty;
3544 // this loop finds the coordinates of the player. needed for scrolling and chasing stone.
3545 // but we only do this, if a living player was found. if not yet, the setup
3546 // routine coordinates are used
3547 if (cave->player_state == GD_PL_LIVING)
3549 if (cave->active_is_first_found)
3551 // to be 1stb compatible, we do everything backwards.
3552 for (y = cave->h - 1; y >= 0; y--)
3554 for (x = cave->w - 1; x >= 0; x--)
3556 if (is_player(cave, x, y))
3558 // here we remember the coordinates.
3567 // as in the original: look for the last one
3568 for (y = 0; y < cave->h; y++)
3570 for (x = 0; x < cave->w; x++)
3572 if (is_player(cave, x, y))
3574 // here we remember the coordinates.
3583 // record coordinates of player for chasing stone
3584 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3586 cave->px[i] = cave->px[i + 1];
3587 cave->py[i] = cave->py[i + 1];
3590 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3591 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3595 // update timing calculated by iterating and counting elements
3596 update_cave_speed(cave);
3598 // cave 3 sounds. precedence is controlled by the sound_play function.
3599 // but we have to check amoeba&magic together as they had a different gritty sound when mixed
3601 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3603 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3604 cave->magic_wall_sound);
3606 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3607 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3608 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3610 if (amoeba_sound && magic_sound)
3612 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3617 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3618 else if (magic_sound)
3619 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3624 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3625 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3626 play_sound_of_element(cave, O_AMOEBA, x, y);
3629 // pneumatic hammer sound - overrides everything.
3630 if (cave->pneumatic_hammer_active_delay > 0)
3631 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3634 // ============================================================================
3636 // ============================================================================
3640 // check if player is alive.
3641 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
3642 cave->player_state = GD_PL_DIED;
3644 // check if any voodoo exploded, and kill players the next scan if that happended.
3645 if (cave->voodoo_touched)
3646 cave->kill_player = TRUE;
3650 // check flags after evaluating.
3651 if (cave->amoeba_state == GD_AM_AWAKE)
3653 if (amoeba_count >= cave->amoeba_max_count)
3654 cave->amoeba_state = GD_AM_TOO_BIG;
3655 if (amoeba_found_enclosed)
3656 cave->amoeba_state = GD_AM_ENCLOSED;
3659 // amoeba can also be turned into diamond by magic wall
3660 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3661 cave->amoeba_state = GD_AM_ENCLOSED;
3664 if (cave->amoeba_2_state == GD_AM_AWAKE)
3666 // check flags after evaluating.
3667 if (amoeba_2_count >= cave->amoeba_2_max_count)
3668 cave->amoeba_2_state = GD_AM_TOO_BIG;
3670 if (amoeba_2_found_enclosed)
3671 cave->amoeba_2_state = GD_AM_ENCLOSED;
3674 // amoeba 2 can also be turned into diamond by magic wall
3675 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3676 cave->amoeba_2_state = GD_AM_ENCLOSED;
3678 // now check times. ---------------------------
3679 // decrement time if a voodoo was killed.
3680 cave->time -= time_decrement_sec * cave->timing_factor;
3684 // only decrement time when player is already born.
3687 int secondsbefore, secondsafter;
3689 secondsbefore = cave->time / cave->timing_factor;
3690 cave->time -= cave->speed;
3691 if (cave->time <= 0)
3694 secondsafter = cave->time / cave->timing_factor;
3695 if (cave->time / cave->timing_factor < 10)
3696 // if less than 10 seconds, no walking sound, but play explosion sound
3697 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3699 if (secondsbefore != secondsafter)
3700 gd_cave_set_seconds_sound(cave);
3703 // a gravity switch was activated; seconds counting down
3704 if (cave->gravity_will_change > 0)
3706 cave->gravity_will_change -= cave->speed;
3707 if (cave->gravity_will_change < 0)
3708 cave->gravity_will_change = 0;
3710 if (cave->gravity_will_change == 0)
3712 cave->gravity = cave->gravity_next_direction;
3713 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound
3717 // creatures direction automatically change
3718 if (cave->creatures_direction_will_change > 0)
3720 cave->creatures_direction_will_change -= cave->speed;
3721 if (cave->creatures_direction_will_change < 0)
3722 cave->creatures_direction_will_change = 0;
3724 if (cave->creatures_direction_will_change == 0)
3726 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3728 cave->creatures_backwards = !cave->creatures_backwards;
3729 cave->creatures_direction_will_change =
3730 cave->creatures_direction_auto_change_time * cave->timing_factor;
3734 // magic wall; if active&wait or not wait for hatching
3735 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3736 (cave->hatched || !cave->magic_timer_wait_for_hatching) &&
3737 !(cave->magic_wall_time == 0 && cave->magic_timer_zero_is_infinite))
3739 cave->magic_wall_time -= cave->speed;
3740 if (cave->magic_wall_time < 0)
3741 cave->magic_wall_time = 0;
3742 if (cave->magic_wall_time == 0)
3743 cave->magic_wall_state = GD_MW_EXPIRED;
3746 // we may wait for hatching, when starting amoeba
3747 if (cave->amoeba_timer_started_immediately ||
3748 (cave->amoeba_state == GD_AM_AWAKE &&
3749 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3751 cave->amoeba_time -= cave->speed;
3752 if (cave->amoeba_time < 0)
3753 cave->amoeba_time = 0;
3754 if (cave->amoeba_time == 0)
3755 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3758 // we may wait for hatching, when starting amoeba
3759 if (cave->amoeba_timer_started_immediately ||
3760 (cave->amoeba_2_state == GD_AM_AWAKE &&
3761 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3763 cave->amoeba_2_time -= cave->speed;
3764 if (cave->amoeba_2_time < 0)
3765 cave->amoeba_2_time = 0;
3766 if (cave->amoeba_2_time == 0)
3767 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3770 // check for player hatching.
3771 start_signal = FALSE;
3773 // if not the c64 scheduling, but the correct frametime is used,
3774 // hatching delay should always be decremented.
3775 // otherwise, the if (millisecs...) condition below will set this.
3776 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3778 // NON-C64 scheduling
3779 if (cave->hatching_delay_frame > 0)
3781 // for milliseconds-based, non-c64 schedulings, hatching delay means frames.
3782 cave->hatching_delay_frame--;
3783 if (cave->hatching_delay_frame == 0)
3784 start_signal = TRUE;
3790 if (cave->hatching_delay_time > 0)
3792 // for c64 schedulings, hatching delay means milliseconds.
3793 cave->hatching_delay_time -= cave->speed;
3794 if (cave->hatching_delay_time <= 0)
3796 cave->hatching_delay_time = 0;
3797 start_signal = TRUE;
3802 // if decremented hatching, and it became zero:
3805 // THIS IS THE CAVE START SIGNAL
3807 // record that now the cave is in its normal state
3808 cave->hatched = TRUE;
3810 // if diamonds needed is below zero, we count the available diamonds now.
3811 gd_cave_count_diamonds(cave);
3813 // setup direction auto change
3814 if (cave->creatures_direction_auto_change_time)
3816 cave->creatures_direction_will_change =
3817 cave->creatures_direction_auto_change_time * cave->timing_factor;
3819 if (cave->creatures_direction_auto_change_on_start)
3820 cave->creatures_backwards = !cave->creatures_backwards;
3823 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3827 if (cave->biters_wait_frame == 0)
3828 cave->biters_wait_frame = cave->biter_delay_frame;
3830 cave->biters_wait_frame--;
3832 // replicators delay
3833 if (cave->replicators_wait_frame == 0)
3834 cave->replicators_wait_frame = cave->replicator_delay_frame;
3836 cave->replicators_wait_frame--;
3839 // ============================================================================
3841 // ============================================================================
3844 // check if cave failed by timeout is done in main game engine
3846 // check if cave failed by timeout
3847 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3849 gd_cave_clear_sounds(cave);
3850 cave->player_state = GD_PL_TIMEOUT;
3851 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3855 // set these for drawing.
3856 cave->last_direction = player_move;
3858 // here we remember last movements for animation. this is needed here,
3859 // as animation is in sync with the game, not the keyboard directly.
3860 // (for example, after exiting the cave, the player was "running" in the
3861 // original, till bonus points were counted for remaining time and so on.
3862 if (player_move == GD_MV_LEFT ||
3863 player_move == GD_MV_UP_LEFT ||
3864 player_move == GD_MV_DOWN_LEFT)
3865 cave->last_horizontal_direction = GD_MV_LEFT;
3867 if (player_move == GD_MV_RIGHT ||
3868 player_move == GD_MV_UP_RIGHT ||
3869 player_move == GD_MV_DOWN_RIGHT)
3870 cave->last_horizontal_direction = GD_MV_RIGHT;
3872 cave->frame++; // XXX
3875 void set_initial_cave_speed(GdCave *cave)
3880 // set cave get function; to implement perfect or lineshifting borders
3881 if (cave->lineshift)
3882 cave->getp = getp_shift;
3884 cave->getp = getp_perfect;
3886 // check whether to scan the first and last line
3887 if (cave->border_scan_first_and_last)
3898 for (y = ymin; y <= ymax; y++)
3900 for (x = 0; x < cave->w; x++)
3902 // add the ckdelay correction value for every element seen.
3903 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3907 // update timing calculated by iterating and counting elements
3908 update_cave_speed(cave);