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, O_EXPLODE_1);
762 creature_explode(cave, x, y, cave->butterfly_explode_to);
769 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
776 creature_explode(cave, x, y, cave->firefly_explode_to);
779 case O_ALT_FIREFLY_1:
780 case O_ALT_FIREFLY_2:
781 case O_ALT_FIREFLY_3:
782 case O_ALT_FIREFLY_4:
783 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
789 case O_PLAYER_STIRRING:
790 case O_PLAYER_ROCKET_LAUNCHER:
791 case O_PLAYER_PNEUMATIC_LEFT:
792 case O_PLAYER_PNEUMATIC_RIGHT:
793 creature_explode(cave, x, y, O_EXPLODE_1);
800 creature_explode(cave, x, y, cave->stonefly_explode_to);
807 creature_explode(cave, x, y, cave->dragonfly_explode_to);
815 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
817 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
821 player eats specified object.
822 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
823 returns other element if something other appears there and he can't move.
824 cave pointer is needed to know the diamond values.
826 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
833 cave->diamond_key_collected = TRUE;
834 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
839 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
844 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
849 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
856 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
863 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
870 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
875 case O_CREATURE_SWITCH: // creatures change direction.
876 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
877 cave->creatures_backwards = !cave->creatures_backwards;
880 case O_EXPANDING_WALL_SWITCH: // expanding wall change direction.
881 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
882 cave->expanding_wall_changed = !cave->expanding_wall_changed;
885 case O_BITER_SWITCH: // biter change delay
886 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
887 cave->biter_delay_frame++;
888 if (cave->biter_delay_frame == 4)
889 cave->biter_delay_frame = 0;
892 case O_REPLICATOR_SWITCH: // replicator on/off switch
893 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
894 cave->replicators_active = !cave->replicators_active;
897 case O_CONVEYOR_SWITCH: // conveyor belts on/off
898 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
899 cave->conveyor_belts_active = !cave->conveyor_belts_active;
902 case O_CONVEYOR_DIR_SWITCH: // conveyor belts switch direction
903 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
904 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
910 case O_STEEL_EATABLE:
911 case O_BRICK_EATABLE:
912 case O_DIRT_SLOPED_UP_RIGHT:
913 case O_DIRT_SLOPED_UP_LEFT:
914 case O_DIRT_SLOPED_DOWN_LEFT:
915 case O_DIRT_SLOPED_DOWN_RIGHT:
918 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
922 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
923 cave->sweet_eaten = TRUE;
926 case O_PNEUMATIC_HAMMER:
927 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
928 cave->got_pneumatic_hammer = TRUE;
933 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
934 cave->time += cave->time_bonus * cave->timing_factor;
935 if (cave->time > cave->max_time * cave->timing_factor)
936 cave->time -= cave->max_time * cave->timing_factor;
937 // no space, rather a dirt remains there...
941 case O_FLYING_DIAMOND:
942 // prevent diamond sounds for O_SKELETON (see below)
943 if (x != -1 && y != -1)
944 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
945 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
947 cave->score += cave->diamond_value;
948 cave->diamonds_collected++;
950 if (cave->diamonds_needed == cave->diamonds_collected)
952 cave->gate_open = TRUE;
954 // extra is worth more points.
955 cave->diamond_value = cave->extra_diamond_value;
957 cave->gate_open_flash = 1;
958 cave->sound3 = GD_S_CRACKING;
959 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
964 cave->skeletons_collected++;
966 // as if player got a diamond
967 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
968 player_get_element(cave, O_DIAMOND, -1, -1);
970 // _after_ calling get_element for the fake diamonds, so we overwrite its sounds
971 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
976 cave->player_state = GD_PL_EXITED; // player now exits the cave!
980 case O_LAVA: // player goes into lava, as if it was space
981 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
985 // the object will remain there.
991 process a crazy dream-style teleporter.
992 called from gd_cave_iterate, for a player or a player_bomb.
993 player is standing at px, py, and trying to move in the direction player_move,
994 where there is a teleporter at (tx_start, ty_start). we check the whole cave,
995 from (tx_start + 1, ty_start), till we get back to (tx_start, ty_start) (by wrapping
996 around). the first teleporter we find, and which is suitable, will be the destination.
997 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
999 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
1001 // start at teleporter position (not at player position!)
1002 int tx_start = px + gd_dx[player_move];
1003 int ty_start = py + gd_dy[player_move];
1009 // jump to next element; wrap around columns and rows.
1021 // if we found a teleporter...
1022 if (get(cave, tx, ty) == O_TELEPORTER &&
1023 is_space_dir(cave, tx, ty, player_move))
1025 // new player appears near teleporter found
1026 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1028 // current player disappears
1029 store(cave, px, py, O_SPACE);
1031 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1033 return TRUE; // return true as teleporter worked
1036 // loop until we get back to original coordinates
1037 while (tx != tx_start || ty != ty_start);
1039 // return false as we did not find any usable teleporter
1044 try to push an element.
1045 returns true if the push is possible; also does move the specified _element_.
1046 up to the caller to move the _player_itself_.
1048 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1051 GdElement what = get_dir(cave, x, y, player_move);
1053 // gravity for falling wall, bladder, ...
1054 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1060 case O_WAITING_STONE:
1063 case O_CHASING_STONE:
1065 case O_FLYING_STONE:
1067 // pushing some kind of stone or nut
1068 // directions possible: 90degrees cw or ccw to current gravity.
1069 // only push if player dir is orthogonal to gravity,
1070 // ie. gravity down, pushing left & right possible
1071 if (player_move == ccw_fourth[cave->gravity] ||
1072 player_move == cw_fourth[cave->gravity])
1078 // different probabilities for different elements.
1081 case O_WAITING_STONE:
1082 // waiting stones are light, can always push
1086 case O_CHASING_STONE:
1087 // chasing can be pushed if player is turbo
1088 if (cave->sweet_eaten)
1093 // mega may(!) be pushed if player is turbo
1094 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1100 case O_FLYING_STONE:
1102 if (cave->sweet_eaten)
1103 prob = cave->pushing_stone_prob_sweet; // probability with sweet
1105 prob = cave->pushing_stone_prob; // probability without sweet.
1112 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1113 gd_rand_int_range(cave->random, 0, 1000000) < prob)
1115 // if decided that he will be able to push,
1116 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1117 play_sound_of_element_pushing(cave, what, x, y);
1132 // pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1133 // not an O_BLADDER_x.
1134 // there is no "delayed" state of a bladder, so we use store_dir_no_scanned!
1136 // first check: we cannot push a bladder "up"
1137 if (player_move != opposite[grav_compat])
1139 // pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check.
1140 // player moving in the direction of gravity.
1144 if (player_move == grav_compat)
1146 // pushing bladder down
1147 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1148 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1149 // if no space to push down, maybe left (down-left to player)
1150 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1152 // left is "down, turned right (cw)"
1153 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1154 // if not, maybe right (down-right to player)
1155 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1156 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1159 // pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check.
1163 else if (player_move == cw_fourth[grav_compat])
1165 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) // pushing it left
1166 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1167 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) // maybe down, and player will move left
1168 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1169 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) // maybe up, and player will move left
1170 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1173 // pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check.
1177 else if (player_move == ccw_fourth[grav_compat])
1179 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) // pushing it right
1180 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1181 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) // maybe down, and player will move right
1182 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1183 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) // maybe up, and player will move right
1184 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1188 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1193 // a box is only pushed with the fire pressed
1196 // but always with 100% probability
1197 switch (player_move)
1203 // pushing in some dir, two steps in that dir - is there space?
1204 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1207 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1209 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1214 // push in no other directions possible
1220 // pushing of other elements not possible
1228 // from the key press booleans, create a direction
1229 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1231 GdDirection player_move;
1233 // from the key press booleans, create a direction
1235 player_move = GD_MV_UP_RIGHT;
1236 else if (down && right)
1237 player_move = GD_MV_DOWN_RIGHT;
1238 else if (down && left)
1239 player_move = GD_MV_DOWN_LEFT;
1240 else if (up && left)
1241 player_move = GD_MV_UP_LEFT;
1243 player_move = GD_MV_UP;
1245 player_move = GD_MV_DOWN;
1247 player_move = GD_MV_LEFT;
1249 player_move = GD_MV_RIGHT;
1251 player_move = GD_MV_STILL;
1256 // clear these to no sound; and they will be set during iteration.
1257 void gd_cave_clear_sounds(GdCave *cave)
1259 cave->sound1 = GD_S_NONE;
1260 cave->sound2 = GD_S_NONE;
1261 cave->sound3 = GD_S_NONE;
1264 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1265 GdElement falling_element)
1267 if (cave->gravity_disabled)
1270 if (is_space_dir(cave, x, y, falling_direction))
1272 // beginning to fall
1273 play_sound_of_element(cave, get(cave, x, y), x, y);
1274 move(cave, x, y, falling_direction, falling_element);
1277 // check if it is on a sloped element, and it can roll.
1278 // for example, sloped wall looks like:
1281 // this is tagged as sloped up&left.
1282 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1283 // then check the direction to roll (left or right)
1284 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1285 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1287 // rolling down, if sitting on a sloped object
1288 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1289 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1290 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1292 // rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1293 // so here we use cw_fourth
1294 play_sound_of_element(cave, get(cave, x, y), x, y);
1295 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1297 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1298 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1299 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1302 play_sound_of_element(cave, get(cave, x, y), x, y);
1303 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1308 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1310 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1311 cave->voodoo_dies_by_stone)
1313 // this is a 1stB-style vodo. explodes by stone, collects diamonds
1314 explode_dir(cave, x, y, fall_dir);
1321 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1323 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1324 cave->voodoo_collects_diamonds)
1326 // this is a 1stB-style voodoo. explodes by stone, collects diamonds
1327 player_get_element(cave, O_DIAMOND, x, y); // as if player got diamond
1328 store(cave, x, y, O_SPACE); // diamond disappears
1335 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1336 GdDirection fall_dir, GdElement bouncing)
1338 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1339 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1342 store(cave, x, y, bouncing);
1343 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1345 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1353 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1354 GdDirection fall_dir, GdElement magic)
1356 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1358 play_sound_of_element(cave, O_DIAMOND, x, y); // always play diamond sound
1360 if (cave->magic_wall_state == GD_MW_DORMANT)
1361 cave->magic_wall_state = GD_MW_ACTIVE;
1363 if (cave->magic_wall_state == GD_MW_ACTIVE &&
1364 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1366 // if magic wall active and place underneath, it turns element
1367 // into anything the effect says to do.
1368 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1371 // active or non-active or anything, element falling in will always disappear
1372 store(cave, x, y, O_SPACE);
1380 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1382 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1384 explode_dir(cave, x, y, fall_dir);
1391 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1392 GdDirection fall_dir, GdElement bouncing)
1394 if (is_space_dir(cave, x, y, fall_dir))
1397 move(cave, x, y, fall_dir, get(cave, x, y));
1402 // check if it is on a sloped element, and it can roll.
1403 // for example, sloped wall looks like:
1406 // this is tagged as sloped up&left.
1407 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1408 // then check the direction to roll (left or right)
1409 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1411 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1413 // sloped element, falling to left or right
1414 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1415 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1416 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1418 play_sound_of_element(cave, get(cave, x, y), x, y);
1420 // try to roll left first - see O_STONE to understand why cw_fourth
1421 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1423 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1424 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1425 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1427 play_sound_of_element(cave, get(cave, x, y), x, y);
1429 // if not, try to roll right
1430 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1434 // cannot roll in any direction, so it stops
1435 play_sound_of_element(cave, get(cave, x, y), x, y);
1436 store(cave, x, y, bouncing);
1442 // any other element, stops
1443 play_sound_of_element(cave, get(cave, x, y), x, y);
1444 store(cave, x, y, bouncing);
1448 static void update_cave_speed(GdCave *cave)
1450 // update timing calculated by iterating and counting elements which were slow to process on c64
1451 switch (cave->scheduling)
1453 case GD_SCHEDULING_MILLISECONDS:
1454 // cave->speed already contains the milliseconds value, do not touch it
1457 case GD_SCHEDULING_BD1:
1458 if (!cave->intermission)
1459 // non-intermissions
1460 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1462 // intermissions were quicker, as only lines 1-12 were processed by the engine.
1463 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1466 case GD_SCHEDULING_BD1_ATARI:
1467 // about 20ms/frame faster than c64 version
1468 if (!cave->intermission)
1469 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); // non-intermissions
1471 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); // for intermissions
1474 case GD_SCHEDULING_BD2:
1476 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1479 case GD_SCHEDULING_PLCK:
1480 // 65 is totally empty cave in construction kit, with delay = 0)
1481 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1484 case GD_SCHEDULING_BD2_PLCK_ATARI:
1485 // a really fast engine; timing works like c64 plck.
1486 // 40 ms was measured in the construction kit, with delay = 0
1487 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1490 case GD_SCHEDULING_CRDR:
1491 if (cave->hammered_walls_reappear) // this made the engine very slow.
1492 cave->ckdelay += 60000;
1493 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1496 case GD_SCHEDULING_MAX:
1502 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1509 // amoeba found to be enclosed. if not, this is cleared
1510 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1512 // counting the number of amoebas. after scan, check if too much
1513 int amoeba_count, amoeba_2_count;
1515 // cave scan found water - for sound
1516 boolean found_water;
1518 boolean inbox_toggle;
1519 boolean start_signal;
1521 // gravity for falling wall, bladder, ...
1522 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1524 // directions for o_something_1, 2, 3 and 4 (creatures)
1525 static const GdDirection creature_dir[] =
1532 static const GdDirection creature_chdir[] =
1539 int time_decrement_sec;
1541 // biters eating elements preference, they try to go in this order
1542 GdElement biter_try[] =
1549 boolean amoeba_sound, magic_sound;
1551 gd_cave_clear_sounds(cave);
1553 // if diagonal movements not allowed,
1554 // horizontal movements have precedence. [BROADRIBB]
1555 if (!cave->diagonal_movements)
1557 switch (player_move)
1559 case GD_MV_UP_RIGHT:
1560 case GD_MV_DOWN_RIGHT:
1561 player_move = GD_MV_RIGHT;
1565 case GD_MV_DOWN_LEFT:
1566 player_move = GD_MV_LEFT;
1570 // no correction needed
1575 // set cave get function; to implement perfect or lineshifting borders
1576 if (cave->lineshift)
1578 cave->getp = getp_shift;
1579 cave->getx = getx_shift;
1580 cave->gety = gety_shift;
1584 cave->getp = getp_perfect;
1585 cave->getx = getx_perfect;
1586 cave->gety = gety_perfect;
1589 // increment this. if the scan routine comes across player, clears it (sets to zero).
1590 if (cave->player_seen_ago < 100)
1591 cave->player_seen_ago++;
1593 if (cave->pneumatic_hammer_active_delay > 0)
1594 cave->pneumatic_hammer_active_delay--;
1596 // inboxes and outboxes flash with the rhythm of the game, not the display.
1597 // also, a player can be born only from an open, not from a steel-wall-like inbox.
1598 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1599 inbox_toggle = cave->inbox_flash_toggle;
1601 if (cave->gate_open_flash > 0)
1602 cave->gate_open_flash--;
1604 // score collected this frame
1607 // suicide only kills the active player
1608 // player_x, player_y was set by the previous iterate routine, or the cave setup.
1609 // we must check if there is a player or not - he may have exploded or something like that
1610 if (suicide && cave->player_state == GD_PL_LIVING &&
1611 is_player(cave, cave->player_x, cave->player_y))
1612 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1614 // check for walls reappearing
1615 if (cave->hammered_reappear)
1617 for (y = 0; y < cave->h; y++)
1619 for (x = 0; x < cave->w; x++)
1621 // timer for the cell > 0?
1622 if (cave->hammered_reappear[y][x] > 0)
1625 cave->hammered_reappear[y][x]--;
1627 // check if it became zero
1628 if (cave->hammered_reappear[y][x] == 0)
1630 store(cave, x, y, O_BRICK);
1631 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1638 // variables to check during the scan
1640 // will be set to false if any of the amoeba is found free.
1641 amoeba_found_enclosed = TRUE;
1642 amoeba_2_found_enclosed = TRUE;
1645 found_water = FALSE;
1647 time_decrement_sec = 0;
1649 // check whether to scan the first and last line
1650 if (cave->border_scan_first_and_last)
1661 // the cave scan routine
1662 for (y = ymin; y <= ymax; y++)
1664 for (x = 0; x < cave->w; x++)
1666 // if we find a scanned element, change it to the normal one, and that's all.
1667 // this is required, for example for chasing stones, which have moved, always passing slime!
1668 if (get(cave, x, y) & SCANNED)
1670 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1675 // add the ckdelay correction value for every element seen.
1676 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1678 switch (get(cave, x, y))
1680 // ============================================================================
1682 // ============================================================================
1685 if (cave->kill_player)
1687 explode (cave, x, y);
1691 cave->player_seen_ago = 0;
1692 // bd4 intermission caves have many players. so if one of them has exited,
1693 // do not change the flag anymore. so this if () is needed
1694 if (cave->player_state != GD_PL_EXITED)
1695 cave->player_state = GD_PL_LIVING;
1697 // check for pneumatic hammer things
1698 // 1) press fire, 2) have pneumatic hammer 4) space on left or right
1699 // for hammer 5) stand on something
1700 if (player_fire && cave->got_pneumatic_hammer &&
1701 is_space_dir(cave, x, y, player_move) &&
1702 !is_space_dir(cave, x, y, GD_MV_DOWN))
1704 if (player_move == GD_MV_LEFT &&
1705 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1707 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1708 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1709 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1713 if (player_move == GD_MV_RIGHT &&
1714 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1716 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1717 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1718 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1723 if (player_move != GD_MV_STILL)
1725 // only do every check if he is not moving
1726 GdElement what = get_dir(cave, x, y, player_move);
1727 GdElement remains = what;
1730 // if we are 'eating' a teleporter, and the function returns true
1731 // (teleporting worked), break here
1732 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1735 // try to push element; if successful, break
1736 push = do_push(cave, x, y, player_move, player_fire);
1746 // if its a bomb, remember he now has one.
1747 // we do not change the "remains" and "what" variables,
1748 // so that part of the code will be ineffective
1749 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1750 store_dir(cave, x, y, player_move, O_SPACE);
1753 store(cave, x, y, O_PLAYER_BOMB);
1755 move(cave, x, y, player_move, O_PLAYER_BOMB);
1758 case O_ROCKET_LAUNCHER:
1759 // if its a rocket launcher, remember he now has one.
1760 // we do not change the "remains" and "what" variables,
1761 // so that part of the code will be ineffective
1762 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1763 store_dir(cave, x, y, player_move, O_SPACE);
1766 store(cave, x, y, O_PLAYER_ROCKET_LAUNCHER);
1768 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
1772 // we do not change the "remains" and "what" variables,
1773 // so that part of the code will be ineffective
1774 if (!player_fire && !cave->gravity_switch_active &&
1775 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1777 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1778 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1779 cave->gravity_disabled = TRUE;
1783 case O_GRAVITY_SWITCH:
1784 // (we cannot use player_get for this as it does not have player_move parameter)
1785 // only allow changing direction if the new dir is not diagonal
1786 if (cave->gravity_switch_active &&
1787 (player_move == GD_MV_LEFT ||
1788 player_move == GD_MV_RIGHT ||
1789 player_move == GD_MV_UP ||
1790 player_move == GD_MV_DOWN))
1792 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1793 cave->gravity_will_change =
1794 cave->gravity_change_time * cave->timing_factor;
1795 cave->gravity_next_direction = player_move;
1796 cave->gravity_switch_active = FALSE;
1801 // get element - process others.
1802 // if cannot get, player_get_element will return the same
1803 remains = player_get_element(cave, what, x, y);
1808 if (remains != what || remains == O_SPACE)
1810 // if anything changed, apply the change.
1812 // if snapping anything and we have snapping explosions set.
1813 // but these is not true for pushing.
1814 if (remains == O_SPACE && player_fire && !push)
1815 remains = cave->snap_element;
1817 if (remains != O_SPACE || player_fire)
1818 // if any other element than space, player cannot move.
1819 // also if pressing fire, will not move.
1820 store_dir(cave, x, y, player_move, remains);
1822 // if space remains there, the player moves.
1823 move(cave, x, y, player_move, O_PLAYER);
1829 // much simpler; cannot steal stones
1830 if (cave->kill_player)
1832 explode(cave, x, y);
1836 cave->player_seen_ago = 0;
1837 // bd4 intermission caves have many players. so if one of them has exited,
1838 // do not change the flag anymore. so this if () is needed
1839 if (cave->player_state != GD_PL_EXITED)
1840 cave->player_state = GD_PL_LIVING;
1842 if (player_move != GD_MV_STILL)
1844 // if the player does not move, nothing to do
1845 GdElement what = get_dir(cave, x, y, player_move);
1846 GdElement remains = what;
1850 // placing a bomb into empty space or dirt
1851 if (is_space_dir(cave, x, y, player_move) ||
1852 is_element_dir(cave, x, y, player_move, O_DIRT))
1854 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1856 // placed bomb, he is normal player again
1857 store(cave, x, y, O_PLAYER);
1858 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1863 // pushing and collecting
1864 // if we are 'eating' a teleporter, and the function returns true
1865 // (teleporting worked), break here
1866 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1869 // player fire is false...
1870 if (do_push(cave, x, y, player_move, FALSE))
1878 case O_GRAVITY_SWITCH:
1879 // (we cannot use player_get for this as it does not have
1880 // player_move parameter)
1881 // only allow changing direction if the new dir is not diagonal
1882 if (cave->gravity_switch_active &&
1883 (player_move == GD_MV_LEFT ||
1884 player_move == GD_MV_RIGHT ||
1885 player_move == GD_MV_UP ||
1886 player_move == GD_MV_DOWN))
1888 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1889 cave->gravity_will_change =
1890 cave->gravity_change_time * cave->timing_factor;
1891 cave->gravity_next_direction = player_move;
1892 cave->gravity_switch_active = FALSE;
1897 // get element. if cannot get, player_get_element will return the same
1898 remains = player_get_element (cave, what, x, y);
1903 // if element changed, OR there is space, move.
1904 if (remains != what || remains == O_SPACE)
1906 // if anything changed, apply the change.
1907 move(cave, x, y, player_move, O_PLAYER_BOMB);
1912 case O_PLAYER_ROCKET_LAUNCHER:
1913 // much simpler; cannot snap-push stones
1914 if (cave->kill_player)
1916 explode(cave, x, y);
1920 cave->player_seen_ago = 0;
1921 // bd4 intermission caves have many players. so if one of them has exited,
1922 // do not change the flag anymore. so this if () is needed
1923 if (cave->player_state != GD_PL_EXITED)
1924 cave->player_state = GD_PL_LIVING;
1927 if (player_move != GD_MV_STILL)
1929 // if the player does not move, nothing to do
1930 GdElement what = get_dir(cave, x, y, player_move);
1931 GdElement remains = what;
1933 // to fire a rocket, diagonal movement should not be allowed.
1934 // so either x or y must be zero
1937 // placing a rocket into empty space
1938 if (is_space_dir(cave, x, y, player_move))
1940 switch (player_move)
1943 store_dir(cave, x, y, player_move, O_ROCKET_1);
1944 if (!cave->infinite_rockets)
1945 store(cave, x, y, O_PLAYER);
1949 store_dir(cave, x, y, player_move, O_ROCKET_2);
1950 if (!cave->infinite_rockets)
1951 store(cave, x, y, O_PLAYER);
1955 store_dir(cave, x, y, player_move, O_ROCKET_3);
1956 if (!cave->infinite_rockets)
1957 store(cave, x, y, O_PLAYER);
1961 store_dir(cave, x, y, player_move, O_ROCKET_4);
1962 if (!cave->infinite_rockets)
1963 store(cave, x, y, O_PLAYER);
1967 // cannot fire in other directions
1971 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1974 // a player with rocket launcher cannot snap elements, so stop here
1978 // pushing and collecting
1979 // if we are 'eating' a teleporter, and the function returns true
1980 // (teleporting worked), break here
1981 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1984 // player fire is false...
1985 if (do_push(cave, x, y, player_move, FALSE))
1991 // get element. if cannot get, player_get_element will return the same
1992 remains = player_get_element(cave, what, x, y);
1995 // if something changed, OR there is space, move.
1996 if (remains != what || remains == O_SPACE)
1998 // if anything changed, apply the change.
1999 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
2004 case O_PLAYER_STIRRING:
2005 if (cave->kill_player)
2007 explode(cave, x, y);
2011 // stirring sound, if no other walking sound or explosion
2012 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
2014 cave->player_seen_ago = 0;
2015 // bd4 intermission caves have many players. so if one of them has exited,
2016 // do not change the flag anymore. so this if () is needed
2017 if (cave->player_state != GD_PL_EXITED)
2018 cave->player_state = GD_PL_LIVING;
2022 // player "exits" stirring the pot by pressing fire
2023 cave->gravity_disabled = FALSE;
2024 store(cave, x, y, O_PLAYER);
2025 cave->gravity_switch_active = TRUE;
2029 // player holding pneumatic hammer
2030 case O_PLAYER_PNEUMATIC_LEFT:
2031 case O_PLAYER_PNEUMATIC_RIGHT:
2032 // usual player stuff
2033 if (cave->kill_player)
2035 explode(cave, x, y);
2039 cave->player_seen_ago = 0;
2040 if (cave->player_state != GD_PL_EXITED)
2041 cave->player_state = GD_PL_LIVING;
2043 // if hammering time is up, becomes a normal player again.
2044 if (cave->pneumatic_hammer_active_delay == 0)
2045 store(cave, x, y, O_PLAYER);
2048 // the active pneumatic hammer itself
2049 case O_PNEUMATIC_ACTIVE_RIGHT:
2050 case O_PNEUMATIC_ACTIVE_LEFT:
2051 if (cave->pneumatic_hammer_active_delay == 0)
2055 // pneumatic hammer element disappears
2056 store(cave, x, y, O_SPACE);
2058 // which is the new element which appears after that one is hammered?
2059 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
2061 // if there is a new element, display it
2062 // O_NONE might be returned, for example if the element being
2063 // hammered explodes during hammering (by a nearby explosion)
2064 if (new_elem != O_NONE)
2066 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
2068 // and if walls reappear, remember it in array
2069 if (cave->hammered_walls_reappear)
2073 wall_y = (y + 1) % cave->h;
2074 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
2080 // ============================================================================
2081 // S T O N E S, D I A M O N D S
2082 // ============================================================================
2084 case O_STONE: // standing stone
2085 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
2088 case O_MEGA_STONE: // standing mega_stone
2089 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
2092 case O_DIAMOND: // standing diamond
2093 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
2096 case O_NUT: // standing nut
2097 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
2100 case O_DIRT_BALL: // standing dirt ball
2101 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
2104 case O_DIRT_LOOSE: // standing loose dirt
2105 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
2108 case O_FLYING_STONE: // standing stone
2109 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
2112 case O_FLYING_DIAMOND: // standing diamond
2113 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2116 // ============================================================================
2117 // 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
2118 // ============================================================================
2120 case O_DIRT_BALL_F: // falling dirt ball
2121 if (!cave->gravity_disabled)
2122 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2125 case O_DIRT_LOOSE_F: // falling loose dirt
2126 if (!cave->gravity_disabled)
2127 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2130 case O_STONE_F: // falling stone
2131 if (!cave->gravity_disabled)
2133 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2136 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2139 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2142 if (do_fall_try_crush(cave, x, y, cave->gravity))
2145 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2149 case O_MEGA_STONE_F: // falling mega
2150 if (!cave->gravity_disabled)
2152 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2155 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2158 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2161 if (do_fall_try_crush(cave, x, y, cave->gravity))
2164 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2168 case O_DIAMOND_F: // falling diamond
2169 if (!cave->gravity_disabled)
2171 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2174 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2177 if (do_fall_try_crush(cave, x, y, cave->gravity))
2180 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2184 case O_NUT_F: // falling nut
2185 if (!cave->gravity_disabled)
2187 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2190 if (do_fall_try_crush(cave, x, y, cave->gravity))
2193 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2197 case O_FLYING_STONE_F: // falling stone
2198 if (!cave->gravity_disabled)
2200 GdDirection fall_dir = opposite[cave->gravity];
2202 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2205 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2208 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2211 if (do_fall_try_crush(cave, x, y, fall_dir))
2214 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2218 case O_FLYING_DIAMOND_F: // falling diamond
2219 if (!cave->gravity_disabled)
2221 GdDirection fall_dir = opposite[cave->gravity];
2223 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2226 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2229 if (do_fall_try_crush(cave, x, y, fall_dir))
2232 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2236 // ============================================================================
2237 // N I T R O P A C K
2238 // ============================================================================
2240 case O_NITRO_PACK: // standing nitro pack
2241 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2244 case O_NITRO_PACK_F: // falling nitro pack
2245 if (!cave->gravity_disabled)
2247 if (is_space_dir(cave, x, y, cave->gravity)) // if space, falling further
2248 move(cave, x, y, cave->gravity, get(cave, x, y));
2249 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2251 // try magic wall; if true, function did the work
2253 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2255 // falling on a dirt, it does NOT explode - just stops at its place.
2256 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2257 store(cave, x, y, O_NITRO_PACK);
2260 // falling on any other element it explodes
2261 explode(cave, x, y);
2265 case O_NITRO_PACK_EXPLODE: // a triggered nitro pack
2266 explode(cave, x, y);
2269 // ============================================================================
2270 // C R E A T U R E S
2271 // ============================================================================
2277 // if cannot move in any direction, becomes an enclosed cow
2278 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2279 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2280 store(cave, x, y, O_COW_ENCLOSED_1);
2283 // THIS IS THE CREATURE MOVE thing copied.
2284 const GdDirection *creature_move;
2285 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2286 GdElement base; // base element number (which is like O_***_1)
2287 int dir, dirn, dirp; // direction
2291 dir = get(cave, x, y)-base; // facing where
2292 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2294 // now change direction if backwards
2295 if (cave->creatures_backwards)
2300 dirn = (dir + 3) & 3; // fast turn
2301 dirp = (dir + 1) & 3; // slow turn
2305 dirn = (dir + 1) & 3; // fast turn
2306 dirp = (dir + 3) & 3; // slow turn
2309 if (is_space_dir(cave, x, y, creature_move[dirn]))
2310 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2311 else if (is_space_dir(cave, x, y, creature_move[dir]))
2312 move(cave, x, y, creature_move[dir], base + dir); // go on
2314 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2318 // enclosed cows wait some time before turning to a skeleton
2319 case O_COW_ENCLOSED_1:
2320 case O_COW_ENCLOSED_2:
2321 case O_COW_ENCLOSED_3:
2322 case O_COW_ENCLOSED_4:
2323 case O_COW_ENCLOSED_5:
2324 case O_COW_ENCLOSED_6:
2325 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2326 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2327 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2328 is_space_dir(cave, x, y, GD_MV_DOWN))
2329 store(cave, x, y, O_COW_1);
2334 case O_COW_ENCLOSED_7:
2335 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2336 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2337 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2338 is_space_dir(cave, x, y, GD_MV_DOWN))
2339 store(cave, x, y, O_COW_1);
2341 store(cave, x, y, O_SKELETON);
2348 case O_ALT_FIREFLY_1:
2349 case O_ALT_FIREFLY_2:
2350 case O_ALT_FIREFLY_3:
2351 case O_ALT_FIREFLY_4:
2356 case O_ALT_BUTTER_1:
2357 case O_ALT_BUTTER_2:
2358 case O_ALT_BUTTER_3:
2359 case O_ALT_BUTTER_4:
2364 // check if touches a voodoo
2365 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2366 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2367 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2368 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2369 cave->voodoo_touched = TRUE;
2371 // check if touches something bad and should explode (includes voodoo by the flags)
2372 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2373 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2374 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2375 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2376 explode (cave, x, y);
2380 const GdDirection *creature_move;
2381 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2382 GdElement base = -1; // base element number (which is like O_***_1)
2383 int dir, dirn, dirp; // direction
2385 if (get(cave, x, y) >= O_FIREFLY_1 &&
2386 get(cave, x, y) <= O_FIREFLY_4)
2388 else if (get(cave, x, y) >= O_BUTTER_1 &&
2389 get(cave, x, y) <= O_BUTTER_4)
2391 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2392 get(cave, x, y) <= O_STONEFLY_4)
2393 base = O_STONEFLY_1;
2394 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2395 get(cave, x, y) <= O_ALT_FIREFLY_4)
2396 base = O_ALT_FIREFLY_1;
2397 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2398 get(cave, x, y) <= O_ALT_BUTTER_4)
2399 base = O_ALT_BUTTER_1;
2401 dir = get(cave, x, y) - base; // facing where
2402 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2404 // now change direction if backwards
2405 if (cave->creatures_backwards)
2410 dirn = (dir + 3) & 3; // fast turn
2411 dirp = (dir + 1) & 3; // slow turn
2415 dirn = (dir + 1) & 3; // fast turn
2416 dirp = (dir + 3) & 3; // slow turn
2419 if (is_space_dir(cave, x, y, creature_move[dirn]))
2420 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2421 else if (is_space_dir(cave, x, y, creature_move[dir]))
2422 move(cave, x, y, creature_move[dir], base + dir); // go on
2424 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2428 case O_WAITING_STONE:
2429 if (is_space_dir(cave, x, y, grav_compat))
2431 // beginning to fall
2433 move(cave, x, y, grav_compat, O_CHASING_STONE);
2435 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2437 // rolling down a brick wall or a stone
2438 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2439 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2440 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2442 // maybe rolling left - see case O_STONE to understand why we use cw_fourth here
2443 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2445 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2446 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2447 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2450 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2455 case O_CHASING_STONE:
2457 int px = cave->px[0];
2458 int py = cave->py[0];
2459 boolean horizontal = gd_rand_boolean(cave->random);
2460 boolean dont_move = FALSE;
2468 // ------------------------------------------------------------
2469 // check for a horizontal movement
2470 // ------------------------------------------------------------
2473 // if coordinates are the same
2475 horizontal = !horizontal;
2482 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2484 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2488 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2490 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2499 horizontal = !horizontal;
2507 // ------------------------------------------------------------
2508 // check for a vertical movement
2509 // ------------------------------------------------------------
2512 // if coordinates are the same
2514 horizontal = !horizontal;
2520 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2522 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2526 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2528 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2537 horizontal = !horizontal;
2550 // if we should move in both directions, but can not move in any, stop.
2555 // check for horizontal
2558 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2559 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2560 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2561 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2562 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2563 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2567 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2568 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2569 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2570 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2571 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2572 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2577 // check for vertical
2580 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2581 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2582 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2583 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2584 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2585 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2589 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2590 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2591 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2592 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2593 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2594 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2602 if (cave->replicators_wait_frame == 0 &&
2603 cave->replicators_active &&
2604 !cave->gravity_disabled)
2606 // only replicate, if space is under it.
2607 // do not replicate players!
2608 // also obeys gravity settings.
2609 // only replicate element if it is not a scanned one
2610 // do not replicate space... that condition looks like it
2611 // makes no sense, but otherwise it generates SCANNED spaces,
2612 // which cannot be "collected" by the player, so he cannot run
2613 // under a replicator
2614 if (is_space_dir(cave, x, y, cave->gravity) &&
2615 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2616 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2618 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2619 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2628 if (cave->biters_wait_frame == 0)
2630 static GdDirection biter_move[] =
2638 // direction, last two bits 0..3
2639 int dir = get(cave, x, y) - O_BITER_1;
2640 int dirn = (dir + 3) & 3;
2641 int dirp = (dir + 1) & 3;
2643 GdElement made_sound_of = O_NONE;
2645 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2647 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2649 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2650 if (biter_try[i] != O_SPACE)
2651 made_sound_of = O_BITER_1; // sound of a biter eating
2654 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2656 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2657 if (biter_try[i] != O_SPACE)
2658 made_sound_of = O_BITER_1; // sound of a biter eating
2661 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2663 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2664 if (biter_try[i] != O_SPACE)
2665 made_sound_of = O_BITER_1; // sound of a biter eating
2670 if (i == ARRAY_SIZE(biter_try))
2671 // i = number of elements in array: could not move, so just turn
2672 store(cave, x, y, O_BITER_1 + dirp);
2673 else if (biter_try[i] == O_STONE)
2675 // if there was a stone there, where we moved...
2676 // do not eat stones, just throw them back
2677 store(cave, x, y, O_STONE);
2678 made_sound_of = O_STONE;
2681 // if biter did move, we had sound. play it.
2682 if (made_sound_of != O_NONE)
2683 play_sound_of_element(cave, made_sound_of, x, y);
2691 // check if touches a voodoo
2692 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2693 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2694 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2695 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2696 cave->voodoo_touched = TRUE;
2698 // check if touches something bad and should explode (includes voodoo by the flags)
2699 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2700 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2701 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2702 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2703 explode (cave, x, y);
2707 const GdDirection *creature_move;
2708 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2709 GdElement base = O_DRAGONFLY_1; // base element number (which is like O_***_1)
2710 int dir, dirn; // direction
2712 dir = get(cave, x, y)-base; // facing where
2713 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2715 // now change direction if backwards
2716 if (cave->creatures_backwards)
2720 dirn = (dir + 3) & 3; // fast turn
2722 dirn = (dir + 1) & 3; // fast turn
2724 // if can move forward, does so.
2725 if (is_space_dir(cave, x, y, creature_move[dir]))
2726 move(cave, x, y, creature_move[dir], base + dir);
2728 // otherwise turns 90 degrees in place.
2729 store(cave, x, y, base + dirn);
2734 store(cave, x, y, O_BLADDER_1);
2745 // bladder with any delay state: try to convert to clock.
2746 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2747 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))
2749 // if touches the specified element, let it be a clock
2750 store(cave, x, y, O_PRE_CLOCK_1);
2752 // plays the bladder convert sound
2753 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2757 // is space over the bladder?
2758 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2760 if (get(cave, x, y) == O_BLADDER_8)
2762 // if it is a bladder 8, really move up
2763 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2764 play_sound_of_element(cave, O_BLADDER, x, y);
2767 // if smaller delay, just increase delay.
2771 // if not space, is something sloped over the bladder?
2772 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2773 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2775 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2776 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2777 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2779 // rolling up, to left
2780 if (get(cave, x, y) == O_BLADDER_8)
2782 // if it is a bladder 8, really roll
2783 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2784 play_sound_of_element(cave, O_BLADDER, x, y);
2787 // if smaller delay, just increase delay.
2790 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2791 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2792 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2794 // rolling up, to left
2795 if (get(cave, x, y) == O_BLADDER_8)
2797 // if it is a bladder 8, really roll
2798 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2799 play_sound_of_element(cave, O_BLADDER, x, y);
2802 // if smaller delay, just increase delay.
2807 // no space, no sloped thing over it - store bladder 1 and that is for now.
2809 store(cave, x, y, O_BLADDER_1);
2814 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2815 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2816 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2817 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2818 explode (cave, x, y);
2823 // the ghost is given four possibilities to move.
2824 for (i = 0; i < 4; i++)
2826 static GdDirection dirs[] =
2833 GdDirection random_dir;
2835 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2836 if (is_space_dir(cave, x, y, random_dir))
2838 move(cave, x, y, random_dir, O_GHOST);
2839 break; // ghost did move -> exit loop
2845 // ============================================================================
2846 // A C T I V E E L E M E N T S
2847 // ============================================================================
2851 switch (cave->amoeba_state)
2854 store(cave, x, y, cave->amoeba_too_big_effect);
2857 case GD_AM_ENCLOSED:
2858 store(cave, x, y, cave->amoeba_enclosed_effect);
2861 case GD_AM_SLEEPING:
2863 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2864 if (amoeba_found_enclosed)
2865 // if still found enclosed, check all four directions, if this one is able to grow.
2866 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2867 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2868 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2869 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2871 // not enclosed. this is a local (per scan) flag!
2872 amoeba_found_enclosed = FALSE;
2873 cave->amoeba_state = GD_AM_AWAKE;
2876 // if alive, check in which dir to grow (or not)
2877 if (cave->amoeba_state == GD_AM_AWAKE)
2879 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2881 switch (gd_rand_int_range(cave->random, 0, 4))
2883 // decided to grow, choose a random direction.
2884 case 0: // let this be up. numbers indifferent.
2885 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2886 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2890 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2891 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2895 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2896 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2900 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2901 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2913 // check if it is touching an amoeba, and explosion is enabled
2914 if (cave->amoeba_2_explodes_by_amoeba &&
2915 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2916 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2917 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2918 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2919 explode (cave, x, y);
2921 switch (cave->amoeba_2_state)
2924 store(cave, x, y, cave->amoeba_2_too_big_effect);
2927 case GD_AM_ENCLOSED:
2928 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2931 case GD_AM_SLEEPING:
2933 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2934 if (amoeba_2_found_enclosed)
2935 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2936 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2937 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2938 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2940 // not enclosed. this is a local (per scan) flag!
2941 amoeba_2_found_enclosed = FALSE;
2942 cave->amoeba_2_state = GD_AM_AWAKE;
2945 // if it is alive, decide if it attempts to grow
2946 if (cave->amoeba_2_state == GD_AM_AWAKE)
2947 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2949 switch (gd_rand_int_range(cave->random, 0, 4))
2951 // decided to grow, choose a random direction.
2952 case 0: // let this be up. numbers indifferent.
2953 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2954 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2958 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2959 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2963 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2964 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2968 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2969 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2979 // choose randomly, if it spreads
2980 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2982 // the current one explodes
2983 store(cave, x, y, cave->acid_turns_to);
2985 // and if neighbours are eaten, put acid there.
2986 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2988 play_sound_of_element(cave, O_ACID, x, y);
2989 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2992 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2994 play_sound_of_element(cave, O_ACID, x, y);
2995 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2998 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
3000 play_sound_of_element(cave, O_ACID, x, y);
3001 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
3004 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
3006 play_sound_of_element(cave, O_ACID, x, y);
3007 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
3014 if (!cave->water_does_not_flow_down &&
3015 is_space_dir(cave, x, y, GD_MV_DOWN))
3016 // emulating the odd behaviour in crdr
3017 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
3019 if (is_space_dir(cave, x, y, GD_MV_UP))
3020 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
3022 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3023 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
3025 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3026 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
3030 store(cave, x, y, O_WATER);
3033 case O_H_EXPANDING_WALL:
3034 case O_V_EXPANDING_WALL:
3035 case O_H_EXPANDING_STEEL_WALL:
3036 case O_V_EXPANDING_STEEL_WALL:
3037 // checks first if direction is changed.
3038 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
3039 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
3040 !cave->expanding_wall_changed) ||
3041 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
3042 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
3043 cave->expanding_wall_changed))
3045 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3047 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3048 play_sound_of_element(cave, get(cave, x, y), x, y);
3051 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3052 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3053 play_sound_of_element(cave, get(cave, x, y), x, y);
3058 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3059 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3060 play_sound_of_element(cave, get(cave, x, y), x, y);
3063 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3064 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3065 play_sound_of_element(cave, get(cave, x, y), x, y);
3070 case O_EXPANDING_WALL:
3071 case O_EXPANDING_STEEL_WALL:
3072 // the wall which grows in all four directions.
3073 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3075 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3076 play_sound_of_element(cave, get(cave, x, y), x, y);
3079 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3080 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3081 play_sound_of_element(cave, get(cave, x, y), x, y);
3084 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3085 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3086 play_sound_of_element(cave, get(cave, x, y), x, y);
3089 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3090 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3091 play_sound_of_element(cave, get(cave, x, y), x, y);
3097 ; // to make compilers happy ...
3099 Info("Step[%03d]", cave->frame); // XXX
3101 int rrr = gd_cave_c64_random(cave);
3104 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
3105 (rrr & cave->slime_permeability_c64) == 0);
3108 * unpredictable: gd_rand_int
3109 * predictable: c64 predictable random generator.
3110 * for predictable, a random number is generated,
3111 * whether or not it is even possible that the stone will be able to pass.
3113 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
3115 GdDirection grav = cave->gravity;
3116 GdDirection oppos = opposite[cave->gravity];
3118 // space under the slime? elements may pass from top to bottom then.
3119 if (is_space_dir(cave, x, y, grav))
3121 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3123 // output a falling xy under
3124 store_dir(cave, x, y, grav, cave->slime_converts_1);
3126 store_dir(cave, x, y, oppos, O_SPACE);
3127 play_sound_of_element(cave, O_SLIME, x, y);
3129 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3131 store_dir(cave, x, y, grav, cave->slime_converts_2);
3132 store_dir(cave, x, y, oppos, O_SPACE);
3133 play_sound_of_element(cave, O_SLIME, x, y);
3135 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3137 store_dir(cave, x, y, grav, cave->slime_converts_3);
3138 store_dir(cave, x, y, oppos, O_SPACE);
3139 play_sound_of_element(cave, O_SLIME, x, y);
3141 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3143 // waiting stones pass without awakening
3144 store_dir(cave, x, y, grav, O_WAITING_STONE);
3145 store_dir(cave, x, y, oppos, O_SPACE);
3146 play_sound_of_element(cave, O_SLIME, x, y);
3148 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3150 // chasing stones pass
3151 store_dir(cave, x, y, grav, O_CHASING_STONE);
3152 store_dir(cave, x, y, oppos, O_SPACE);
3153 play_sound_of_element(cave, O_SLIME, x, y);
3158 // or space over the slime? elements may pass from bottom to up then.
3159 if (is_space_dir(cave, x, y, oppos))
3161 if (get_dir(cave, x, y, grav) == O_BLADDER)
3163 // bladders move UP the slime
3164 store_dir(cave, x, y, grav, O_SPACE);
3165 store_dir(cave, x, y, oppos, O_BLADDER_1);
3166 play_sound_of_element(cave, O_SLIME, x, y);
3168 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3170 store_dir(cave, x, y, grav, O_SPACE);
3171 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3172 play_sound_of_element(cave, O_SLIME, x, y);
3174 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3176 store_dir(cave, x, y, grav, O_SPACE);
3177 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3178 play_sound_of_element(cave, O_SLIME, x, y);
3185 case O_FALLING_WALL:
3186 if (is_space_dir(cave, x, y, grav_compat))
3188 // try falling if space under.
3191 for (yy = y + 1; yy < y + cave->h; yy++)
3192 // yy < y + cave->h is to check everything OVER the wall - since caves wrap around !!
3193 if (get(cave, x, yy) != O_SPACE)
3194 // stop cycle when other than space
3197 // if scanning stopped by a player... start falling!
3198 if (get(cave, x, yy) == O_PLAYER ||
3199 get(cave, x, yy) == O_PLAYER_GLUED ||
3200 get(cave, x, yy) == O_PLAYER_BOMB)
3202 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3203 // no sound when the falling wall starts falling!
3208 case O_FALLING_WALL_F:
3209 switch (get_dir(cave, x, y, grav_compat))
3212 case O_PLAYER_GLUED:
3214 // if player under, it explodes - the falling wall, not the player!
3215 explode(cave, x, y);
3220 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3225 play_sound_of_element(cave, get(cave, x, y), x, y);
3226 store(cave, x, y, O_FALLING_WALL);
3231 // ============================================================================
3232 // C O N V E Y O R B E L T S
3233 // ============================================================================
3235 case O_CONVEYOR_RIGHT:
3236 case O_CONVEYOR_LEFT:
3237 // only works if gravity is up or down!!!
3238 // first, check for gravity and running belts.
3239 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3241 const GdDirection *dir;
3245 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3246 if (cave->conveyor_belts_direction_changed)
3248 dir = left ? ccw_eighth : cw_eighth;
3250 // CHECK IF IT CONVEYS THE ELEMENT ABOVE IT
3251 // if gravity is normal, and the conveyor belt has something
3252 // ABOVE which can be moved
3254 // the gravity is up, so anything that should float now goes
3255 // DOWN and touches the conveyor
3256 if ((cave->gravity == GD_MV_DOWN &&
3257 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3258 (cave->gravity == GD_MV_UP &&
3259 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3261 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3262 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3264 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move
3265 store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space.
3269 // CHECK IF IT CONVEYS THE ELEMENT BELOW IT
3270 if ((cave->gravity == GD_MV_UP &&
3271 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3272 (cave->gravity == GD_MV_DOWN &&
3273 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3275 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3276 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3278 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move
3279 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear.
3285 // ============================================================================
3287 // ============================================================================
3290 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3291 move(cave, x, y, GD_MV_RIGHT, O_ROCKET_1);
3293 explode(cave, x, y);
3297 if (is_space_dir(cave, x, y, GD_MV_UP))
3298 move(cave, x, y, GD_MV_UP, O_ROCKET_2);
3300 explode(cave, x, y);
3304 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3305 move(cave, x, y, GD_MV_LEFT, O_ROCKET_3);
3307 explode(cave, x, y);
3311 if (is_space_dir(cave, x, y, GD_MV_DOWN))
3312 move(cave, x, y, GD_MV_DOWN, O_ROCKET_4);
3314 explode(cave, x, y);
3317 // ============================================================================
3318 // S I M P L E C H A N G I N G; E X P L O S I O N S
3319 // ============================================================================
3322 store(cave, x, y, cave->explosion_effect);
3326 store(cave, x, y, O_DIAMOND);
3330 store(cave, x, y, cave->diamond_birth_effect);
3334 store(cave, x, y, O_STONE);
3337 case O_NITRO_EXPL_4:
3338 store(cave, x, y, cave->nitro_explosion_effect);
3342 store(cave, x, y, cave->bomb_explosion_effect);
3345 case O_AMOEBA_2_EXPL_4:
3346 store(cave, x, y, cave->amoeba_2_explosion_effect);
3349 case O_GHOST_EXPL_4:
3351 static GdElement ghost_explode[] =
3353 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3354 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3355 O_WAITING_STONE, O_BITER_1
3358 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3363 store(cave, x, y, O_STEEL);
3367 store(cave, x, y, O_CLOCK);
3371 explode(cave, x, y);
3374 case O_TRAPPED_DIAMOND:
3375 if (cave->diamond_key_collected)
3376 store(cave, x, y, O_DIAMOND);
3380 if (cave->gate_open) // if no more diamonds needed
3381 store(cave, x, y, O_OUTBOX); // open outbox
3384 case O_PRE_INVIS_OUTBOX:
3385 if (cave->gate_open) // if no more diamonds needed
3386 store(cave, x, y, O_INVIS_OUTBOX); // open outbox. invisible one :P
3390 if (cave->hatched && !inbox_toggle) // if it is time of birth
3391 store(cave, x, y, O_PRE_PL_1);
3392 inbox_toggle = !inbox_toggle;
3396 store(cave, x, y, O_PLAYER);
3421 case O_GHOST_EXPL_1:
3422 case O_GHOST_EXPL_2:
3423 case O_GHOST_EXPL_3:
3433 case O_NITRO_EXPL_1:
3434 case O_NITRO_EXPL_2:
3435 case O_NITRO_EXPL_3:
3436 case O_AMOEBA_2_EXPL_1:
3437 case O_AMOEBA_2_EXPL_2:
3438 case O_AMOEBA_2_EXPL_3:
3439 // simply the next identifier
3458 found_water = TRUE; // for sound
3459 // simply the next identifier
3463 case O_BLADDER_SPENDER:
3464 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3466 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3467 store(cave, x, y, O_PRE_STEEL_1);
3468 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3473 // other inanimate elements that do nothing
3480 // ============================================================================
3482 // ============================================================================
3484 // another scan-like routine:
3485 // short explosions (for example, in bd1) started with explode_2.
3486 // internally we use explode_1; and change it to explode_2 if needed.
3487 if (cave->short_explosions)
3489 for (y = 0; y < cave->h; y++)
3491 for (x = 0; x < cave->w; x++)
3493 if (is_first_stage_of_explosion(cave, x, y))
3495 next(cave, x, y); // select next frame of explosion
3497 // forget scanned flag immediately
3498 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3504 // finally: forget "scanned" flags for objects.
3505 // also, check for time penalties.
3506 // these is something like an effect table, but we do not really use one.
3507 for (y = 0; y < cave->h; y++)
3509 for (x = 0; x < cave->w; x++)
3511 if (get(cave, x, y) & SCANNED)
3512 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3514 if (get(cave, x, y) == O_TIME_PENALTY)
3516 store(cave, x, y, O_GRAVESTONE);
3518 // there is time penalty for destroying the voodoo
3519 time_decrement_sec += cave->time_penalty;
3524 // this loop finds the coordinates of the player. needed for scrolling and chasing stone.
3525 // but we only do this, if a living player was found. if not yet, the setup
3526 // routine coordinates are used
3527 if (cave->player_state == GD_PL_LIVING)
3529 if (cave->active_is_first_found)
3531 // to be 1stb compatible, we do everything backwards.
3532 for (y = cave->h - 1; y >= 0; y--)
3534 for (x = cave->w - 1; x >= 0; x--)
3536 if (is_player(cave, x, y))
3538 // here we remember the coordinates.
3547 // as in the original: look for the last one
3548 for (y = 0; y < cave->h; y++)
3550 for (x = 0; x < cave->w; x++)
3552 if (is_player(cave, x, y))
3554 // here we remember the coordinates.
3563 // record coordinates of player for chasing stone
3564 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3566 cave->px[i] = cave->px[i + 1];
3567 cave->py[i] = cave->py[i + 1];
3570 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3571 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3575 // update timing calculated by iterating and counting elements
3576 update_cave_speed(cave);
3578 // cave 3 sounds. precedence is controlled by the sound_play function.
3579 // but we have to check amoeba&magic together as they had a different gritty sound when mixed
3581 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3583 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3584 cave->magic_wall_sound);
3586 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3587 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3588 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3590 if (amoeba_sound && magic_sound)
3592 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3597 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3598 else if (magic_sound)
3599 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3604 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3605 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3606 play_sound_of_element(cave, O_AMOEBA, x, y);
3609 // pneumatic hammer sound - overrides everything.
3610 if (cave->pneumatic_hammer_active_delay > 0)
3611 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3614 // ============================================================================
3616 // ============================================================================
3620 // check if player is alive.
3621 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
3622 cave->player_state = GD_PL_DIED;
3624 // check if any voodoo exploded, and kill players the next scan if that happended.
3625 if (cave->voodoo_touched)
3626 cave->kill_player = TRUE;
3630 // check flags after evaluating.
3631 if (cave->amoeba_state == GD_AM_AWAKE)
3633 if (amoeba_count >= cave->amoeba_max_count)
3634 cave->amoeba_state = GD_AM_TOO_BIG;
3635 if (amoeba_found_enclosed)
3636 cave->amoeba_state = GD_AM_ENCLOSED;
3639 // amoeba can also be turned into diamond by magic wall
3640 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3641 cave->amoeba_state = GD_AM_ENCLOSED;
3644 if (cave->amoeba_2_state == GD_AM_AWAKE)
3646 // check flags after evaluating.
3647 if (amoeba_2_count >= cave->amoeba_2_max_count)
3648 cave->amoeba_2_state = GD_AM_TOO_BIG;
3650 if (amoeba_2_found_enclosed)
3651 cave->amoeba_2_state = GD_AM_ENCLOSED;
3654 // amoeba 2 can also be turned into diamond by magic wall
3655 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3656 cave->amoeba_2_state = GD_AM_ENCLOSED;
3658 // now check times. ---------------------------
3659 // decrement time if a voodoo was killed.
3660 cave->time -= time_decrement_sec * cave->timing_factor;
3664 // only decrement time when player is already born.
3667 int secondsbefore, secondsafter;
3669 secondsbefore = cave->time / cave->timing_factor;
3670 cave->time -= cave->speed;
3671 if (cave->time <= 0)
3674 secondsafter = cave->time / cave->timing_factor;
3675 if (cave->time / cave->timing_factor < 10)
3676 // if less than 10 seconds, no walking sound, but play explosion sound
3677 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3679 if (secondsbefore != secondsafter)
3680 gd_cave_set_seconds_sound(cave);
3683 // a gravity switch was activated; seconds counting down
3684 if (cave->gravity_will_change > 0)
3686 cave->gravity_will_change -= cave->speed;
3687 if (cave->gravity_will_change < 0)
3688 cave->gravity_will_change = 0;
3690 if (cave->gravity_will_change == 0)
3692 cave->gravity = cave->gravity_next_direction;
3693 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound
3697 // creatures direction automatically change
3698 if (cave->creatures_direction_will_change > 0)
3700 cave->creatures_direction_will_change -= cave->speed;
3701 if (cave->creatures_direction_will_change < 0)
3702 cave->creatures_direction_will_change = 0;
3704 if (cave->creatures_direction_will_change == 0)
3706 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3708 cave->creatures_backwards = !cave->creatures_backwards;
3709 cave->creatures_direction_will_change =
3710 cave->creatures_direction_auto_change_time * cave->timing_factor;
3714 // magic wall; if active&wait or not wait for hatching
3715 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3716 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3718 cave->magic_wall_time -= cave->speed;
3719 if (cave->magic_wall_time < 0)
3720 cave->magic_wall_time = 0;
3721 if (cave->magic_wall_time == 0)
3722 cave->magic_wall_state = GD_MW_EXPIRED;
3725 // we may wait for hatching, when starting amoeba
3726 if (cave->amoeba_timer_started_immediately ||
3727 (cave->amoeba_state == GD_AM_AWAKE &&
3728 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3730 cave->amoeba_time -= cave->speed;
3731 if (cave->amoeba_time < 0)
3732 cave->amoeba_time = 0;
3733 if (cave->amoeba_time == 0)
3734 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3737 // we may wait for hatching, when starting amoeba
3738 if (cave->amoeba_timer_started_immediately ||
3739 (cave->amoeba_2_state == GD_AM_AWAKE &&
3740 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3742 cave->amoeba_2_time -= cave->speed;
3743 if (cave->amoeba_2_time < 0)
3744 cave->amoeba_2_time = 0;
3745 if (cave->amoeba_2_time == 0)
3746 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3749 // check for player hatching.
3750 start_signal = FALSE;
3752 // if not the c64 scheduling, but the correct frametime is used,
3753 // hatching delay should always be decremented.
3754 // otherwise, the if (millisecs...) condition below will set this.
3755 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3757 // NON-C64 scheduling
3758 if (cave->hatching_delay_frame > 0)
3760 // for milliseconds-based, non-c64 schedulings, hatching delay means frames.
3761 cave->hatching_delay_frame--;
3762 if (cave->hatching_delay_frame == 0)
3763 start_signal = TRUE;
3769 if (cave->hatching_delay_time > 0)
3771 // for c64 schedulings, hatching delay means milliseconds.
3772 cave->hatching_delay_time -= cave->speed;
3773 if (cave->hatching_delay_time <= 0)
3775 cave->hatching_delay_time = 0;
3776 start_signal = TRUE;
3781 // if decremented hatching, and it became zero:
3784 // THIS IS THE CAVE START SIGNAL
3786 // record that now the cave is in its normal state
3787 cave->hatched = TRUE;
3789 // if diamonds needed is below zero, we count the available diamonds now.
3790 gd_cave_count_diamonds(cave);
3792 // setup direction auto change
3793 if (cave->creatures_direction_auto_change_time)
3795 cave->creatures_direction_will_change =
3796 cave->creatures_direction_auto_change_time * cave->timing_factor;
3798 if (cave->creatures_direction_auto_change_on_start)
3799 cave->creatures_backwards = !cave->creatures_backwards;
3802 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3806 if (cave->biters_wait_frame == 0)
3807 cave->biters_wait_frame = cave->biter_delay_frame;
3809 cave->biters_wait_frame--;
3811 // replicators delay
3812 if (cave->replicators_wait_frame == 0)
3813 cave->replicators_wait_frame = cave->replicator_delay_frame;
3815 cave->replicators_wait_frame--;
3818 // ============================================================================
3820 // ============================================================================
3823 // check if cave failed by timeout is done in main game engine
3825 // check if cave failed by timeout
3826 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3828 gd_cave_clear_sounds(cave);
3829 cave->player_state = GD_PL_TIMEOUT;
3830 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3834 // set these for drawing.
3835 cave->last_direction = player_move;
3837 // here we remember last movements for animation. this is needed here,
3838 // as animation is in sync with the game, not the keyboard directly.
3839 // (for example, after exiting the cave, the player was "running" in the
3840 // original, till bonus points were counted for remaining time and so on.
3841 if (player_move == GD_MV_LEFT ||
3842 player_move == GD_MV_UP_LEFT ||
3843 player_move == GD_MV_DOWN_LEFT)
3844 cave->last_horizontal_direction = GD_MV_LEFT;
3846 if (player_move == GD_MV_RIGHT ||
3847 player_move == GD_MV_UP_RIGHT ||
3848 player_move == GD_MV_DOWN_RIGHT)
3849 cave->last_horizontal_direction = GD_MV_RIGHT;
3851 cave->frame++; // XXX
3854 void set_initial_cave_speed(GdCave *cave)
3859 // set cave get function; to implement perfect or lineshifting borders
3860 if (cave->lineshift)
3861 cave->getp = getp_shift;
3863 cave->getp = getp_perfect;
3865 // check whether to scan the first and last line
3866 if (cave->border_scan_first_and_last)
3877 for (y = ymin; y <= ymax; y++)
3879 for (x = 0; x < cave->w; x++)
3881 // add the ckdelay correction value for every element seen.
3882 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3886 // update timing calculated by iterating and counting elements
3887 update_cave_speed(cave);