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 at (tx_start, ty_start). we check the whole cave,
987 from (tx_start + 1, ty_start), till we get back to (tx_start, ty_start) (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)
993 // start at teleporter position (not at player position!)
994 int tx_start = px + gd_dx[player_move];
995 int ty_start = py + gd_dy[player_move];
1001 // jump to next element; wrap around columns and rows.
1013 // if we found a teleporter...
1014 if (get(cave, tx, ty) == O_TELEPORTER &&
1015 is_space_dir(cave, tx, ty, player_move))
1017 // new player appears near teleporter found
1018 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1020 // current player disappears
1021 store(cave, px, py, O_SPACE);
1023 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1025 return TRUE; // return true as teleporter worked
1028 // loop until we get back to original coordinates
1029 while (tx != tx_start || ty != ty_start);
1031 // return false as we did not find any usable teleporter
1036 try to push an element.
1037 returns true if the push is possible; also does move the specified _element_.
1038 up to the caller to move the _player_itself_.
1040 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1043 GdElement what = get_dir(cave, x, y, player_move);
1045 // gravity for falling wall, bladder, ...
1046 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1052 case O_WAITING_STONE:
1055 case O_CHASING_STONE:
1057 case O_FLYING_STONE:
1059 // pushing some kind of stone or nut
1060 // directions possible: 90degrees cw or ccw to current gravity.
1061 // only push if player dir is orthogonal to gravity,
1062 // ie. gravity down, pushing left & right possible
1063 if (player_move == ccw_fourth[cave->gravity] ||
1064 player_move == cw_fourth[cave->gravity])
1070 // different probabilities for different elements.
1073 case O_WAITING_STONE:
1074 // waiting stones are light, can always push
1078 case O_CHASING_STONE:
1079 // chasing can be pushed if player is turbo
1080 if (cave->sweet_eaten)
1085 // mega may(!) be pushed if player is turbo
1086 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1092 case O_FLYING_STONE:
1094 if (cave->sweet_eaten)
1095 prob = cave->pushing_stone_prob_sweet; // probability with sweet
1097 prob = cave->pushing_stone_prob; // probability without sweet.
1104 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1105 gd_rand_int_range(cave->random, 0, 1000000) < prob)
1107 // if decided that he will be able to push,
1108 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1109 play_sound_of_element_pushing(cave, what, x, y);
1124 // pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1125 // not an O_BLADDER_x.
1126 // there is no "delayed" state of a bladder, so we use store_dir_no_scanned!
1128 // first check: we cannot push a bladder "up"
1129 if (player_move != opposite[grav_compat])
1131 // pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check.
1132 // player moving in the direction of gravity.
1136 if (player_move == grav_compat)
1138 // pushing bladder down
1139 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1140 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1141 // if no space to push down, maybe left (down-left to player)
1142 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1144 // left is "down, turned right (cw)"
1145 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1146 // if not, maybe right (down-right to player)
1147 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1148 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1151 // pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check.
1155 else if (player_move == cw_fourth[grav_compat])
1157 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) // pushing it left
1158 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1159 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) // maybe down, and player will move left
1160 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1161 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) // maybe up, and player will move left
1162 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1165 // pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check.
1169 else if (player_move == ccw_fourth[grav_compat])
1171 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) // pushing it right
1172 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1173 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) // maybe down, and player will move right
1174 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1175 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) // maybe up, and player will move right
1176 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1180 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1185 // a box is only pushed with the fire pressed
1188 // but always with 100% probability
1189 switch (player_move)
1195 // pushing in some dir, two steps in that dir - is there space?
1196 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1199 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1201 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1206 // push in no other directions possible
1212 // pushing of other elements not possible
1220 // from the key press booleans, create a direction
1221 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1223 GdDirection player_move;
1225 // from the key press booleans, create a direction
1227 player_move = GD_MV_UP_RIGHT;
1228 else if (down && right)
1229 player_move = GD_MV_DOWN_RIGHT;
1230 else if (down && left)
1231 player_move = GD_MV_DOWN_LEFT;
1232 else if (up && left)
1233 player_move = GD_MV_UP_LEFT;
1235 player_move = GD_MV_UP;
1237 player_move = GD_MV_DOWN;
1239 player_move = GD_MV_LEFT;
1241 player_move = GD_MV_RIGHT;
1243 player_move = GD_MV_STILL;
1248 // clear these to no sound; and they will be set during iteration.
1249 void gd_cave_clear_sounds(GdCave *cave)
1251 cave->sound1 = GD_S_NONE;
1252 cave->sound2 = GD_S_NONE;
1253 cave->sound3 = GD_S_NONE;
1256 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1257 GdElement falling_element)
1259 if (cave->gravity_disabled)
1262 if (is_space_dir(cave, x, y, falling_direction))
1264 // beginning to fall
1265 play_sound_of_element(cave, get(cave, x, y), x, y);
1266 move(cave, x, y, falling_direction, falling_element);
1269 // check if it is on a sloped element, and it can roll.
1270 // for example, sloped wall looks like:
1273 // this is tagged as sloped up&left.
1274 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1275 // then check the direction to roll (left or right)
1276 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1277 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1279 // rolling down, if sitting on a sloped object
1280 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1281 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1282 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1284 // rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1285 // so here we use cw_fourth
1286 play_sound_of_element(cave, get(cave, x, y), x, y);
1287 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1289 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1290 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1291 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1294 play_sound_of_element(cave, get(cave, x, y), x, y);
1295 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1300 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1302 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1303 cave->voodoo_dies_by_stone)
1305 // this is a 1stB-style vodo. explodes by stone, collects diamonds
1306 explode_dir(cave, x, y, fall_dir);
1313 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1315 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1316 cave->voodoo_collects_diamonds)
1318 // this is a 1stB-style voodoo. explodes by stone, collects diamonds
1319 player_get_element(cave, O_DIAMOND, x, y); // as if player got diamond
1320 store(cave, x, y, O_SPACE); // diamond disappears
1327 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1328 GdDirection fall_dir, GdElement bouncing)
1330 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1331 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1334 store(cave, x, y, bouncing);
1335 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1337 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1345 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1346 GdDirection fall_dir, GdElement magic)
1348 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1350 play_sound_of_element(cave, O_DIAMOND, x, y); // always play diamond sound
1352 if (cave->magic_wall_state == GD_MW_DORMANT)
1353 cave->magic_wall_state = GD_MW_ACTIVE;
1355 if (cave->magic_wall_state == GD_MW_ACTIVE &&
1356 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1358 // if magic wall active and place underneath, it turns element
1359 // into anything the effect says to do.
1360 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1363 // active or non-active or anything, element falling in will always disappear
1364 store(cave, x, y, O_SPACE);
1372 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1374 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1376 explode_dir(cave, x, y, fall_dir);
1383 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1384 GdDirection fall_dir, GdElement bouncing)
1386 if (is_space_dir(cave, x, y, fall_dir))
1389 move(cave, x, y, fall_dir, get(cave, x, y));
1394 // check if it is on a sloped element, and it can roll.
1395 // for example, sloped wall looks like:
1398 // this is tagged as sloped up&left.
1399 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1400 // then check the direction to roll (left or right)
1401 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1403 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1405 // sloped element, falling to left or right
1406 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1407 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1408 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1410 play_sound_of_element(cave, get(cave, x, y), x, y);
1412 // try to roll left first - see O_STONE to understand why cw_fourth
1413 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1415 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1416 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1417 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1419 play_sound_of_element(cave, get(cave, x, y), x, y);
1421 // if not, try to roll right
1422 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1426 // cannot roll in any direction, so it stops
1427 play_sound_of_element(cave, get(cave, x, y), x, y);
1428 store(cave, x, y, bouncing);
1434 // any other element, stops
1435 play_sound_of_element(cave, get(cave, x, y), x, y);
1436 store(cave, x, y, bouncing);
1440 static void update_cave_speed(GdCave *cave)
1442 // update timing calculated by iterating and counting elements which were slow to process on c64
1443 switch (cave->scheduling)
1445 case GD_SCHEDULING_MILLISECONDS:
1446 // cave->speed already contains the milliseconds value, do not touch it
1449 case GD_SCHEDULING_BD1:
1450 if (!cave->intermission)
1451 // non-intermissions
1452 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1454 // intermissions were quicker, as only lines 1-12 were processed by the engine.
1455 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1458 case GD_SCHEDULING_BD1_ATARI:
1459 // about 20ms/frame faster than c64 version
1460 if (!cave->intermission)
1461 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); // non-intermissions
1463 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); // for intermissions
1466 case GD_SCHEDULING_BD2:
1468 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1471 case GD_SCHEDULING_PLCK:
1472 // 65 is totally empty cave in construction kit, with delay = 0)
1473 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1476 case GD_SCHEDULING_BD2_PLCK_ATARI:
1477 // a really fast engine; timing works like c64 plck.
1478 // 40 ms was measured in the construction kit, with delay = 0
1479 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1482 case GD_SCHEDULING_CRDR:
1483 if (cave->hammered_walls_reappear) // this made the engine very slow.
1484 cave->ckdelay += 60000;
1485 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1488 case GD_SCHEDULING_MAX:
1494 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1501 // amoeba found to be enclosed. if not, this is cleared
1502 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1504 // counting the number of amoebas. after scan, check if too much
1505 int amoeba_count, amoeba_2_count;
1507 // cave scan found water - for sound
1508 boolean found_water;
1510 boolean inbox_toggle;
1511 boolean start_signal;
1513 // gravity for falling wall, bladder, ...
1514 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1516 // directions for o_something_1, 2, 3 and 4 (creatures)
1517 static const GdDirection creature_dir[] =
1524 static const GdDirection creature_chdir[] =
1531 int time_decrement_sec;
1533 // biters eating elements preference, they try to go in this order
1534 GdElement biter_try[] =
1541 boolean amoeba_sound, magic_sound;
1543 gd_cave_clear_sounds(cave);
1545 // if diagonal movements not allowed,
1546 // horizontal movements have precedence. [BROADRIBB]
1547 if (!cave->diagonal_movements)
1549 switch (player_move)
1551 case GD_MV_UP_RIGHT:
1552 case GD_MV_DOWN_RIGHT:
1553 player_move = GD_MV_RIGHT;
1557 case GD_MV_DOWN_LEFT:
1558 player_move = GD_MV_LEFT;
1562 // no correction needed
1567 // set cave get function; to implement perfect or lineshifting borders
1568 if (cave->lineshift)
1570 cave->getp = getp_shift;
1571 cave->getx = getx_shift;
1572 cave->gety = gety_shift;
1576 cave->getp = getp_perfect;
1577 cave->getx = getx_perfect;
1578 cave->gety = gety_perfect;
1581 // increment this. if the scan routine comes across player, clears it (sets to zero).
1582 if (cave->player_seen_ago < 100)
1583 cave->player_seen_ago++;
1585 if (cave->pneumatic_hammer_active_delay > 0)
1586 cave->pneumatic_hammer_active_delay--;
1588 // inboxes and outboxes flash with the rhythm of the game, not the display.
1589 // also, a player can be born only from an open, not from a steel-wall-like inbox.
1590 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1591 inbox_toggle = cave->inbox_flash_toggle;
1593 if (cave->gate_open_flash > 0)
1594 cave->gate_open_flash--;
1596 // score collected this frame
1599 // suicide only kills the active player
1600 // player_x, player_y was set by the previous iterate routine, or the cave setup.
1601 // we must check if there is a player or not - he may have exploded or something like that
1602 if (suicide && cave->player_state == GD_PL_LIVING &&
1603 is_player(cave, cave->player_x, cave->player_y))
1604 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1606 // check for walls reappearing
1607 if (cave->hammered_reappear)
1609 for (y = 0; y < cave->h; y++)
1611 for (x = 0; x < cave->w; x++)
1613 // timer for the cell > 0?
1614 if (cave->hammered_reappear[y][x] > 0)
1617 cave->hammered_reappear[y][x]--;
1619 // check if it became zero
1620 if (cave->hammered_reappear[y][x] == 0)
1622 store(cave, x, y, O_BRICK);
1623 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1630 // variables to check during the scan
1632 // will be set to false if any of the amoeba is found free.
1633 amoeba_found_enclosed = TRUE;
1634 amoeba_2_found_enclosed = TRUE;
1637 found_water = FALSE;
1639 time_decrement_sec = 0;
1641 // check whether to scan the first and last line
1642 if (cave->border_scan_first_and_last)
1653 // the cave scan routine
1654 for (y = ymin; y <= ymax; y++)
1656 for (x = 0; x < cave->w; x++)
1658 // if we find a scanned element, change it to the normal one, and that's all.
1659 // this is required, for example for chasing stones, which have moved, always passing slime!
1660 if (get(cave, x, y) & SCANNED)
1662 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1667 // add the ckdelay correction value for every element seen.
1668 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1670 switch (get(cave, x, y))
1672 // ============================================================================
1674 // ============================================================================
1677 if (cave->kill_player)
1679 explode (cave, x, y);
1683 cave->player_seen_ago = 0;
1684 // bd4 intermission caves have many players. so if one of them has exited,
1685 // do not change the flag anymore. so this if () is needed
1686 if (cave->player_state != GD_PL_EXITED)
1687 cave->player_state = GD_PL_LIVING;
1689 // check for pneumatic hammer things
1690 // 1) press fire, 2) have pneumatic hammer 4) space on left or right
1691 // for hammer 5) stand on something
1692 if (player_fire && cave->got_pneumatic_hammer &&
1693 is_space_dir(cave, x, y, player_move) &&
1694 !is_space_dir(cave, x, y, GD_MV_DOWN))
1696 if (player_move == GD_MV_LEFT &&
1697 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1699 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1700 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1701 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1705 if (player_move == GD_MV_RIGHT &&
1706 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1708 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1709 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1710 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1715 if (player_move != GD_MV_STILL)
1717 // only do every check if he is not moving
1718 GdElement what = get_dir(cave, x, y, player_move);
1719 GdElement remains = what;
1722 // if we are 'eating' a teleporter, and the function returns true
1723 // (teleporting worked), break here
1724 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1727 // try to push element; if successful, break
1728 push = do_push(cave, x, y, player_move, player_fire);
1738 // if its a bomb, remember he now has one.
1739 // we do not change the "remains" and "what" variables,
1740 // so that part of the code will be ineffective
1741 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1742 store_dir(cave, x, y, player_move, O_SPACE);
1745 store(cave, x, y, O_PLAYER_BOMB);
1747 move(cave, x, y, player_move, O_PLAYER_BOMB);
1751 // we do not change the "remains" and "what" variables,
1752 // so that part of the code will be ineffective
1753 if (!player_fire && !cave->gravity_switch_active &&
1754 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1756 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1757 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1758 cave->gravity_disabled = TRUE;
1762 case O_GRAVITY_SWITCH:
1763 // (we cannot use player_get for this as it does not have player_move parameter)
1764 // only allow changing direction if the new dir is not diagonal
1765 if (cave->gravity_switch_active &&
1766 (player_move == GD_MV_LEFT ||
1767 player_move == GD_MV_RIGHT ||
1768 player_move == GD_MV_UP ||
1769 player_move == GD_MV_DOWN))
1771 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1772 cave->gravity_will_change =
1773 cave->gravity_change_time * cave->timing_factor;
1774 cave->gravity_next_direction = player_move;
1775 cave->gravity_switch_active = FALSE;
1780 // get element - process others.
1781 // if cannot get, player_get_element will return the same
1782 remains = player_get_element(cave, what, x, y);
1787 if (remains != what || remains == O_SPACE)
1789 // if anything changed, apply the change.
1791 // if snapping anything and we have snapping explosions set.
1792 // but these is not true for pushing.
1793 if (remains == O_SPACE && player_fire && !push)
1794 remains = cave->snap_element;
1796 if (remains != O_SPACE || player_fire)
1797 // if any other element than space, player cannot move.
1798 // also if pressing fire, will not move.
1799 store_dir(cave, x, y, player_move, remains);
1801 // if space remains there, the player moves.
1802 move(cave, x, y, player_move, O_PLAYER);
1808 // much simpler; cannot steal stones
1809 if (cave->kill_player)
1811 explode(cave, x, y);
1815 cave->player_seen_ago = 0;
1816 // bd4 intermission caves have many players. so if one of them has exited,
1817 // do not change the flag anymore. so this if () is needed
1818 if (cave->player_state != GD_PL_EXITED)
1819 cave->player_state = GD_PL_LIVING;
1821 if (player_move != GD_MV_STILL)
1823 // if the player does not move, nothing to do
1824 GdElement what = get_dir(cave, x, y, player_move);
1825 GdElement remains = what;
1829 // placing a bomb into empty space or dirt
1830 if (is_space_dir(cave, x, y, player_move) ||
1831 is_element_dir(cave, x, y, player_move, O_DIRT))
1833 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1835 // placed bomb, he is normal player again
1836 store(cave, x, y, O_PLAYER);
1837 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1842 // pushing and collecting
1843 // if we are 'eating' a teleporter, and the function returns true
1844 // (teleporting worked), break here
1845 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1848 // player fire is false...
1849 if (do_push(cave, x, y, player_move, FALSE))
1857 case O_GRAVITY_SWITCH:
1858 // (we cannot use player_get for this as it does not have
1859 // player_move parameter)
1860 // only allow changing direction if the new dir is not diagonal
1861 if (cave->gravity_switch_active &&
1862 (player_move == GD_MV_LEFT ||
1863 player_move == GD_MV_RIGHT ||
1864 player_move == GD_MV_UP ||
1865 player_move == GD_MV_DOWN))
1867 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1868 cave->gravity_will_change =
1869 cave->gravity_change_time * cave->timing_factor;
1870 cave->gravity_next_direction = player_move;
1871 cave->gravity_switch_active = FALSE;
1876 // get element. if cannot get, player_get_element will return the same
1877 remains = player_get_element (cave, what, x, y);
1882 // if element changed, OR there is space, move.
1883 if (remains != what || remains == O_SPACE)
1885 // if anything changed, apply the change.
1886 move(cave, x, y, player_move, O_PLAYER_BOMB);
1891 case O_PLAYER_STIRRING:
1892 if (cave->kill_player)
1894 explode(cave, x, y);
1898 // stirring sound, if no other walking sound or explosion
1899 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1901 cave->player_seen_ago = 0;
1902 // bd4 intermission caves have many players. so if one of them has exited,
1903 // do not change the flag anymore. so this if () is needed
1904 if (cave->player_state != GD_PL_EXITED)
1905 cave->player_state = GD_PL_LIVING;
1909 // player "exits" stirring the pot by pressing fire
1910 cave->gravity_disabled = FALSE;
1911 store(cave, x, y, O_PLAYER);
1912 cave->gravity_switch_active = TRUE;
1916 // player holding pneumatic hammer
1917 case O_PLAYER_PNEUMATIC_LEFT:
1918 case O_PLAYER_PNEUMATIC_RIGHT:
1919 // usual player stuff
1920 if (cave->kill_player)
1922 explode(cave, x, y);
1926 cave->player_seen_ago = 0;
1927 if (cave->player_state != GD_PL_EXITED)
1928 cave->player_state = GD_PL_LIVING;
1930 // if hammering time is up, becomes a normal player again.
1931 if (cave->pneumatic_hammer_active_delay == 0)
1932 store(cave, x, y, O_PLAYER);
1935 // the active pneumatic hammer itself
1936 case O_PNEUMATIC_ACTIVE_RIGHT:
1937 case O_PNEUMATIC_ACTIVE_LEFT:
1938 if (cave->pneumatic_hammer_active_delay == 0)
1942 // pneumatic hammer element disappears
1943 store(cave, x, y, O_SPACE);
1945 // which is the new element which appears after that one is hammered?
1946 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1948 // if there is a new element, display it
1949 // O_NONE might be returned, for example if the element being
1950 // hammered explodes during hammering (by a nearby explosion)
1951 if (new_elem != O_NONE)
1953 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1955 // and if walls reappear, remember it in array
1956 if (cave->hammered_walls_reappear)
1960 wall_y = (y + 1) % cave->h;
1961 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1967 // ============================================================================
1968 // S T O N E S, D I A M O N D S
1969 // ============================================================================
1971 case O_STONE: // standing stone
1972 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1975 case O_MEGA_STONE: // standing mega_stone
1976 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1979 case O_DIAMOND: // standing diamond
1980 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1983 case O_NUT: // standing nut
1984 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1987 case O_DIRT_BALL: // standing dirt ball
1988 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1991 case O_DIRT_LOOSE: // standing loose dirt
1992 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1995 case O_FLYING_STONE: // standing stone
1996 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1999 case O_FLYING_DIAMOND: // standing diamond
2000 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2003 // ============================================================================
2004 // 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
2005 // ============================================================================
2007 case O_DIRT_BALL_F: // falling dirt ball
2008 if (!cave->gravity_disabled)
2009 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2012 case O_DIRT_LOOSE_F: // falling loose dirt
2013 if (!cave->gravity_disabled)
2014 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2017 case O_STONE_F: // falling stone
2018 if (!cave->gravity_disabled)
2020 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2023 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2026 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2029 if (do_fall_try_crush(cave, x, y, cave->gravity))
2032 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2036 case O_MEGA_STONE_F: // falling mega
2037 if (!cave->gravity_disabled)
2039 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2042 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2045 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2048 if (do_fall_try_crush(cave, x, y, cave->gravity))
2051 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2055 case O_DIAMOND_F: // falling diamond
2056 if (!cave->gravity_disabled)
2058 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2061 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2064 if (do_fall_try_crush(cave, x, y, cave->gravity))
2067 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2071 case O_NUT_F: // falling nut
2072 if (!cave->gravity_disabled)
2074 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2077 if (do_fall_try_crush(cave, x, y, cave->gravity))
2080 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2084 case O_FLYING_STONE_F: // falling stone
2085 if (!cave->gravity_disabled)
2087 GdDirection fall_dir = opposite[cave->gravity];
2089 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2092 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2095 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2098 if (do_fall_try_crush(cave, x, y, fall_dir))
2101 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2105 case O_FLYING_DIAMOND_F: // falling diamond
2106 if (!cave->gravity_disabled)
2108 GdDirection fall_dir = opposite[cave->gravity];
2110 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2113 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2116 if (do_fall_try_crush(cave, x, y, fall_dir))
2119 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2123 // ============================================================================
2124 // N I T R O P A C K
2125 // ============================================================================
2127 case O_NITRO_PACK: // standing nitro pack
2128 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2131 case O_NITRO_PACK_F: // falling nitro pack
2132 if (!cave->gravity_disabled)
2134 if (is_space_dir(cave, x, y, cave->gravity)) // if space, falling further
2135 move(cave, x, y, cave->gravity, get(cave, x, y));
2136 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2138 // try magic wall; if true, function did the work
2140 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2142 // falling on a dirt, it does NOT explode - just stops at its place.
2143 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2144 store(cave, x, y, O_NITRO_PACK);
2147 // falling on any other element it explodes
2148 explode(cave, x, y);
2152 case O_NITRO_PACK_EXPLODE: // a triggered nitro pack
2153 explode(cave, x, y);
2156 // ============================================================================
2157 // C R E A T U R E S
2158 // ============================================================================
2164 // if cannot move in any direction, becomes an enclosed cow
2165 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2166 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2167 store(cave, x, y, O_COW_ENCLOSED_1);
2170 // THIS IS THE CREATURE MOVE thing copied.
2171 const GdDirection *creature_move;
2172 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2173 GdElement base; // base element number (which is like O_***_1)
2174 int dir, dirn, dirp; // direction
2178 dir = get(cave, x, y)-base; // facing where
2179 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2181 // now change direction if backwards
2182 if (cave->creatures_backwards)
2187 dirn = (dir + 3) & 3; // fast turn
2188 dirp = (dir + 1) & 3; // slow turn
2192 dirn = (dir + 1) & 3; // fast turn
2193 dirp = (dir + 3) & 3; // slow turn
2196 if (is_space_dir(cave, x, y, creature_move[dirn]))
2197 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2198 else if (is_space_dir(cave, x, y, creature_move[dir]))
2199 move(cave, x, y, creature_move[dir], base + dir); // go on
2201 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2205 // enclosed cows wait some time before turning to a skeleton
2206 case O_COW_ENCLOSED_1:
2207 case O_COW_ENCLOSED_2:
2208 case O_COW_ENCLOSED_3:
2209 case O_COW_ENCLOSED_4:
2210 case O_COW_ENCLOSED_5:
2211 case O_COW_ENCLOSED_6:
2212 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2213 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2214 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2215 is_space_dir(cave, x, y, GD_MV_DOWN))
2216 store(cave, x, y, O_COW_1);
2221 case O_COW_ENCLOSED_7:
2222 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2223 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2224 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2225 is_space_dir(cave, x, y, GD_MV_DOWN))
2226 store(cave, x, y, O_COW_1);
2228 store(cave, x, y, O_SKELETON);
2235 case O_ALT_FIREFLY_1:
2236 case O_ALT_FIREFLY_2:
2237 case O_ALT_FIREFLY_3:
2238 case O_ALT_FIREFLY_4:
2243 case O_ALT_BUTTER_1:
2244 case O_ALT_BUTTER_2:
2245 case O_ALT_BUTTER_3:
2246 case O_ALT_BUTTER_4:
2251 // check if touches a voodoo
2252 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2253 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2254 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2255 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2256 cave->voodoo_touched = TRUE;
2258 // check if touches something bad and should explode (includes voodoo by the flags)
2259 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2260 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2261 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2262 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2263 explode (cave, x, y);
2267 const GdDirection *creature_move;
2268 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2269 GdElement base = -1; // base element number (which is like O_***_1)
2270 int dir, dirn, dirp; // direction
2272 if (get(cave, x, y) >= O_FIREFLY_1 &&
2273 get(cave, x, y) <= O_FIREFLY_4)
2275 else if (get(cave, x, y) >= O_BUTTER_1 &&
2276 get(cave, x, y) <= O_BUTTER_4)
2278 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2279 get(cave, x, y) <= O_STONEFLY_4)
2280 base = O_STONEFLY_1;
2281 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2282 get(cave, x, y) <= O_ALT_FIREFLY_4)
2283 base = O_ALT_FIREFLY_1;
2284 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2285 get(cave, x, y) <= O_ALT_BUTTER_4)
2286 base = O_ALT_BUTTER_1;
2288 dir = get(cave, x, y) - base; // facing where
2289 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2291 // now change direction if backwards
2292 if (cave->creatures_backwards)
2297 dirn = (dir + 3) & 3; // fast turn
2298 dirp = (dir + 1) & 3; // slow turn
2302 dirn = (dir + 1) & 3; // fast turn
2303 dirp = (dir + 3) & 3; // slow turn
2306 if (is_space_dir(cave, x, y, creature_move[dirn]))
2307 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2308 else if (is_space_dir(cave, x, y, creature_move[dir]))
2309 move(cave, x, y, creature_move[dir], base + dir); // go on
2311 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2315 case O_WAITING_STONE:
2316 if (is_space_dir(cave, x, y, grav_compat))
2318 // beginning to fall
2320 move(cave, x, y, grav_compat, O_CHASING_STONE);
2322 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2324 // rolling down a brick wall or a stone
2325 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2326 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2327 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2329 // maybe rolling left - see case O_STONE to understand why we use cw_fourth here
2330 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2332 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2333 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2334 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2337 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2342 case O_CHASING_STONE:
2344 int px = cave->px[0];
2345 int py = cave->py[0];
2346 boolean horizontal = gd_rand_boolean(cave->random);
2347 boolean dont_move = FALSE;
2355 // ------------------------------------------------------------
2356 // check for a horizontal movement
2357 // ------------------------------------------------------------
2360 // if coordinates are the same
2362 horizontal = !horizontal;
2369 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2371 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2375 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2377 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2386 horizontal = !horizontal;
2394 // ------------------------------------------------------------
2395 // check for a vertical movement
2396 // ------------------------------------------------------------
2399 // if coordinates are the same
2401 horizontal = !horizontal;
2407 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2409 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2413 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2415 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2424 horizontal = !horizontal;
2437 // if we should move in both directions, but can not move in any, stop.
2442 // check for horizontal
2445 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2446 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2447 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2448 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2449 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2450 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2454 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2455 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2456 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2457 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2458 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2459 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2464 // check for vertical
2467 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2468 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2469 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2470 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2471 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2472 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2476 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2477 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2478 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2479 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2480 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2481 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2489 if (cave->replicators_wait_frame == 0 &&
2490 cave->replicators_active &&
2491 !cave->gravity_disabled)
2493 // only replicate, if space is under it.
2494 // do not replicate players!
2495 // also obeys gravity settings.
2496 // only replicate element if it is not a scanned one
2497 // do not replicate space... that condition looks like it
2498 // makes no sense, but otherwise it generates SCANNED spaces,
2499 // which cannot be "collected" by the player, so he cannot run
2500 // under a replicator
2501 if (is_space_dir(cave, x, y, cave->gravity) &&
2502 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2503 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2505 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2506 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2515 if (cave->biters_wait_frame == 0)
2517 static GdDirection biter_move[] =
2525 // direction, last two bits 0..3
2526 int dir = get(cave, x, y) - O_BITER_1;
2527 int dirn = (dir + 3) & 3;
2528 int dirp = (dir + 1) & 3;
2530 GdElement made_sound_of = O_NONE;
2532 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2534 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2536 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2537 if (biter_try[i] != O_SPACE)
2538 made_sound_of = O_BITER_1; // sound of a biter eating
2541 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2543 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2544 if (biter_try[i] != O_SPACE)
2545 made_sound_of = O_BITER_1; // sound of a biter eating
2548 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2550 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2551 if (biter_try[i] != O_SPACE)
2552 made_sound_of = O_BITER_1; // sound of a biter eating
2557 if (i == ARRAY_SIZE(biter_try))
2558 // i = number of elements in array: could not move, so just turn
2559 store(cave, x, y, O_BITER_1 + dirp);
2560 else if (biter_try[i] == O_STONE)
2562 // if there was a stone there, where we moved...
2563 // do not eat stones, just throw them back
2564 store(cave, x, y, O_STONE);
2565 made_sound_of = O_STONE;
2568 // if biter did move, we had sound. play it.
2569 if (made_sound_of != O_NONE)
2570 play_sound_of_element(cave, made_sound_of, x, y);
2578 // check if touches a voodoo
2579 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2580 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2581 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2582 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2583 cave->voodoo_touched = TRUE;
2585 // check if touches something bad and should explode (includes voodoo by the flags)
2586 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2587 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2588 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2589 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2590 explode (cave, x, y);
2594 const GdDirection *creature_move;
2595 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2596 GdElement base = O_DRAGONFLY_1; // base element number (which is like O_***_1)
2597 int dir, dirn; // direction
2599 dir = get(cave, x, y)-base; // facing where
2600 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2602 // now change direction if backwards
2603 if (cave->creatures_backwards)
2607 dirn = (dir + 3) & 3; // fast turn
2609 dirn = (dir + 1) & 3; // fast turn
2611 // if can move forward, does so.
2612 if (is_space_dir(cave, x, y, creature_move[dir]))
2613 move(cave, x, y, creature_move[dir], base + dir);
2615 // otherwise turns 90 degrees in place.
2616 store(cave, x, y, base + dirn);
2621 store(cave, x, y, O_BLADDER_1);
2632 // bladder with any delay state: try to convert to clock.
2633 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2634 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))
2636 // if touches the specified element, let it be a clock
2637 store(cave, x, y, O_PRE_CLOCK_1);
2639 // plays the bladder convert sound
2640 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2644 // is space over the bladder?
2645 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2647 if (get(cave, x, y) == O_BLADDER_8)
2649 // if it is a bladder 8, really move up
2650 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2651 play_sound_of_element(cave, O_BLADDER, x, y);
2654 // if smaller delay, just increase delay.
2658 // if not space, is something sloped over the bladder?
2659 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2660 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2662 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2663 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2664 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2666 // rolling up, to left
2667 if (get(cave, x, y) == O_BLADDER_8)
2669 // if it is a bladder 8, really roll
2670 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2671 play_sound_of_element(cave, O_BLADDER, x, y);
2674 // if smaller delay, just increase delay.
2677 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2678 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2679 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2681 // rolling up, to left
2682 if (get(cave, x, y) == O_BLADDER_8)
2684 // if it is a bladder 8, really roll
2685 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2686 play_sound_of_element(cave, O_BLADDER, x, y);
2689 // if smaller delay, just increase delay.
2694 // no space, no sloped thing over it - store bladder 1 and that is for now.
2696 store(cave, x, y, O_BLADDER_1);
2701 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2702 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2703 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2704 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2705 explode (cave, x, y);
2710 // the ghost is given four possibilities to move.
2711 for (i = 0; i < 4; i++)
2713 static GdDirection dirs[] =
2720 GdDirection random_dir;
2722 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2723 if (is_space_dir(cave, x, y, random_dir))
2725 move(cave, x, y, random_dir, O_GHOST);
2726 break; // ghost did move -> exit loop
2732 // ============================================================================
2733 // A C T I V E E L E M E N T S
2734 // ============================================================================
2738 switch (cave->amoeba_state)
2741 store(cave, x, y, cave->amoeba_too_big_effect);
2744 case GD_AM_ENCLOSED:
2745 store(cave, x, y, cave->amoeba_enclosed_effect);
2748 case GD_AM_SLEEPING:
2750 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2751 if (amoeba_found_enclosed)
2752 // if still found enclosed, check all four directions, if this one is able to grow.
2753 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2754 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2755 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2756 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2758 // not enclosed. this is a local (per scan) flag!
2759 amoeba_found_enclosed = FALSE;
2760 cave->amoeba_state = GD_AM_AWAKE;
2763 // if alive, check in which dir to grow (or not)
2764 if (cave->amoeba_state == GD_AM_AWAKE)
2766 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2768 switch (gd_rand_int_range(cave->random, 0, 4))
2770 // decided to grow, choose a random direction.
2771 case 0: // let this be up. numbers indifferent.
2772 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2773 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2777 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2778 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2782 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2783 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2787 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2788 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2800 // check if it is touching an amoeba, and explosion is enabled
2801 if (cave->amoeba_2_explodes_by_amoeba &&
2802 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2803 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2804 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2805 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2806 explode (cave, x, y);
2808 switch (cave->amoeba_2_state)
2811 store(cave, x, y, cave->amoeba_2_too_big_effect);
2814 case GD_AM_ENCLOSED:
2815 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2818 case GD_AM_SLEEPING:
2820 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2821 if (amoeba_2_found_enclosed)
2822 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2823 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2824 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2825 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2827 // not enclosed. this is a local (per scan) flag!
2828 amoeba_2_found_enclosed = FALSE;
2829 cave->amoeba_2_state = GD_AM_AWAKE;
2832 // if it is alive, decide if it attempts to grow
2833 if (cave->amoeba_2_state == GD_AM_AWAKE)
2834 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2836 switch (gd_rand_int_range(cave->random, 0, 4))
2838 // decided to grow, choose a random direction.
2839 case 0: // let this be up. numbers indifferent.
2840 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2841 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2845 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2846 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2850 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2851 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2855 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2856 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2866 // choose randomly, if it spreads
2867 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2869 // the current one explodes
2870 store(cave, x, y, cave->acid_turns_to);
2872 // and if neighbours are eaten, put acid there.
2873 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2875 play_sound_of_element(cave, O_ACID, x, y);
2876 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2879 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2881 play_sound_of_element(cave, O_ACID, x, y);
2882 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2885 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2887 play_sound_of_element(cave, O_ACID, x, y);
2888 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2891 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2893 play_sound_of_element(cave, O_ACID, x, y);
2894 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2901 if (!cave->water_does_not_flow_down &&
2902 is_space_dir(cave, x, y, GD_MV_DOWN))
2903 // emulating the odd behaviour in crdr
2904 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2906 if (is_space_dir(cave, x, y, GD_MV_UP))
2907 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2909 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2910 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2912 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2913 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2917 store(cave, x, y, O_WATER);
2920 case O_H_EXPANDING_WALL:
2921 case O_V_EXPANDING_WALL:
2922 case O_H_EXPANDING_STEEL_WALL:
2923 case O_V_EXPANDING_STEEL_WALL:
2924 // checks first if direction is changed.
2925 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2926 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2927 !cave->expanding_wall_changed) ||
2928 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
2929 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
2930 cave->expanding_wall_changed))
2932 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2934 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2935 play_sound_of_element(cave, get(cave, x, y), x, y);
2938 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2939 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2940 play_sound_of_element(cave, get(cave, x, y), x, y);
2945 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2946 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2947 play_sound_of_element(cave, get(cave, x, y), x, y);
2950 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2951 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2952 play_sound_of_element(cave, get(cave, x, y), x, y);
2957 case O_EXPANDING_WALL:
2958 case O_EXPANDING_STEEL_WALL:
2959 // the wall which grows in all four directions.
2960 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2962 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2963 play_sound_of_element(cave, get(cave, x, y), x, y);
2966 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2967 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2968 play_sound_of_element(cave, get(cave, x, y), x, y);
2971 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2972 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2973 play_sound_of_element(cave, get(cave, x, y), x, y);
2976 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2977 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2978 play_sound_of_element(cave, get(cave, x, y), x, y);
2984 ; // to make compilers happy ...
2986 Info("Step[%03d]", cave->frame); // XXX
2988 int rrr = gd_cave_c64_random(cave);
2991 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2992 (rrr & cave->slime_permeability_c64) == 0);
2995 * unpredictable: gd_rand_int
2996 * predictable: c64 predictable random generator.
2997 * for predictable, a random number is generated,
2998 * whether or not it is even possible that the stone will be able to pass.
3000 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
3002 GdDirection grav = cave->gravity;
3003 GdDirection oppos = opposite[cave->gravity];
3005 // space under the slime? elements may pass from top to bottom then.
3006 if (is_space_dir(cave, x, y, grav))
3008 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3010 // output a falling xy under
3011 store_dir(cave, x, y, grav, cave->slime_converts_1);
3013 store_dir(cave, x, y, oppos, O_SPACE);
3014 play_sound_of_element(cave, O_SLIME, x, y);
3016 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3018 store_dir(cave, x, y, grav, cave->slime_converts_2);
3019 store_dir(cave, x, y, oppos, O_SPACE);
3020 play_sound_of_element(cave, O_SLIME, x, y);
3022 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3024 store_dir(cave, x, y, grav, cave->slime_converts_3);
3025 store_dir(cave, x, y, oppos, O_SPACE);
3026 play_sound_of_element(cave, O_SLIME, x, y);
3028 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3030 // waiting stones pass without awakening
3031 store_dir(cave, x, y, grav, O_WAITING_STONE);
3032 store_dir(cave, x, y, oppos, O_SPACE);
3033 play_sound_of_element(cave, O_SLIME, x, y);
3035 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3037 // chasing stones pass
3038 store_dir(cave, x, y, grav, O_CHASING_STONE);
3039 store_dir(cave, x, y, oppos, O_SPACE);
3040 play_sound_of_element(cave, O_SLIME, x, y);
3045 // or space over the slime? elements may pass from bottom to up then.
3046 if (is_space_dir(cave, x, y, oppos))
3048 if (get_dir(cave, x, y, grav) == O_BLADDER)
3050 // bladders move UP the slime
3051 store_dir(cave, x, y, grav, O_SPACE);
3052 store_dir(cave, x, y, oppos, O_BLADDER_1);
3053 play_sound_of_element(cave, O_SLIME, x, y);
3055 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3057 store_dir(cave, x, y, grav, O_SPACE);
3058 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3059 play_sound_of_element(cave, O_SLIME, x, y);
3061 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3063 store_dir(cave, x, y, grav, O_SPACE);
3064 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3065 play_sound_of_element(cave, O_SLIME, x, y);
3072 case O_FALLING_WALL:
3073 if (is_space_dir(cave, x, y, grav_compat))
3075 // try falling if space under.
3078 for (yy = y + 1; yy < y + cave->h; yy++)
3079 // yy < y + cave->h is to check everything OVER the wall - since caves wrap around !!
3080 if (get(cave, x, yy) != O_SPACE)
3081 // stop cycle when other than space
3084 // if scanning stopped by a player... start falling!
3085 if (get(cave, x, yy) == O_PLAYER ||
3086 get(cave, x, yy) == O_PLAYER_GLUED ||
3087 get(cave, x, yy) == O_PLAYER_BOMB)
3089 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3090 // no sound when the falling wall starts falling!
3095 case O_FALLING_WALL_F:
3096 switch (get_dir(cave, x, y, grav_compat))
3099 case O_PLAYER_GLUED:
3101 // if player under, it explodes - the falling wall, not the player!
3102 explode(cave, x, y);
3107 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3112 play_sound_of_element(cave, get(cave, x, y), x, y);
3113 store(cave, x, y, O_FALLING_WALL);
3118 // ============================================================================
3119 // C O N V E Y O R B E L T S
3120 // ============================================================================
3122 case O_CONVEYOR_RIGHT:
3123 case O_CONVEYOR_LEFT:
3124 // only works if gravity is up or down!!!
3125 // first, check for gravity and running belts.
3126 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3128 const GdDirection *dir;
3132 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3133 if (cave->conveyor_belts_direction_changed)
3135 dir = left ? ccw_eighth : cw_eighth;
3137 // CHECK IF IT CONVEYS THE ELEMENT ABOVE IT
3138 // if gravity is normal, and the conveyor belt has something
3139 // ABOVE which can be moved
3141 // the gravity is up, so anything that should float now goes
3142 // DOWN and touches the conveyor
3143 if ((cave->gravity == GD_MV_DOWN &&
3144 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3145 (cave->gravity == GD_MV_UP &&
3146 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3148 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3149 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3151 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move
3152 store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space.
3156 // CHECK IF IT CONVEYS THE ELEMENT BELOW IT
3157 if ((cave->gravity == GD_MV_UP &&
3158 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3159 (cave->gravity == GD_MV_DOWN &&
3160 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3162 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3163 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3165 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move
3166 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear.
3172 // ============================================================================
3173 // S I M P L E C H A N G I N G; E X P L O S I O N S
3174 // ============================================================================
3177 store(cave, x, y, cave->explosion_effect);
3181 store(cave, x, y, O_DIAMOND);
3185 store(cave, x, y, cave->diamond_birth_effect);
3189 store(cave, x, y, O_STONE);
3192 case O_NITRO_EXPL_4:
3193 store(cave, x, y, cave->nitro_explosion_effect);
3197 store(cave, x, y, cave->bomb_explosion_effect);
3200 case O_AMOEBA_2_EXPL_4:
3201 store(cave, x, y, cave->amoeba_2_explosion_effect);
3204 case O_GHOST_EXPL_4:
3206 static GdElement ghost_explode[] =
3208 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3209 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3210 O_WAITING_STONE, O_BITER_1
3213 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3218 store(cave, x, y, O_STEEL);
3222 store(cave, x, y, O_CLOCK);
3226 explode(cave, x, y);
3229 case O_TRAPPED_DIAMOND:
3230 if (cave->diamond_key_collected)
3231 store(cave, x, y, O_DIAMOND);
3235 if (cave->gate_open) // if no more diamonds needed
3236 store(cave, x, y, O_OUTBOX); // open outbox
3239 case O_PRE_INVIS_OUTBOX:
3240 if (cave->gate_open) // if no more diamonds needed
3241 store(cave, x, y, O_INVIS_OUTBOX); // open outbox. invisible one :P
3245 if (cave->hatched && !inbox_toggle) // if it is time of birth
3246 store(cave, x, y, O_PRE_PL_1);
3247 inbox_toggle = !inbox_toggle;
3251 store(cave, x, y, O_PLAYER);
3276 case O_GHOST_EXPL_1:
3277 case O_GHOST_EXPL_2:
3278 case O_GHOST_EXPL_3:
3288 case O_NITRO_EXPL_1:
3289 case O_NITRO_EXPL_2:
3290 case O_NITRO_EXPL_3:
3291 case O_AMOEBA_2_EXPL_1:
3292 case O_AMOEBA_2_EXPL_2:
3293 case O_AMOEBA_2_EXPL_3:
3294 // simply the next identifier
3313 found_water = TRUE; // for sound
3314 // simply the next identifier
3318 case O_BLADDER_SPENDER:
3319 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3321 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3322 store(cave, x, y, O_PRE_STEEL_1);
3323 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3328 // other inanimate elements that do nothing
3335 // ============================================================================
3337 // ============================================================================
3339 // another scan-like routine:
3340 // short explosions (for example, in bd1) started with explode_2.
3341 // internally we use explode_1; and change it to explode_2 if needed.
3342 if (cave->short_explosions)
3344 for (y = 0; y < cave->h; y++)
3346 for (x = 0; x < cave->w; x++)
3348 if (is_first_stage_of_explosion(cave, x, y))
3350 next(cave, x, y); // select next frame of explosion
3352 // forget scanned flag immediately
3353 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3359 // finally: forget "scanned" flags for objects.
3360 // also, check for time penalties.
3361 // these is something like an effect table, but we do not really use one.
3362 for (y = 0; y < cave->h; y++)
3364 for (x = 0; x < cave->w; x++)
3366 if (get(cave, x, y) & SCANNED)
3367 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3369 if (get(cave, x, y) == O_TIME_PENALTY)
3371 store(cave, x, y, O_GRAVESTONE);
3373 // there is time penalty for destroying the voodoo
3374 time_decrement_sec += cave->time_penalty;
3379 // this loop finds the coordinates of the player. needed for scrolling and chasing stone.
3380 // but we only do this, if a living player was found. if not yet, the setup
3381 // routine coordinates are used
3382 if (cave->player_state == GD_PL_LIVING)
3384 if (cave->active_is_first_found)
3386 // to be 1stb compatible, we do everything backwards.
3387 for (y = cave->h - 1; y >= 0; y--)
3389 for (x = cave->w - 1; x >= 0; x--)
3391 if (is_player(cave, x, y))
3393 // here we remember the coordinates.
3402 // as in the original: look for the last one
3403 for (y = 0; y < cave->h; y++)
3405 for (x = 0; x < cave->w; x++)
3407 if (is_player(cave, x, y))
3409 // here we remember the coordinates.
3418 // record coordinates of player for chasing stone
3419 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3421 cave->px[i] = cave->px[i + 1];
3422 cave->py[i] = cave->py[i + 1];
3425 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3426 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3430 // update timing calculated by iterating and counting elements
3431 update_cave_speed(cave);
3433 // cave 3 sounds. precedence is controlled by the sound_play function.
3434 // but we have to check amoeba&magic together as they had a different gritty sound when mixed
3436 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3438 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3439 cave->magic_wall_sound);
3441 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3442 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3443 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3445 if (amoeba_sound && magic_sound)
3447 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3452 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3453 else if (magic_sound)
3454 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3459 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3460 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3461 play_sound_of_element(cave, O_AMOEBA, x, y);
3464 // pneumatic hammer sound - overrides everything.
3465 if (cave->pneumatic_hammer_active_delay > 0)
3466 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3469 // ============================================================================
3471 // ============================================================================
3475 // check if player is alive.
3476 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
3477 cave->player_state = GD_PL_DIED;
3479 // check if any voodoo exploded, and kill players the next scan if that happended.
3480 if (cave->voodoo_touched)
3481 cave->kill_player = TRUE;
3485 // check flags after evaluating.
3486 if (cave->amoeba_state == GD_AM_AWAKE)
3488 if (amoeba_count >= cave->amoeba_max_count)
3489 cave->amoeba_state = GD_AM_TOO_BIG;
3490 if (amoeba_found_enclosed)
3491 cave->amoeba_state = GD_AM_ENCLOSED;
3494 // amoeba can also be turned into diamond by magic wall
3495 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3496 cave->amoeba_state = GD_AM_ENCLOSED;
3499 if (cave->amoeba_2_state == GD_AM_AWAKE)
3501 // check flags after evaluating.
3502 if (amoeba_2_count >= cave->amoeba_2_max_count)
3503 cave->amoeba_2_state = GD_AM_TOO_BIG;
3505 if (amoeba_2_found_enclosed)
3506 cave->amoeba_2_state = GD_AM_ENCLOSED;
3509 // amoeba 2 can also be turned into diamond by magic wall
3510 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3511 cave->amoeba_2_state = GD_AM_ENCLOSED;
3513 // now check times. ---------------------------
3514 // decrement time if a voodoo was killed.
3515 cave->time -= time_decrement_sec * cave->timing_factor;
3519 // only decrement time when player is already born.
3522 int secondsbefore, secondsafter;
3524 secondsbefore = cave->time / cave->timing_factor;
3525 cave->time -= cave->speed;
3526 if (cave->time <= 0)
3529 secondsafter = cave->time / cave->timing_factor;
3530 if (cave->time / cave->timing_factor < 10)
3531 // if less than 10 seconds, no walking sound, but play explosion sound
3532 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3534 if (secondsbefore != secondsafter)
3535 gd_cave_set_seconds_sound(cave);
3538 // a gravity switch was activated; seconds counting down
3539 if (cave->gravity_will_change > 0)
3541 cave->gravity_will_change -= cave->speed;
3542 if (cave->gravity_will_change < 0)
3543 cave->gravity_will_change = 0;
3545 if (cave->gravity_will_change == 0)
3547 cave->gravity = cave->gravity_next_direction;
3548 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound
3552 // creatures direction automatically change
3553 if (cave->creatures_direction_will_change > 0)
3555 cave->creatures_direction_will_change -= cave->speed;
3556 if (cave->creatures_direction_will_change < 0)
3557 cave->creatures_direction_will_change = 0;
3559 if (cave->creatures_direction_will_change == 0)
3561 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3563 cave->creatures_backwards = !cave->creatures_backwards;
3564 cave->creatures_direction_will_change =
3565 cave->creatures_direction_auto_change_time * cave->timing_factor;
3569 // magic wall; if active&wait or not wait for hatching
3570 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3571 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3573 cave->magic_wall_time -= cave->speed;
3574 if (cave->magic_wall_time < 0)
3575 cave->magic_wall_time = 0;
3576 if (cave->magic_wall_time == 0)
3577 cave->magic_wall_state = GD_MW_EXPIRED;
3580 // we may wait for hatching, when starting amoeba
3581 if (cave->amoeba_timer_started_immediately ||
3582 (cave->amoeba_state == GD_AM_AWAKE &&
3583 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3585 cave->amoeba_time -= cave->speed;
3586 if (cave->amoeba_time < 0)
3587 cave->amoeba_time = 0;
3588 if (cave->amoeba_time == 0)
3589 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3592 // we may wait for hatching, when starting amoeba
3593 if (cave->amoeba_timer_started_immediately ||
3594 (cave->amoeba_2_state == GD_AM_AWAKE &&
3595 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3597 cave->amoeba_2_time -= cave->speed;
3598 if (cave->amoeba_2_time < 0)
3599 cave->amoeba_2_time = 0;
3600 if (cave->amoeba_2_time == 0)
3601 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3604 // check for player hatching.
3605 start_signal = FALSE;
3607 // if not the c64 scheduling, but the correct frametime is used,
3608 // hatching delay should always be decremented.
3609 // otherwise, the if (millisecs...) condition below will set this.
3610 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3612 // NON-C64 scheduling
3613 if (cave->hatching_delay_frame > 0)
3615 // for milliseconds-based, non-c64 schedulings, hatching delay means frames.
3616 cave->hatching_delay_frame--;
3617 if (cave->hatching_delay_frame == 0)
3618 start_signal = TRUE;
3624 if (cave->hatching_delay_time > 0)
3626 // for c64 schedulings, hatching delay means milliseconds.
3627 cave->hatching_delay_time -= cave->speed;
3628 if (cave->hatching_delay_time <= 0)
3630 cave->hatching_delay_time = 0;
3631 start_signal = TRUE;
3636 // if decremented hatching, and it became zero:
3639 // THIS IS THE CAVE START SIGNAL
3641 // record that now the cave is in its normal state
3642 cave->hatched = TRUE;
3644 // if diamonds needed is below zero, we count the available diamonds now.
3645 gd_cave_count_diamonds(cave);
3647 // setup direction auto change
3648 if (cave->creatures_direction_auto_change_time)
3650 cave->creatures_direction_will_change =
3651 cave->creatures_direction_auto_change_time * cave->timing_factor;
3653 if (cave->creatures_direction_auto_change_on_start)
3654 cave->creatures_backwards = !cave->creatures_backwards;
3657 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3661 if (cave->biters_wait_frame == 0)
3662 cave->biters_wait_frame = cave->biter_delay_frame;
3664 cave->biters_wait_frame--;
3666 // replicators delay
3667 if (cave->replicators_wait_frame == 0)
3668 cave->replicators_wait_frame = cave->replicator_delay_frame;
3670 cave->replicators_wait_frame--;
3673 // ============================================================================
3675 // ============================================================================
3678 // check if cave failed by timeout is done in main game engine
3680 // check if cave failed by timeout
3681 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3683 gd_cave_clear_sounds(cave);
3684 cave->player_state = GD_PL_TIMEOUT;
3685 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3689 // set these for drawing.
3690 cave->last_direction = player_move;
3692 // here we remember last movements for animation. this is needed here,
3693 // as animation is in sync with the game, not the keyboard directly.
3694 // (for example, after exiting the cave, the player was "running" in the
3695 // original, till bonus points were counted for remaining time and so on.
3696 if (player_move == GD_MV_LEFT ||
3697 player_move == GD_MV_UP_LEFT ||
3698 player_move == GD_MV_DOWN_LEFT)
3699 cave->last_horizontal_direction = GD_MV_LEFT;
3701 if (player_move == GD_MV_RIGHT ||
3702 player_move == GD_MV_UP_RIGHT ||
3703 player_move == GD_MV_DOWN_RIGHT)
3704 cave->last_horizontal_direction = GD_MV_RIGHT;
3706 cave->frame++; // XXX
3709 void set_initial_cave_speed(GdCave *cave)
3714 // set cave get function; to implement perfect or lineshifting borders
3715 if (cave->lineshift)
3716 cave->getp = getp_shift;
3718 cave->getp = getp_perfect;
3720 // check whether to scan the first and last line
3721 if (cave->border_scan_first_and_last)
3732 for (y = ymin; y <= ymax; y++)
3734 for (x = 0; x < cave->w; x++)
3736 // add the ckdelay correction value for every element seen.
3737 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3741 // update timing calculated by iterating and counting elements
3742 update_cave_speed(cave);