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 static inline 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 is explodable and explodes to space, for example the player
471 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
473 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
476 // returns true if the element is moved by the conveyor belt
477 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
478 const GdDirection dir)
480 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
483 // returns true if the element is moved by the conveyor belt
484 static inline boolean moved_by_conveyor_bottom_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_BOTTOM) != 0;
490 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
491 const GdDirection dir)
493 return (get_dir(cave, x, y, dir) & SCANNED) != 0;
496 // returns true if neighbouring element is "e"
497 // treats dirt specially
498 // treats lava specially
499 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
500 const GdDirection dir, GdElement e)
502 GdElement examined = get_dir(cave, x, y, dir);
504 // if it is a dirt-like, change to dirt, so equality will evaluate to true
505 if (gd_elements[examined & O_MASK].properties & P_DIRT)
508 if (gd_elements[e & O_MASK].properties & P_DIRT)
511 // if the element on the map is a lava, it should be like space
512 if (examined == O_LAVA)
515 return (e == examined);
518 // returns true if neighbouring element is space
519 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
520 const GdDirection dir)
522 GdElement e = get_dir(cave, x, y, dir) & O_MASK;
524 return (e == O_SPACE || e == O_LAVA);
527 static inline void store_dir_buffer(GdCave *cave, const int x, const int y, const GdDirection dir)
529 // raw values without range correction
530 int raw_x = x + gd_dx[dir];
531 int raw_y = y + gd_dy[dir];
533 // final values with range correction
534 int new_x = getx(cave, raw_x, raw_y);
535 int new_y = gety(cave, raw_x, raw_y);
536 int new_dir = (dir > GD_MV_TWICE ? dir - GD_MV_TWICE : dir);
538 game_bd.game->dir_buffer[new_y][new_x] = new_dir;
541 // store an element at the given position
542 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
544 GdElement *e = getp(cave, x, y);
548 play_sound_of_element(cave, O_LAVA, x, y);
556 // store an element with SCANNED flag turned on
557 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
559 store(cave, x, y, element | SCANNED);
562 // store an element to a neighbouring cell
563 static inline void store_dir(GdCave *cave, const int x, const int y,
564 const GdDirection dir, const GdElement element)
566 store_dir_buffer(cave, x, y, dir);
567 store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED);
570 // store an element to a neighbouring cell
571 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
572 const GdDirection dir, const GdElement element)
574 store_dir_buffer(cave, x, y, dir);
575 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
578 // move element to direction; then place space at x, y
579 static inline void move(GdCave *cave, const int x, const int y,
580 const GdDirection dir, const GdElement e)
582 store_dir(cave, x, y, dir, e);
583 store(cave, x, y, O_SPACE);
586 // increment a cave element; can be used for elements which are one after
587 // the other, for example bladder1, bladder2, bladder3...
588 static inline void next(GdCave *cave, const int x, const int y)
590 (*getp(cave, x, y))++;
593 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
595 if (non_explodable (cave, x, y))
598 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
599 cave->voodoo_touched = TRUE;
601 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
602 // voodoo turns into a time penalty
603 store_sc(cave, x, y, O_TIME_PENALTY);
604 else if (get(cave, x, y) == O_NITRO_PACK ||
605 get(cave, x, y) == O_NITRO_PACK_F)
606 // nitro pack inside an explosion - it is now triggered
607 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
609 // for everything else
610 store_sc(cave, x, y, explode_to);
613 // a creature explodes to a 3x3 something.
614 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
618 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
619 cave->ckdelay += 1200;
620 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
622 for (yy = y - 1; yy <= y + 1; yy++)
623 for (xx = x - 1; xx <= x + 1; xx++)
624 cell_explode(cave, xx, yy, explode_to);
627 static void nitro_explode(GdCave *cave, int x, int y)
631 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
632 cave->ckdelay += 1200;
633 gd_sound_play(cave, GD_S_NITRO_PACK_EXPLODING, get(cave, x, y), x, y);
635 for (yy = y - 1; yy <= y + 1; yy++)
636 for (xx = x - 1; xx <= x + 1; xx++)
637 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
639 // the current cell is explicitly changed into a nitro expl,
640 // as cell_explode changes it to a triggered nitro pack
641 store_sc(cave, x, y, O_NITRO_EXPL_1);
644 // a voodoo explodes, leaving a 3x3 steel and a time penalty behind.
645 static void voodoo_explode(GdCave *cave, int x, int y)
649 // the processing of an explosion took pretty much time: processing 3x3 = 9 elements
650 cave->ckdelay += 1000;
652 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
653 if (cave->voodoo_any_hurt_kills_player)
654 cave->voodoo_touched = TRUE;
656 // voodoo explodes to 3x3 steel
657 for (yy = y - 1; yy <= y + 1; yy++)
658 for (xx = x - 1; xx <= x + 1; xx++)
659 store_sc(cave, xx, yy, O_PRE_STEEL_1);
661 // middle is a time penalty (which will be turned into a gravestone)
662 store_sc(cave, x, y, O_TIME_PENALTY);
666 a bomb does not explode the voodoo, neither does the ghost.
667 this function check this, and stores the new element or not.
668 destroying the voodoo is also controlled by the
669 voodoo_disappear_in_explosion flag.
671 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
673 if (non_explodable (cave, x, y))
676 // bomb does not explode voodoo
677 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
680 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
681 cave->voodoo_touched = TRUE;
683 store_sc (cave, x, y, expl);
686 // X shaped ghost explosion; does not touch voodoo!
687 static void ghost_explode(GdCave *cave, const int x, const int y)
689 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
691 // the processing of an explosion took pretty much time: processing 5 elements
692 cave->ckdelay += 650;
694 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
695 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
696 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
697 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
698 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
701 // +shaped bomb explosion; does not touch voodoo!
702 static void bomb_explode(GdCave *cave, const int x, const int y)
704 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
706 // the processing of an explosion took pretty much time: processing 5 elements
707 cave->ckdelay += 650;
709 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
710 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
711 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
712 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
713 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
717 explode an element with the appropriate type of exlposion.
719 static void explode(GdCave *cave, int x, int y)
721 GdElement e = get(cave, x, y) & O_MASK;
726 ghost_explode(cave, x, y);
730 bomb_explode(cave, x, y);
734 voodoo_explode(cave, x, y);
739 case O_NITRO_PACK_EXPLODE:
740 nitro_explode(cave, x, y);
744 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
747 case O_FALLING_WALL_F:
748 creature_explode(cave, x, y, O_EXPLODE_1);
755 creature_explode(cave, x, y, cave->butterfly_explode_to);
762 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
769 creature_explode(cave, x, y, cave->firefly_explode_to);
772 case O_ALT_FIREFLY_1:
773 case O_ALT_FIREFLY_2:
774 case O_ALT_FIREFLY_3:
775 case O_ALT_FIREFLY_4:
776 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
782 case O_PLAYER_STIRRING:
783 case O_PLAYER_PNEUMATIC_LEFT:
784 case O_PLAYER_PNEUMATIC_RIGHT:
785 creature_explode(cave, x, y, O_EXPLODE_1);
792 creature_explode(cave, x, y, cave->stonefly_explode_to);
799 creature_explode(cave, x, y, cave->dragonfly_explode_to);
807 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
809 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
813 player eats specified object.
814 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
815 returns other element if something other appears there and he can't move.
816 cave pointer is needed to know the diamond values.
818 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
825 cave->diamond_key_collected = TRUE;
826 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
831 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
836 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
841 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
848 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
855 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
862 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
867 case O_CREATURE_SWITCH: // creatures change direction.
868 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
869 cave->creatures_backwards = !cave->creatures_backwards;
872 case O_EXPANDING_WALL_SWITCH: // expanding wall change direction.
873 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
874 cave->expanding_wall_changed = !cave->expanding_wall_changed;
877 case O_BITER_SWITCH: // biter change delay
878 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
879 cave->biter_delay_frame++;
880 if (cave->biter_delay_frame == 4)
881 cave->biter_delay_frame = 0;
884 case O_REPLICATOR_SWITCH: // replicator on/off switch
885 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
886 cave->replicators_active = !cave->replicators_active;
889 case O_CONVEYOR_SWITCH: // conveyor belts on/off
890 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
891 cave->conveyor_belts_active = !cave->conveyor_belts_active;
894 case O_CONVEYOR_DIR_SWITCH: // conveyor belts switch direction
895 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
896 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
902 case O_STEEL_EATABLE:
903 case O_BRICK_EATABLE:
904 case O_DIRT_SLOPED_UP_RIGHT:
905 case O_DIRT_SLOPED_UP_LEFT:
906 case O_DIRT_SLOPED_DOWN_LEFT:
907 case O_DIRT_SLOPED_DOWN_RIGHT:
910 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
914 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
915 cave->sweet_eaten = TRUE;
918 case O_PNEUMATIC_HAMMER:
919 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
920 cave->got_pneumatic_hammer = TRUE;
925 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
926 cave->time += cave->time_bonus * cave->timing_factor;
927 if (cave->time > cave->max_time * cave->timing_factor)
928 cave->time -= cave->max_time * cave->timing_factor;
929 // no space, rather a dirt remains there...
933 case O_FLYING_DIAMOND:
934 // prevent diamond sounds for O_SKELETON (see below)
935 if (x != -1 && y != -1)
936 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
937 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
939 cave->score += cave->diamond_value;
940 cave->diamonds_collected++;
942 if (cave->diamonds_needed == cave->diamonds_collected)
944 cave->gate_open = TRUE;
946 // extra is worth more points.
947 cave->diamond_value = cave->extra_diamond_value;
949 cave->gate_open_flash = 1;
950 cave->sound3 = GD_S_CRACKING;
951 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
956 cave->skeletons_collected++;
958 // as if player got a diamond
959 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
960 player_get_element(cave, O_DIAMOND, -1, -1);
962 // _after_ calling get_element for the fake diamonds, so we overwrite its sounds
963 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
968 cave->player_state = GD_PL_EXITED; // player now exits the cave!
972 case O_LAVA: // player goes into lava, as if it was space
973 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
977 // the object will remain there.
983 process a crazy dream-style teleporter.
984 called from gd_cave_iterate, for a player or a player_bomb.
985 player is standing at px, py, and trying to move in the direction player_move,
986 where there is a teleporter.
987 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
988 around). the first teleporter we find, and which is suitable, will be the destination.
989 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
991 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
1000 // jump to next element; wrap around columns and rows.
1012 // if we found a teleporter...
1013 if (get(cave, tx, ty) == O_TELEPORTER &&
1014 is_space_dir(cave, tx, ty, player_move))
1016 // new player appears near teleporter found
1017 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1019 // current player disappears
1020 store(cave, px, py, O_SPACE);
1022 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1024 return TRUE; // return true as teleporter worked
1027 // loop until we get back to original coordinates
1028 while (tx != px || ty != py);
1030 // return false as we did not find any usable teleporter
1035 try to push an element.
1036 returns true if the push is possible; also does move the specified _element_.
1037 up to the caller to move the _player_itself_.
1039 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1042 GdElement what = get_dir(cave, x, y, player_move);
1044 // gravity for falling wall, bladder, ...
1045 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1051 case O_WAITING_STONE:
1054 case O_CHASING_STONE:
1056 case O_FLYING_STONE:
1058 // pushing some kind of stone or nut
1059 // directions possible: 90degrees cw or ccw to current gravity.
1060 // only push if player dir is orthogonal to gravity,
1061 // ie. gravity down, pushing left & right possible
1062 if (player_move == ccw_fourth[cave->gravity] ||
1063 player_move == cw_fourth[cave->gravity])
1069 // different probabilities for different elements.
1072 case O_WAITING_STONE:
1073 // waiting stones are light, can always push
1077 case O_CHASING_STONE:
1078 // chasing can be pushed if player is turbo
1079 if (cave->sweet_eaten)
1084 // mega may(!) be pushed if player is turbo
1085 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1091 case O_FLYING_STONE:
1093 if (cave->sweet_eaten)
1094 prob = cave->pushing_stone_prob_sweet; // probability with sweet
1096 prob = cave->pushing_stone_prob; // probability without sweet.
1103 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1104 gd_rand_int_range(cave->random, 0, 1000000) < prob)
1106 // if decided that he will be able to push,
1107 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1108 play_sound_of_element_pushing(cave, what, x, y);
1123 // pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1124 // not an O_BLADDER_x.
1125 // there is no "delayed" state of a bladder, so we use store_dir_no_scanned!
1127 // first check: we cannot push a bladder "up"
1128 if (player_move != opposite[grav_compat])
1130 // pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check.
1131 // player moving in the direction of gravity.
1135 if (player_move == grav_compat)
1137 // pushing bladder down
1138 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1139 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1140 // if no space to push down, maybe left (down-left to player)
1141 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1143 // left is "down, turned right (cw)"
1144 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1145 // if not, maybe right (down-right to player)
1146 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1147 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1150 // pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check.
1154 else if (player_move == cw_fourth[grav_compat])
1156 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) // pushing it left
1157 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1158 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) // maybe down, and player will move left
1159 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1160 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) // maybe up, and player will move left
1161 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1164 // pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check.
1168 else if (player_move == ccw_fourth[grav_compat])
1170 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) // pushing it right
1171 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1172 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) // maybe down, and player will move right
1173 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1174 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) // maybe up, and player will move right
1175 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1179 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1184 // a box is only pushed with the fire pressed
1187 // but always with 100% probability
1188 switch (player_move)
1194 // pushing in some dir, two steps in that dir - is there space?
1195 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1198 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1200 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1205 // push in no other directions possible
1211 // pushing of other elements not possible
1219 // from the key press booleans, create a direction
1220 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1222 GdDirection player_move;
1224 // from the key press booleans, create a direction
1226 player_move = GD_MV_UP_RIGHT;
1227 else if (down && right)
1228 player_move = GD_MV_DOWN_RIGHT;
1229 else if (down && left)
1230 player_move = GD_MV_DOWN_LEFT;
1231 else if (up && left)
1232 player_move = GD_MV_UP_LEFT;
1234 player_move = GD_MV_UP;
1236 player_move = GD_MV_DOWN;
1238 player_move = GD_MV_LEFT;
1240 player_move = GD_MV_RIGHT;
1242 player_move = GD_MV_STILL;
1247 // clear these to no sound; and they will be set during iteration.
1248 void gd_cave_clear_sounds(GdCave *cave)
1250 cave->sound1 = GD_S_NONE;
1251 cave->sound2 = GD_S_NONE;
1252 cave->sound3 = GD_S_NONE;
1255 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1256 GdElement falling_element)
1258 if (cave->gravity_disabled)
1261 if (is_space_dir(cave, x, y, falling_direction))
1263 // beginning to fall
1264 play_sound_of_element(cave, get(cave, x, y), x, y);
1265 move(cave, x, y, falling_direction, falling_element);
1268 // check if it is on a sloped element, and it can roll.
1269 // for example, sloped wall looks like:
1272 // this is tagged as sloped up&left.
1273 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1274 // then check the direction to roll (left or right)
1275 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1276 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1278 // rolling down, if sitting on a sloped object
1279 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1280 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1281 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1283 // rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1284 // so here we use cw_fourth
1285 play_sound_of_element(cave, get(cave, x, y), x, y);
1286 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1288 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1289 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1290 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1293 play_sound_of_element(cave, get(cave, x, y), x, y);
1294 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1299 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1301 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1302 cave->voodoo_dies_by_stone)
1304 // this is a 1stB-style vodo. explodes by stone, collects diamonds
1305 explode_dir(cave, x, y, fall_dir);
1312 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1314 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1315 cave->voodoo_collects_diamonds)
1317 // this is a 1stB-style voodoo. explodes by stone, collects diamonds
1318 player_get_element(cave, O_DIAMOND, x, y); // as if player got diamond
1319 store(cave, x, y, O_SPACE); // diamond disappears
1326 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1327 GdDirection fall_dir, GdElement bouncing)
1329 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1330 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1333 store(cave, x, y, bouncing);
1334 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1336 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1344 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1345 GdDirection fall_dir, GdElement magic)
1347 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1349 play_sound_of_element(cave, O_DIAMOND, x, y); // always play diamond sound
1351 if (cave->magic_wall_state == GD_MW_DORMANT)
1352 cave->magic_wall_state = GD_MW_ACTIVE;
1354 if (cave->magic_wall_state == GD_MW_ACTIVE &&
1355 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1357 // if magic wall active and place underneath, it turns element
1358 // into anything the effect says to do.
1359 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1362 // active or non-active or anything, element falling in will always disappear
1363 store(cave, x, y, O_SPACE);
1371 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1373 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1375 explode_dir(cave, x, y, fall_dir);
1382 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1383 GdDirection fall_dir, GdElement bouncing)
1385 if (is_space_dir(cave, x, y, fall_dir))
1388 move(cave, x, y, fall_dir, get(cave, x, y));
1393 // check if it is on a sloped element, and it can roll.
1394 // for example, sloped wall looks like:
1397 // this is tagged as sloped up&left.
1398 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1399 // then check the direction to roll (left or right)
1400 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1402 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1404 // sloped element, falling to left or right
1405 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1406 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1407 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1409 play_sound_of_element(cave, get(cave, x, y), x, y);
1411 // try to roll left first - see O_STONE to understand why cw_fourth
1412 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1414 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1415 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1416 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1418 play_sound_of_element(cave, get(cave, x, y), x, y);
1420 // if not, try to roll right
1421 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1425 // cannot roll in any direction, so it stops
1426 play_sound_of_element(cave, get(cave, x, y), x, y);
1427 store(cave, x, y, bouncing);
1433 // any other element, stops
1434 play_sound_of_element(cave, get(cave, x, y), x, y);
1435 store(cave, x, y, bouncing);
1439 static void update_cave_speed(GdCave *cave)
1441 // update timing calculated by iterating and counting elements which were slow to process on c64
1442 switch (cave->scheduling)
1444 case GD_SCHEDULING_MILLISECONDS:
1445 // cave->speed already contains the milliseconds value, do not touch it
1448 case GD_SCHEDULING_BD1:
1449 if (!cave->intermission)
1450 // non-intermissions
1451 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1453 // intermissions were quicker, as only lines 1-12 were processed by the engine.
1454 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1457 case GD_SCHEDULING_BD1_ATARI:
1458 // about 20ms/frame faster than c64 version
1459 if (!cave->intermission)
1460 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); // non-intermissions
1462 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); // for intermissions
1465 case GD_SCHEDULING_BD2:
1467 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1470 case GD_SCHEDULING_PLCK:
1471 // 65 is totally empty cave in construction kit, with delay = 0)
1472 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1475 case GD_SCHEDULING_BD2_PLCK_ATARI:
1476 // a really fast engine; timing works like c64 plck.
1477 // 40 ms was measured in the construction kit, with delay = 0
1478 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1481 case GD_SCHEDULING_CRDR:
1482 if (cave->hammered_walls_reappear) // this made the engine very slow.
1483 cave->ckdelay += 60000;
1484 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1487 case GD_SCHEDULING_MAX:
1493 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1500 // amoeba found to be enclosed. if not, this is cleared
1501 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1503 // counting the number of amoebas. after scan, check if too much
1504 int amoeba_count, amoeba_2_count;
1506 // cave scan found water - for sound
1507 boolean found_water;
1509 boolean inbox_toggle;
1510 boolean start_signal;
1512 // gravity for falling wall, bladder, ...
1513 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1515 // directions for o_something_1, 2, 3 and 4 (creatures)
1516 static const GdDirection creature_dir[] =
1523 static const GdDirection creature_chdir[] =
1530 int time_decrement_sec;
1532 // biters eating elements preference, they try to go in this order
1533 GdElement biter_try[] =
1540 boolean amoeba_sound, magic_sound;
1542 gd_cave_clear_sounds(cave);
1544 // if diagonal movements not allowed,
1545 // horizontal movements have precedence. [BROADRIBB]
1546 if (!cave->diagonal_movements)
1548 switch (player_move)
1550 case GD_MV_UP_RIGHT:
1551 case GD_MV_DOWN_RIGHT:
1552 player_move = GD_MV_RIGHT;
1556 case GD_MV_DOWN_LEFT:
1557 player_move = GD_MV_LEFT;
1561 // no correction needed
1566 // set cave get function; to implement perfect or lineshifting borders
1567 if (cave->lineshift)
1569 cave->getp = getp_shift;
1570 cave->getx = getx_shift;
1571 cave->gety = gety_shift;
1575 cave->getp = getp_perfect;
1576 cave->getx = getx_perfect;
1577 cave->gety = gety_perfect;
1580 // increment this. if the scan routine comes across player, clears it (sets to zero).
1581 if (cave->player_seen_ago < 100)
1582 cave->player_seen_ago++;
1584 if (cave->pneumatic_hammer_active_delay > 0)
1585 cave->pneumatic_hammer_active_delay--;
1587 // inboxes and outboxes flash with the rhythm of the game, not the display.
1588 // also, a player can be born only from an open, not from a steel-wall-like inbox.
1589 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1590 inbox_toggle = cave->inbox_flash_toggle;
1592 if (cave->gate_open_flash > 0)
1593 cave->gate_open_flash--;
1595 // score collected this frame
1598 // suicide only kills the active player
1599 // player_x, player_y was set by the previous iterate routine, or the cave setup.
1600 // we must check if there is a player or not - he may have exploded or something like that
1601 if (suicide && cave->player_state == GD_PL_LIVING &&
1602 is_player(cave, cave->player_x, cave->player_y))
1603 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1605 // check for walls reappearing
1606 if (cave->hammered_reappear)
1608 for (y = 0; y < cave->h; y++)
1610 for (x = 0; x < cave->w; x++)
1612 // timer for the cell > 0?
1613 if (cave->hammered_reappear[y][x] > 0)
1616 cave->hammered_reappear[y][x]--;
1618 // check if it became zero
1619 if (cave->hammered_reappear[y][x] == 0)
1621 store(cave, x, y, O_BRICK);
1622 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1629 // variables to check during the scan
1631 // will be set to false if any of the amoeba is found free.
1632 amoeba_found_enclosed = TRUE;
1633 amoeba_2_found_enclosed = TRUE;
1636 found_water = FALSE;
1638 time_decrement_sec = 0;
1640 // check whether to scan the first and last line
1641 if (cave->border_scan_first_and_last)
1652 // the cave scan routine
1653 for (y = ymin; y <= ymax; y++)
1655 for (x = 0; x < cave->w; x++)
1657 // if we find a scanned element, change it to the normal one, and that's all.
1658 // this is required, for example for chasing stones, which have moved, always passing slime!
1659 if (get(cave, x, y) & SCANNED)
1661 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1666 // add the ckdelay correction value for every element seen.
1667 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1669 switch (get(cave, x, y))
1671 // ============================================================================
1673 // ============================================================================
1676 if (cave->kill_player)
1678 explode (cave, x, y);
1682 cave->player_seen_ago = 0;
1683 // bd4 intermission caves have many players. so if one of them has exited,
1684 // do not change the flag anymore. so this if () is needed
1685 if (cave->player_state != GD_PL_EXITED)
1686 cave->player_state = GD_PL_LIVING;
1688 // check for pneumatic hammer things
1689 // 1) press fire, 2) have pneumatic hammer 4) space on left or right
1690 // for hammer 5) stand on something
1691 if (player_fire && cave->got_pneumatic_hammer &&
1692 is_space_dir(cave, x, y, player_move) &&
1693 !is_space_dir(cave, x, y, GD_MV_DOWN))
1695 if (player_move == GD_MV_LEFT &&
1696 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1698 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1699 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1700 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1704 if (player_move == GD_MV_RIGHT &&
1705 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1707 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1708 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1709 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1714 if (player_move != GD_MV_STILL)
1716 // only do every check if he is not moving
1717 GdElement what = get_dir(cave, x, y, player_move);
1718 GdElement remains = what;
1721 // if we are 'eating' a teleporter, and the function returns true
1722 // (teleporting worked), break here
1723 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1726 // try to push element; if successful, break
1727 push = do_push(cave, x, y, player_move, player_fire);
1737 // if its a bomb, remember he now has one.
1738 // we do not change the "remains" and "what" variables,
1739 // so that part of the code will be ineffective
1740 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1741 store_dir(cave, x, y, player_move, O_SPACE);
1744 store(cave, x, y, O_PLAYER_BOMB);
1746 move(cave, x, y, player_move, O_PLAYER_BOMB);
1750 // we do not change the "remains" and "what" variables,
1751 // so that part of the code will be ineffective
1752 if (!player_fire && !cave->gravity_switch_active &&
1753 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1755 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1756 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1757 cave->gravity_disabled = TRUE;
1761 case O_GRAVITY_SWITCH:
1762 // (we cannot use player_get for this as it does not have player_move parameter)
1763 // only allow changing direction if the new dir is not diagonal
1764 if (cave->gravity_switch_active &&
1765 (player_move == GD_MV_LEFT ||
1766 player_move == GD_MV_RIGHT ||
1767 player_move == GD_MV_UP ||
1768 player_move == GD_MV_DOWN))
1770 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1771 cave->gravity_will_change =
1772 cave->gravity_change_time * cave->timing_factor;
1773 cave->gravity_next_direction = player_move;
1774 cave->gravity_switch_active = FALSE;
1779 // get element - process others.
1780 // if cannot get, player_get_element will return the same
1781 remains = player_get_element(cave, what, x, y);
1786 if (remains != what || remains == O_SPACE)
1788 // if anything changed, apply the change.
1790 // if snapping anything and we have snapping explosions set.
1791 // but these is not true for pushing.
1792 if (remains == O_SPACE && player_fire && !push)
1793 remains = cave->snap_element;
1795 if (remains != O_SPACE || player_fire)
1796 // if any other element than space, player cannot move.
1797 // also if pressing fire, will not move.
1798 store_dir(cave, x, y, player_move, remains);
1800 // if space remains there, the player moves.
1801 move(cave, x, y, player_move, O_PLAYER);
1807 // much simpler; cannot steal stones
1808 if (cave->kill_player)
1810 explode(cave, x, y);
1814 cave->player_seen_ago = 0;
1815 // bd4 intermission caves have many players. so if one of them has exited,
1816 // do not change the flag anymore. so this if () is needed
1817 if (cave->player_state != GD_PL_EXITED)
1818 cave->player_state = GD_PL_LIVING;
1820 if (player_move != GD_MV_STILL)
1822 // if the player does not move, nothing to do
1823 GdElement what = get_dir(cave, x, y, player_move);
1824 GdElement remains = what;
1828 // placing a bomb into empty space or dirt
1829 if (is_space_dir(cave, x, y, player_move) ||
1830 is_element_dir(cave, x, y, player_move, O_DIRT))
1832 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1834 // placed bomb, he is normal player again
1835 store(cave, x, y, O_PLAYER);
1836 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1841 // pushing and collecting
1842 // if we are 'eating' a teleporter, and the function returns true
1843 // (teleporting worked), break here
1844 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1847 // player fire is false...
1848 if (do_push(cave, x, y, player_move, FALSE))
1856 case O_GRAVITY_SWITCH:
1857 // (we cannot use player_get for this as it does not have
1858 // player_move parameter)
1859 // only allow changing direction if the new dir is not diagonal
1860 if (cave->gravity_switch_active &&
1861 (player_move == GD_MV_LEFT ||
1862 player_move == GD_MV_RIGHT ||
1863 player_move == GD_MV_UP ||
1864 player_move == GD_MV_DOWN))
1866 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1867 cave->gravity_will_change =
1868 cave->gravity_change_time * cave->timing_factor;
1869 cave->gravity_next_direction = player_move;
1870 cave->gravity_switch_active = FALSE;
1875 // get element. if cannot get, player_get_element will return the same
1876 remains = player_get_element (cave, what, x, y);
1881 // if element changed, OR there is space, move.
1882 if (remains != what || remains == O_SPACE)
1884 // if anything changed, apply the change.
1885 move(cave, x, y, player_move, O_PLAYER_BOMB);
1890 case O_PLAYER_STIRRING:
1891 if (cave->kill_player)
1893 explode(cave, x, y);
1897 // stirring sound, if no other walking sound or explosion
1898 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1900 cave->player_seen_ago = 0;
1901 // bd4 intermission caves have many players. so if one of them has exited,
1902 // do not change the flag anymore. so this if () is needed
1903 if (cave->player_state != GD_PL_EXITED)
1904 cave->player_state = GD_PL_LIVING;
1908 // player "exits" stirring the pot by pressing fire
1909 cave->gravity_disabled = FALSE;
1910 store(cave, x, y, O_PLAYER);
1911 cave->gravity_switch_active = TRUE;
1915 // player holding pneumatic hammer
1916 case O_PLAYER_PNEUMATIC_LEFT:
1917 case O_PLAYER_PNEUMATIC_RIGHT:
1918 // usual player stuff
1919 if (cave->kill_player)
1921 explode(cave, x, y);
1925 cave->player_seen_ago = 0;
1926 if (cave->player_state != GD_PL_EXITED)
1927 cave->player_state = GD_PL_LIVING;
1929 // if hammering time is up, becomes a normal player again.
1930 if (cave->pneumatic_hammer_active_delay == 0)
1931 store(cave, x, y, O_PLAYER);
1934 // the active pneumatic hammer itself
1935 case O_PNEUMATIC_ACTIVE_RIGHT:
1936 case O_PNEUMATIC_ACTIVE_LEFT:
1937 if (cave->pneumatic_hammer_active_delay == 0)
1941 // pneumatic hammer element disappears
1942 store(cave, x, y, O_SPACE);
1944 // which is the new element which appears after that one is hammered?
1945 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1947 // if there is a new element, display it
1948 // O_NONE might be returned, for example if the element being
1949 // hammered explodes during hammering (by a nearby explosion)
1950 if (new_elem != O_NONE)
1952 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1954 // and if walls reappear, remember it in array
1955 if (cave->hammered_walls_reappear)
1959 wall_y = (y + 1) % cave->h;
1960 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1966 // ============================================================================
1967 // S T O N E S, D I A M O N D S
1968 // ============================================================================
1970 case O_STONE: // standing stone
1971 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1974 case O_MEGA_STONE: // standing mega_stone
1975 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1978 case O_DIAMOND: // standing diamond
1979 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1982 case O_NUT: // standing nut
1983 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1986 case O_DIRT_BALL: // standing dirt ball
1987 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1990 case O_DIRT_LOOSE: // standing loose dirt
1991 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1994 case O_FLYING_STONE: // standing stone
1995 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1998 case O_FLYING_DIAMOND: // standing diamond
1999 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2002 // ============================================================================
2003 // 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
2004 // ============================================================================
2006 case O_DIRT_BALL_F: // falling dirt ball
2007 if (!cave->gravity_disabled)
2008 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2011 case O_DIRT_LOOSE_F: // falling loose dirt
2012 if (!cave->gravity_disabled)
2013 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2016 case O_STONE_F: // falling stone
2017 if (!cave->gravity_disabled)
2019 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2022 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2025 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2028 if (do_fall_try_crush(cave, x, y, cave->gravity))
2031 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2035 case O_MEGA_STONE_F: // falling mega
2036 if (!cave->gravity_disabled)
2038 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2041 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2044 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2047 if (do_fall_try_crush(cave, x, y, cave->gravity))
2050 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2054 case O_DIAMOND_F: // falling diamond
2055 if (!cave->gravity_disabled)
2057 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2060 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2063 if (do_fall_try_crush(cave, x, y, cave->gravity))
2066 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2070 case O_NUT_F: // falling nut
2071 if (!cave->gravity_disabled)
2073 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2076 if (do_fall_try_crush(cave, x, y, cave->gravity))
2079 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2083 case O_FLYING_STONE_F: // falling stone
2084 if (!cave->gravity_disabled)
2086 GdDirection fall_dir = opposite[cave->gravity];
2088 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2091 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2094 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2097 if (do_fall_try_crush(cave, x, y, fall_dir))
2100 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2104 case O_FLYING_DIAMOND_F: // falling diamond
2105 if (!cave->gravity_disabled)
2107 GdDirection fall_dir = opposite[cave->gravity];
2109 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2112 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2115 if (do_fall_try_crush(cave, x, y, fall_dir))
2118 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2122 // ============================================================================
2123 // N I T R O P A C K
2124 // ============================================================================
2126 case O_NITRO_PACK: // standing nitro pack
2127 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2130 case O_NITRO_PACK_F: // falling nitro pack
2131 if (!cave->gravity_disabled)
2133 if (is_space_dir(cave, x, y, cave->gravity)) // if space, falling further
2134 move(cave, x, y, cave->gravity, get(cave, x, y));
2135 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2137 // try magic wall; if true, function did the work
2139 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2141 // falling on a dirt, it does NOT explode - just stops at its place.
2142 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2143 store(cave, x, y, O_NITRO_PACK);
2146 // falling on any other element it explodes
2147 explode(cave, x, y);
2151 case O_NITRO_PACK_EXPLODE: // a triggered nitro pack
2152 explode(cave, x, y);
2155 // ============================================================================
2156 // C R E A T U R E S
2157 // ============================================================================
2163 // if cannot move in any direction, becomes an enclosed cow
2164 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2165 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2166 store(cave, x, y, O_COW_ENCLOSED_1);
2169 // THIS IS THE CREATURE MOVE thing copied.
2170 const GdDirection *creature_move;
2171 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2172 GdElement base; // base element number (which is like O_***_1)
2173 int dir, dirn, dirp; // direction
2177 dir = get(cave, x, y)-base; // facing where
2178 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2180 // now change direction if backwards
2181 if (cave->creatures_backwards)
2186 dirn = (dir + 3) & 3; // fast turn
2187 dirp = (dir + 1) & 3; // slow turn
2191 dirn = (dir + 1) & 3; // fast turn
2192 dirp = (dir + 3) & 3; // slow turn
2195 if (is_space_dir(cave, x, y, creature_move[dirn]))
2196 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2197 else if (is_space_dir(cave, x, y, creature_move[dir]))
2198 move(cave, x, y, creature_move[dir], base + dir); // go on
2200 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2204 // enclosed cows wait some time before turning to a skeleton
2205 case O_COW_ENCLOSED_1:
2206 case O_COW_ENCLOSED_2:
2207 case O_COW_ENCLOSED_3:
2208 case O_COW_ENCLOSED_4:
2209 case O_COW_ENCLOSED_5:
2210 case O_COW_ENCLOSED_6:
2211 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2212 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2213 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2214 is_space_dir(cave, x, y, GD_MV_DOWN))
2215 store(cave, x, y, O_COW_1);
2220 case O_COW_ENCLOSED_7:
2221 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2222 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2223 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2224 is_space_dir(cave, x, y, GD_MV_DOWN))
2225 store(cave, x, y, O_COW_1);
2227 store(cave, x, y, O_SKELETON);
2234 case O_ALT_FIREFLY_1:
2235 case O_ALT_FIREFLY_2:
2236 case O_ALT_FIREFLY_3:
2237 case O_ALT_FIREFLY_4:
2242 case O_ALT_BUTTER_1:
2243 case O_ALT_BUTTER_2:
2244 case O_ALT_BUTTER_3:
2245 case O_ALT_BUTTER_4:
2250 // check if touches a voodoo
2251 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2252 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2253 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2254 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2255 cave->voodoo_touched = TRUE;
2257 // check if touches something bad and should explode (includes voodoo by the flags)
2258 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2259 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2260 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2261 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2262 explode (cave, x, y);
2266 const GdDirection *creature_move;
2267 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2268 GdElement base = -1; // base element number (which is like O_***_1)
2269 int dir, dirn, dirp; // direction
2271 if (get(cave, x, y) >= O_FIREFLY_1 &&
2272 get(cave, x, y) <= O_FIREFLY_4)
2274 else if (get(cave, x, y) >= O_BUTTER_1 &&
2275 get(cave, x, y) <= O_BUTTER_4)
2277 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2278 get(cave, x, y) <= O_STONEFLY_4)
2279 base = O_STONEFLY_1;
2280 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2281 get(cave, x, y) <= O_ALT_FIREFLY_4)
2282 base = O_ALT_FIREFLY_1;
2283 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2284 get(cave, x, y) <= O_ALT_BUTTER_4)
2285 base = O_ALT_BUTTER_1;
2287 dir = get(cave, x, y) - base; // facing where
2288 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2290 // now change direction if backwards
2291 if (cave->creatures_backwards)
2296 dirn = (dir + 3) & 3; // fast turn
2297 dirp = (dir + 1) & 3; // slow turn
2301 dirn = (dir + 1) & 3; // fast turn
2302 dirp = (dir + 3) & 3; // slow turn
2305 if (is_space_dir(cave, x, y, creature_move[dirn]))
2306 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2307 else if (is_space_dir(cave, x, y, creature_move[dir]))
2308 move(cave, x, y, creature_move[dir], base + dir); // go on
2310 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2314 case O_WAITING_STONE:
2315 if (is_space_dir(cave, x, y, grav_compat))
2317 // beginning to fall
2319 move(cave, x, y, grav_compat, O_CHASING_STONE);
2321 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2323 // rolling down a brick wall or a stone
2324 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2325 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2326 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2328 // maybe rolling left - see case O_STONE to understand why we use cw_fourth here
2329 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2331 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2332 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2333 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2336 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2341 case O_CHASING_STONE:
2343 int px = cave->px[0];
2344 int py = cave->py[0];
2345 boolean horizontal = gd_rand_boolean(cave->random);
2346 boolean dont_move = FALSE;
2354 // ------------------------------------------------------------
2355 // check for a horizontal movement
2356 // ------------------------------------------------------------
2359 // if coordinates are the same
2361 horizontal = !horizontal;
2368 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2370 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2374 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2376 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2385 horizontal = !horizontal;
2393 // ------------------------------------------------------------
2394 // check for a vertical movement
2395 // ------------------------------------------------------------
2398 // if coordinates are the same
2400 horizontal = !horizontal;
2406 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2408 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2412 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2414 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2423 horizontal = !horizontal;
2436 // if we should move in both directions, but can not move in any, stop.
2441 // check for horizontal
2444 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2445 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2446 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2447 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2448 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2449 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2453 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2454 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2455 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2456 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2457 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2458 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2463 // check for vertical
2466 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2467 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2468 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2469 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2470 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2471 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2475 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2476 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2477 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2478 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2479 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2480 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2488 if (cave->replicators_wait_frame == 0 &&
2489 cave->replicators_active &&
2490 !cave->gravity_disabled)
2492 // only replicate, if space is under it.
2493 // do not replicate players!
2494 // also obeys gravity settings.
2495 // only replicate element if it is not a scanned one
2496 // do not replicate space... that condition looks like it
2497 // makes no sense, but otherwise it generates SCANNED spaces,
2498 // which cannot be "collected" by the player, so he cannot run
2499 // under a replicator
2500 if (is_space_dir(cave, x, y, cave->gravity) &&
2501 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2502 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2504 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2505 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2514 if (cave->biters_wait_frame == 0)
2516 static GdDirection biter_move[] =
2524 // direction, last two bits 0..3
2525 int dir = get(cave, x, y) - O_BITER_1;
2526 int dirn = (dir + 3) & 3;
2527 int dirp = (dir + 1) & 3;
2529 GdElement made_sound_of = O_NONE;
2531 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2533 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2535 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2536 if (biter_try[i] != O_SPACE)
2537 made_sound_of = O_BITER_1; // sound of a biter eating
2540 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2542 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2543 if (biter_try[i] != O_SPACE)
2544 made_sound_of = O_BITER_1; // sound of a biter eating
2547 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2549 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2550 if (biter_try[i] != O_SPACE)
2551 made_sound_of = O_BITER_1; // sound of a biter eating
2556 if (i == ARRAY_SIZE(biter_try))
2557 // i = number of elements in array: could not move, so just turn
2558 store(cave, x, y, O_BITER_1 + dirp);
2559 else if (biter_try[i] == O_STONE)
2561 // if there was a stone there, where we moved...
2562 // do not eat stones, just throw them back
2563 store(cave, x, y, O_STONE);
2564 made_sound_of = O_STONE;
2567 // if biter did move, we had sound. play it.
2568 if (made_sound_of != O_NONE)
2569 play_sound_of_element(cave, made_sound_of, x, y);
2577 // check if touches a voodoo
2578 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2579 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2580 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2581 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2582 cave->voodoo_touched = TRUE;
2584 // check if touches something bad and should explode (includes voodoo by the flags)
2585 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2586 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2587 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2588 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2589 explode (cave, x, y);
2593 const GdDirection *creature_move;
2594 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2595 GdElement base = O_DRAGONFLY_1; // base element number (which is like O_***_1)
2596 int dir, dirn; // direction
2598 dir = get(cave, x, y)-base; // facing where
2599 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2601 // now change direction if backwards
2602 if (cave->creatures_backwards)
2606 dirn = (dir + 3) & 3; // fast turn
2608 dirn = (dir + 1) & 3; // fast turn
2610 // if can move forward, does so.
2611 if (is_space_dir(cave, x, y, creature_move[dir]))
2612 move(cave, x, y, creature_move[dir], base + dir);
2614 // otherwise turns 90 degrees in place.
2615 store(cave, x, y, base + dirn);
2620 store(cave, x, y, O_BLADDER_1);
2631 // bladder with any delay state: try to convert to clock.
2632 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2633 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))
2635 // if touches the specified element, let it be a clock
2636 store(cave, x, y, O_PRE_CLOCK_1);
2638 // plays the bladder convert sound
2639 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2643 // is space over the bladder?
2644 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2646 if (get(cave, x, y) == O_BLADDER_8)
2648 // if it is a bladder 8, really move up
2649 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2650 play_sound_of_element(cave, O_BLADDER, x, y);
2653 // if smaller delay, just increase delay.
2657 // if not space, is something sloped over the bladder?
2658 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2659 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2661 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2662 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2663 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2665 // rolling up, to left
2666 if (get(cave, x, y) == O_BLADDER_8)
2668 // if it is a bladder 8, really roll
2669 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2670 play_sound_of_element(cave, O_BLADDER, x, y);
2673 // if smaller delay, just increase delay.
2676 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2677 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2678 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2680 // rolling up, to left
2681 if (get(cave, x, y) == O_BLADDER_8)
2683 // if it is a bladder 8, really roll
2684 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2685 play_sound_of_element(cave, O_BLADDER, x, y);
2688 // if smaller delay, just increase delay.
2693 // no space, no sloped thing over it - store bladder 1 and that is for now.
2695 store(cave, x, y, O_BLADDER_1);
2700 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2701 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2702 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2703 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2704 explode (cave, x, y);
2709 // the ghost is given four possibilities to move.
2710 for (i = 0; i < 4; i++)
2712 static GdDirection dirs[] =
2719 GdDirection random_dir;
2721 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2722 if (is_space_dir(cave, x, y, random_dir))
2724 move(cave, x, y, random_dir, O_GHOST);
2725 break; // ghost did move -> exit loop
2731 // ============================================================================
2732 // A C T I V E E L E M E N T S
2733 // ============================================================================
2737 switch (cave->amoeba_state)
2740 store(cave, x, y, cave->amoeba_too_big_effect);
2743 case GD_AM_ENCLOSED:
2744 store(cave, x, y, cave->amoeba_enclosed_effect);
2747 case GD_AM_SLEEPING:
2749 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2750 if (amoeba_found_enclosed)
2751 // if still found enclosed, check all four directions, if this one is able to grow.
2752 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2753 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2754 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2755 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2757 // not enclosed. this is a local (per scan) flag!
2758 amoeba_found_enclosed = FALSE;
2759 cave->amoeba_state = GD_AM_AWAKE;
2762 // if alive, check in which dir to grow (or not)
2763 if (cave->amoeba_state == GD_AM_AWAKE)
2765 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2767 switch (gd_rand_int_range(cave->random, 0, 4))
2769 // decided to grow, choose a random direction.
2770 case 0: // let this be up. numbers indifferent.
2771 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2772 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2776 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2777 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2781 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2782 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2786 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2787 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2799 // check if it is touching an amoeba, and explosion is enabled
2800 if (cave->amoeba_2_explodes_by_amoeba &&
2801 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2802 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2803 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2804 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2805 explode (cave, x, y);
2807 switch (cave->amoeba_2_state)
2810 store(cave, x, y, cave->amoeba_2_too_big_effect);
2813 case GD_AM_ENCLOSED:
2814 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2817 case GD_AM_SLEEPING:
2819 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2820 if (amoeba_2_found_enclosed)
2821 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2822 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2823 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2824 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2826 // not enclosed. this is a local (per scan) flag!
2827 amoeba_2_found_enclosed = FALSE;
2828 cave->amoeba_2_state = GD_AM_AWAKE;
2831 // if it is alive, decide if it attempts to grow
2832 if (cave->amoeba_2_state == GD_AM_AWAKE)
2833 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2835 switch (gd_rand_int_range(cave->random, 0, 4))
2837 // decided to grow, choose a random direction.
2838 case 0: // let this be up. numbers indifferent.
2839 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2840 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2844 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2845 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2849 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2850 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2854 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2855 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2865 // choose randomly, if it spreads
2866 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2868 // the current one explodes
2869 store(cave, x, y, cave->acid_turns_to);
2871 // and if neighbours are eaten, put acid there.
2872 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2874 play_sound_of_element(cave, O_ACID, x, y);
2875 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2878 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2880 play_sound_of_element(cave, O_ACID, x, y);
2881 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2884 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2886 play_sound_of_element(cave, O_ACID, x, y);
2887 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2890 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2892 play_sound_of_element(cave, O_ACID, x, y);
2893 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2900 if (!cave->water_does_not_flow_down &&
2901 is_space_dir(cave, x, y, GD_MV_DOWN))
2902 // emulating the odd behaviour in crdr
2903 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2905 if (is_space_dir(cave, x, y, GD_MV_UP))
2906 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2908 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2909 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2911 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2912 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2916 store(cave, x, y, O_WATER);
2919 case O_H_EXPANDING_WALL:
2920 case O_V_EXPANDING_WALL:
2921 case O_H_EXPANDING_STEEL_WALL:
2922 case O_V_EXPANDING_STEEL_WALL:
2923 // checks first if direction is changed.
2924 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2925 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2926 !cave->expanding_wall_changed) ||
2927 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
2928 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
2929 cave->expanding_wall_changed))
2931 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2933 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2934 play_sound_of_element(cave, get(cave, x, y), x, y);
2937 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2938 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2939 play_sound_of_element(cave, get(cave, x, y), x, y);
2944 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2945 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2946 play_sound_of_element(cave, get(cave, x, y), x, y);
2949 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2950 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2951 play_sound_of_element(cave, get(cave, x, y), x, y);
2956 case O_EXPANDING_WALL:
2957 case O_EXPANDING_STEEL_WALL:
2958 // the wall which grows in all four directions.
2959 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2961 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2962 play_sound_of_element(cave, get(cave, x, y), x, y);
2965 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2966 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2967 play_sound_of_element(cave, get(cave, x, y), x, y);
2970 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2971 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2972 play_sound_of_element(cave, get(cave, x, y), x, y);
2975 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2976 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2977 play_sound_of_element(cave, get(cave, x, y), x, y);
2983 ; // to make compilers happy ...
2985 Info("Step[%03d]", cave->frame); // XXX
2987 int rrr = gd_cave_c64_random(cave);
2990 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2991 (rrr & cave->slime_permeability_c64) == 0);
2994 * unpredictable: gd_rand_int
2995 * predictable: c64 predictable random generator.
2996 * for predictable, a random number is generated,
2997 * whether or not it is even possible that the stone will be able to pass.
2999 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
3001 GdDirection grav = cave->gravity;
3002 GdDirection oppos = opposite[cave->gravity];
3004 // space under the slime? elements may pass from top to bottom then.
3005 if (is_space_dir(cave, x, y, grav))
3007 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3009 // output a falling xy under
3010 store_dir(cave, x, y, grav, cave->slime_converts_1);
3012 store_dir(cave, x, y, oppos, O_SPACE);
3013 play_sound_of_element(cave, O_SLIME, x, y);
3015 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3017 store_dir(cave, x, y, grav, cave->slime_converts_2);
3018 store_dir(cave, x, y, oppos, O_SPACE);
3019 play_sound_of_element(cave, O_SLIME, x, y);
3021 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3023 store_dir(cave, x, y, grav, cave->slime_converts_3);
3024 store_dir(cave, x, y, oppos, O_SPACE);
3025 play_sound_of_element(cave, O_SLIME, x, y);
3027 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3029 // waiting stones pass without awakening
3030 store_dir(cave, x, y, grav, O_WAITING_STONE);
3031 store_dir(cave, x, y, oppos, O_SPACE);
3032 play_sound_of_element(cave, O_SLIME, x, y);
3034 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3036 // chasing stones pass
3037 store_dir(cave, x, y, grav, O_CHASING_STONE);
3038 store_dir(cave, x, y, oppos, O_SPACE);
3039 play_sound_of_element(cave, O_SLIME, x, y);
3044 // or space over the slime? elements may pass from bottom to up then.
3045 if (is_space_dir(cave, x, y, oppos))
3047 if (get_dir(cave, x, y, grav) == O_BLADDER)
3049 // bladders move UP the slime
3050 store_dir(cave, x, y, grav, O_SPACE);
3051 store_dir(cave, x, y, oppos, O_BLADDER_1);
3052 play_sound_of_element(cave, O_SLIME, x, y);
3054 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3056 store_dir(cave, x, y, grav, O_SPACE);
3057 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3058 play_sound_of_element(cave, O_SLIME, x, y);
3060 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3062 store_dir(cave, x, y, grav, O_SPACE);
3063 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3064 play_sound_of_element(cave, O_SLIME, x, y);
3071 case O_FALLING_WALL:
3072 if (is_space_dir(cave, x, y, grav_compat))
3074 // try falling if space under.
3077 for (yy = y + 1; yy < y + cave->h; yy++)
3078 // yy < y + cave->h is to check everything OVER the wall - since caves wrap around !!
3079 if (get(cave, x, yy) != O_SPACE)
3080 // stop cycle when other than space
3083 // if scanning stopped by a player... start falling!
3084 if (get(cave, x, yy) == O_PLAYER ||
3085 get(cave, x, yy) == O_PLAYER_GLUED ||
3086 get(cave, x, yy) == O_PLAYER_BOMB)
3088 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3089 // no sound when the falling wall starts falling!
3094 case O_FALLING_WALL_F:
3095 switch (get_dir(cave, x, y, grav_compat))
3098 case O_PLAYER_GLUED:
3100 // if player under, it explodes - the falling wall, not the player!
3101 explode(cave, x, y);
3106 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3111 play_sound_of_element(cave, get(cave, x, y), x, y);
3112 store(cave, x, y, O_FALLING_WALL);
3117 // ============================================================================
3118 // C O N V E Y O R B E L T S
3119 // ============================================================================
3121 case O_CONVEYOR_RIGHT:
3122 case O_CONVEYOR_LEFT:
3123 // only works if gravity is up or down!!!
3124 // first, check for gravity and running belts.
3125 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3127 const GdDirection *dir;
3131 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3132 if (cave->conveyor_belts_direction_changed)
3134 dir = left ? ccw_eighth : cw_eighth;
3136 // CHECK IF IT CONVEYS THE ELEMENT ABOVE IT
3137 // if gravity is normal, and the conveyor belt has something
3138 // ABOVE which can be moved
3140 // the gravity is up, so anything that should float now goes
3141 // DOWN and touches the conveyor
3142 if ((cave->gravity == GD_MV_DOWN &&
3143 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3144 (cave->gravity == GD_MV_UP &&
3145 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3147 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3148 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3150 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move
3151 store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space.
3155 // CHECK IF IT CONVEYS THE ELEMENT BELOW IT
3156 if ((cave->gravity == GD_MV_UP &&
3157 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3158 (cave->gravity == GD_MV_DOWN &&
3159 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3161 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3162 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3164 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move
3165 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear.
3171 // ============================================================================
3172 // S I M P L E C H A N G I N G; E X P L O S I O N S
3173 // ============================================================================
3176 store(cave, x, y, cave->explosion_effect);
3180 store(cave, x, y, O_DIAMOND);
3184 store(cave, x, y, cave->diamond_birth_effect);
3188 store(cave, x, y, O_STONE);
3191 case O_NITRO_EXPL_4:
3192 store(cave, x, y, cave->nitro_explosion_effect);
3196 store(cave, x, y, cave->bomb_explosion_effect);
3199 case O_AMOEBA_2_EXPL_4:
3200 store(cave, x, y, cave->amoeba_2_explosion_effect);
3203 case O_GHOST_EXPL_4:
3205 static GdElement ghost_explode[] =
3207 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3208 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3209 O_WAITING_STONE, O_BITER_1
3212 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3217 store(cave, x, y, O_STEEL);
3221 store(cave, x, y, O_CLOCK);
3225 explode(cave, x, y);
3228 case O_TRAPPED_DIAMOND:
3229 if (cave->diamond_key_collected)
3230 store(cave, x, y, O_DIAMOND);
3234 if (cave->gate_open) // if no more diamonds needed
3235 store(cave, x, y, O_OUTBOX); // open outbox
3238 case O_PRE_INVIS_OUTBOX:
3239 if (cave->gate_open) // if no more diamonds needed
3240 store(cave, x, y, O_INVIS_OUTBOX); // open outbox. invisible one :P
3244 if (cave->hatched && !inbox_toggle) // if it is time of birth
3245 store(cave, x, y, O_PRE_PL_1);
3246 inbox_toggle = !inbox_toggle;
3250 store(cave, x, y, O_PLAYER);
3275 case O_GHOST_EXPL_1:
3276 case O_GHOST_EXPL_2:
3277 case O_GHOST_EXPL_3:
3287 case O_NITRO_EXPL_1:
3288 case O_NITRO_EXPL_2:
3289 case O_NITRO_EXPL_3:
3290 case O_AMOEBA_2_EXPL_1:
3291 case O_AMOEBA_2_EXPL_2:
3292 case O_AMOEBA_2_EXPL_3:
3293 // simply the next identifier
3312 found_water = TRUE; // for sound
3313 // simply the next identifier
3317 case O_BLADDER_SPENDER:
3318 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3320 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3321 store(cave, x, y, O_PRE_STEEL_1);
3322 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3327 // other inanimate elements that do nothing
3334 // ============================================================================
3336 // ============================================================================
3338 // another scan-like routine:
3339 // short explosions (for example, in bd1) started with explode_2.
3340 // internally we use explode_1; and change it to explode_2 if needed.
3341 if (cave->short_explosions)
3343 for (y = 0; y < cave->h; y++)
3345 for (x = 0; x < cave->w; x++)
3347 if (is_first_stage_of_explosion(cave, x, y))
3349 next(cave, x, y); // select next frame of explosion
3351 // forget scanned flag immediately
3352 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3358 // finally: forget "scanned" flags for objects.
3359 // also, check for time penalties.
3360 // these is something like an effect table, but we do not really use one.
3361 for (y = 0; y < cave->h; y++)
3363 for (x = 0; x < cave->w; x++)
3365 if (get(cave, x, y) & SCANNED)
3366 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3368 if (get(cave, x, y) == O_TIME_PENALTY)
3370 store(cave, x, y, O_GRAVESTONE);
3372 // there is time penalty for destroying the voodoo
3373 time_decrement_sec += cave->time_penalty;
3378 // this loop finds the coordinates of the player. needed for scrolling and chasing stone.
3379 // but we only do this, if a living player was found. if not yet, the setup
3380 // routine coordinates are used
3381 if (cave->player_state == GD_PL_LIVING)
3383 if (cave->active_is_first_found)
3385 // to be 1stb compatible, we do everything backwards.
3386 for (y = cave->h - 1; y >= 0; y--)
3388 for (x = cave->w - 1; x >= 0; x--)
3390 if (is_player(cave, x, y))
3392 // here we remember the coordinates.
3401 // as in the original: look for the last one
3402 for (y = 0; y < cave->h; y++)
3404 for (x = 0; x < cave->w; x++)
3406 if (is_player(cave, x, y))
3408 // here we remember the coordinates.
3417 // record coordinates of player for chasing stone
3418 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3420 cave->px[i] = cave->px[i + 1];
3421 cave->py[i] = cave->py[i + 1];
3424 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3425 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3429 // update timing calculated by iterating and counting elements
3430 update_cave_speed(cave);
3432 // cave 3 sounds. precedence is controlled by the sound_play function.
3433 // but we have to check amoeba&magic together as they had a different gritty sound when mixed
3435 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3437 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3438 cave->magic_wall_sound);
3440 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3441 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3442 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3444 if (amoeba_sound && magic_sound)
3446 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3451 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3452 else if (magic_sound)
3453 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3458 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3459 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3460 play_sound_of_element(cave, O_AMOEBA, x, y);
3463 // pneumatic hammer sound - overrides everything.
3464 if (cave->pneumatic_hammer_active_delay > 0)
3465 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3468 // ============================================================================
3470 // ============================================================================
3474 // check if player is alive.
3475 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
3476 cave->player_state = GD_PL_DIED;
3478 // check if any voodoo exploded, and kill players the next scan if that happended.
3479 if (cave->voodoo_touched)
3480 cave->kill_player = TRUE;
3484 // check flags after evaluating.
3485 if (cave->amoeba_state == GD_AM_AWAKE)
3487 if (amoeba_count >= cave->amoeba_max_count)
3488 cave->amoeba_state = GD_AM_TOO_BIG;
3489 if (amoeba_found_enclosed)
3490 cave->amoeba_state = GD_AM_ENCLOSED;
3493 // amoeba can also be turned into diamond by magic wall
3494 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3495 cave->amoeba_state = GD_AM_ENCLOSED;
3498 if (cave->amoeba_2_state == GD_AM_AWAKE)
3500 // check flags after evaluating.
3501 if (amoeba_2_count >= cave->amoeba_2_max_count)
3502 cave->amoeba_2_state = GD_AM_TOO_BIG;
3504 if (amoeba_2_found_enclosed)
3505 cave->amoeba_2_state = GD_AM_ENCLOSED;
3508 // amoeba 2 can also be turned into diamond by magic wall
3509 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3510 cave->amoeba_2_state = GD_AM_ENCLOSED;
3512 // now check times. ---------------------------
3513 // decrement time if a voodoo was killed.
3514 cave->time -= time_decrement_sec * cave->timing_factor;
3518 // only decrement time when player is already born.
3521 int secondsbefore, secondsafter;
3523 secondsbefore = cave->time / cave->timing_factor;
3524 cave->time -= cave->speed;
3525 if (cave->time <= 0)
3528 secondsafter = cave->time / cave->timing_factor;
3529 if (cave->time / cave->timing_factor < 10)
3530 // if less than 10 seconds, no walking sound, but play explosion sound
3531 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3533 if (secondsbefore != secondsafter)
3534 gd_cave_set_seconds_sound(cave);
3537 // a gravity switch was activated; seconds counting down
3538 if (cave->gravity_will_change > 0)
3540 cave->gravity_will_change -= cave->speed;
3541 if (cave->gravity_will_change < 0)
3542 cave->gravity_will_change = 0;
3544 if (cave->gravity_will_change == 0)
3546 cave->gravity = cave->gravity_next_direction;
3547 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound
3551 // creatures direction automatically change
3552 if (cave->creatures_direction_will_change > 0)
3554 cave->creatures_direction_will_change -= cave->speed;
3555 if (cave->creatures_direction_will_change < 0)
3556 cave->creatures_direction_will_change = 0;
3558 if (cave->creatures_direction_will_change == 0)
3560 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3562 cave->creatures_backwards = !cave->creatures_backwards;
3563 cave->creatures_direction_will_change =
3564 cave->creatures_direction_auto_change_time * cave->timing_factor;
3568 // magic wall; if active&wait or not wait for hatching
3569 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3570 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3572 cave->magic_wall_time -= cave->speed;
3573 if (cave->magic_wall_time < 0)
3574 cave->magic_wall_time = 0;
3575 if (cave->magic_wall_time == 0)
3576 cave->magic_wall_state = GD_MW_EXPIRED;
3579 // we may wait for hatching, when starting amoeba
3580 if (cave->amoeba_timer_started_immediately ||
3581 (cave->amoeba_state == GD_AM_AWAKE &&
3582 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3584 cave->amoeba_time -= cave->speed;
3585 if (cave->amoeba_time < 0)
3586 cave->amoeba_time = 0;
3587 if (cave->amoeba_time == 0)
3588 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3591 // we may wait for hatching, when starting amoeba
3592 if (cave->amoeba_timer_started_immediately ||
3593 (cave->amoeba_2_state == GD_AM_AWAKE &&
3594 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3596 cave->amoeba_2_time -= cave->speed;
3597 if (cave->amoeba_2_time < 0)
3598 cave->amoeba_2_time = 0;
3599 if (cave->amoeba_2_time == 0)
3600 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3603 // check for player hatching.
3604 start_signal = FALSE;
3606 // if not the c64 scheduling, but the correct frametime is used,
3607 // hatching delay should always be decremented.
3608 // otherwise, the if (millisecs...) condition below will set this.
3609 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3611 // NON-C64 scheduling
3612 if (cave->hatching_delay_frame > 0)
3614 // for milliseconds-based, non-c64 schedulings, hatching delay means frames.
3615 cave->hatching_delay_frame--;
3616 if (cave->hatching_delay_frame == 0)
3617 start_signal = TRUE;
3623 if (cave->hatching_delay_time > 0)
3625 // for c64 schedulings, hatching delay means milliseconds.
3626 cave->hatching_delay_time -= cave->speed;
3627 if (cave->hatching_delay_time <= 0)
3629 cave->hatching_delay_time = 0;
3630 start_signal = TRUE;
3635 // if decremented hatching, and it became zero:
3638 // THIS IS THE CAVE START SIGNAL
3640 // record that now the cave is in its normal state
3641 cave->hatched = TRUE;
3643 // if diamonds needed is below zero, we count the available diamonds now.
3644 gd_cave_count_diamonds(cave);
3646 // setup direction auto change
3647 if (cave->creatures_direction_auto_change_time)
3649 cave->creatures_direction_will_change =
3650 cave->creatures_direction_auto_change_time * cave->timing_factor;
3652 if (cave->creatures_direction_auto_change_on_start)
3653 cave->creatures_backwards = !cave->creatures_backwards;
3656 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3660 if (cave->biters_wait_frame == 0)
3661 cave->biters_wait_frame = cave->biter_delay_frame;
3663 cave->biters_wait_frame--;
3665 // replicators delay
3666 if (cave->replicators_wait_frame == 0)
3667 cave->replicators_wait_frame = cave->replicator_delay_frame;
3669 cave->replicators_wait_frame--;
3672 // ============================================================================
3674 // ============================================================================
3677 // check if cave failed by timeout is done in main game engine
3679 // check if cave failed by timeout
3680 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3682 gd_cave_clear_sounds(cave);
3683 cave->player_state = GD_PL_TIMEOUT;
3684 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3688 // set these for drawing.
3689 cave->last_direction = player_move;
3691 // here we remember last movements for animation. this is needed here,
3692 // as animation is in sync with the game, not the keyboard directly.
3693 // (for example, after exiting the cave, the player was "running" in the
3694 // original, till bonus points were counted for remaining time and so on.
3695 if (player_move == GD_MV_LEFT ||
3696 player_move == GD_MV_UP_LEFT ||
3697 player_move == GD_MV_DOWN_LEFT)
3698 cave->last_horizontal_direction = GD_MV_LEFT;
3700 if (player_move == GD_MV_RIGHT ||
3701 player_move == GD_MV_UP_RIGHT ||
3702 player_move == GD_MV_DOWN_RIGHT)
3703 cave->last_horizontal_direction = GD_MV_RIGHT;
3705 cave->frame++; // XXX
3708 void set_initial_cave_speed(GdCave *cave)
3713 // set cave get function; to implement perfect or lineshifting borders
3714 if (cave->lineshift)
3715 cave->getp = getp_shift;
3717 cave->getp = getp_perfect;
3719 // check whether to scan the first and last line
3720 if (cave->border_scan_first_and_last)
3731 for (y = ymin; y <= ymax; y++)
3733 for (x = 0; x < cave->w; x++)
3735 // add the ckdelay correction value for every element seen.
3736 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3740 // update timing calculated by iterating and counting elements
3741 update_cave_speed(cave);