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);
1374 if (cave->magic_wall_breakscan && cave->amoeba_state == GD_AM_AWAKE)
1375 cave->convert_amoeba_this_frame = TRUE;
1383 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1385 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1387 explode_dir(cave, x, y, fall_dir);
1394 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1395 GdDirection fall_dir, GdElement bouncing)
1397 if (is_space_dir(cave, x, y, fall_dir))
1400 move(cave, x, y, fall_dir, get(cave, x, y));
1405 // check if it is on a sloped element, and it can roll.
1406 // for example, sloped wall looks like:
1409 // this is tagged as sloped up&left.
1410 // first check if the stone or diamond is coming from "up" (ie. opposite of gravity)
1411 // then check the direction to roll (left or right)
1412 // this way, gravity can also be pointing right, and the above slope will work as one would expect
1414 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1416 // sloped element, falling to left or right
1417 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1418 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1419 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1421 play_sound_of_element(cave, get(cave, x, y), x, y);
1423 // try to roll left first - see O_STONE to understand why cw_fourth
1424 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1426 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1427 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1428 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1430 play_sound_of_element(cave, get(cave, x, y), x, y);
1432 // if not, try to roll right
1433 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1437 // cannot roll in any direction, so it stops
1438 play_sound_of_element(cave, get(cave, x, y), x, y);
1439 store(cave, x, y, bouncing);
1445 // any other element, stops
1446 play_sound_of_element(cave, get(cave, x, y), x, y);
1447 store(cave, x, y, bouncing);
1451 static void update_cave_speed(GdCave *cave)
1453 // update timing calculated by iterating and counting elements which were slow to process on c64
1454 switch (cave->scheduling)
1456 case GD_SCHEDULING_MILLISECONDS:
1457 // cave->speed already contains the milliseconds value, do not touch it
1460 case GD_SCHEDULING_BD1:
1461 if (!cave->intermission)
1462 // non-intermissions
1463 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1465 // intermissions were quicker, as only lines 1-12 were processed by the engine.
1466 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1469 case GD_SCHEDULING_BD1_ATARI:
1470 // about 20ms/frame faster than c64 version
1471 if (!cave->intermission)
1472 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); // non-intermissions
1474 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); // for intermissions
1477 case GD_SCHEDULING_BD2:
1479 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1482 case GD_SCHEDULING_PLCK:
1483 // 65 is totally empty cave in construction kit, with delay = 0)
1484 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1487 case GD_SCHEDULING_BD2_PLCK_ATARI:
1488 // a really fast engine; timing works like c64 plck.
1489 // 40 ms was measured in the construction kit, with delay = 0
1490 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1493 case GD_SCHEDULING_CRDR:
1494 if (cave->hammered_walls_reappear) // this made the engine very slow.
1495 cave->ckdelay += 60000;
1496 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1499 case GD_SCHEDULING_MAX:
1505 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1512 // amoeba found to be enclosed. if not, this is cleared
1513 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1515 // counting the number of amoebas. after scan, check if too much
1516 int amoeba_count, amoeba_2_count;
1518 // cave scan found water - for sound
1519 boolean found_water;
1521 boolean inbox_toggle;
1522 boolean start_signal;
1524 // gravity for falling wall, bladder, ...
1525 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1527 // directions for o_something_1, 2, 3 and 4 (creatures)
1528 static const GdDirection creature_dir[] =
1535 static const GdDirection creature_chdir[] =
1542 int time_decrement_sec;
1544 // biters eating elements preference, they try to go in this order
1545 GdElement biter_try[] =
1552 boolean amoeba_sound, magic_sound;
1554 gd_cave_clear_sounds(cave);
1556 // if diagonal movements not allowed,
1557 // horizontal movements have precedence. [BROADRIBB]
1558 if (!cave->diagonal_movements)
1560 switch (player_move)
1562 case GD_MV_UP_RIGHT:
1563 case GD_MV_DOWN_RIGHT:
1564 player_move = GD_MV_RIGHT;
1568 case GD_MV_DOWN_LEFT:
1569 player_move = GD_MV_LEFT;
1573 // no correction needed
1578 // set cave get function; to implement perfect or lineshifting borders
1579 if (cave->lineshift)
1581 cave->getp = getp_shift;
1582 cave->getx = getx_shift;
1583 cave->gety = gety_shift;
1587 cave->getp = getp_perfect;
1588 cave->getx = getx_perfect;
1589 cave->gety = gety_perfect;
1592 // increment this. if the scan routine comes across player, clears it (sets to zero).
1593 if (cave->player_seen_ago < 100)
1594 cave->player_seen_ago++;
1596 if (cave->pneumatic_hammer_active_delay > 0)
1597 cave->pneumatic_hammer_active_delay--;
1599 // inboxes and outboxes flash with the rhythm of the game, not the display.
1600 // also, a player can be born only from an open, not from a steel-wall-like inbox.
1601 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1602 inbox_toggle = cave->inbox_flash_toggle;
1604 if (cave->gate_open_flash > 0)
1605 cave->gate_open_flash--;
1607 // score collected this frame
1610 // to implement buggy bd1 amoeba+magic wall behaviour
1611 cave->convert_amoeba_this_frame = FALSE;
1613 // suicide only kills the active player
1614 // player_x, player_y was set by the previous iterate routine, or the cave setup.
1615 // we must check if there is a player or not - he may have exploded or something like that
1616 if (suicide && cave->player_state == GD_PL_LIVING &&
1617 is_player(cave, cave->player_x, cave->player_y))
1618 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1620 // check for walls reappearing
1621 if (cave->hammered_reappear)
1623 for (y = 0; y < cave->h; y++)
1625 for (x = 0; x < cave->w; x++)
1627 // timer for the cell > 0?
1628 if (cave->hammered_reappear[y][x] > 0)
1631 cave->hammered_reappear[y][x]--;
1633 // check if it became zero
1634 if (cave->hammered_reappear[y][x] == 0)
1636 store(cave, x, y, O_BRICK);
1637 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1644 // variables to check during the scan
1646 // will be set to false if any of the amoeba is found free.
1647 amoeba_found_enclosed = TRUE;
1648 amoeba_2_found_enclosed = TRUE;
1651 found_water = FALSE;
1653 time_decrement_sec = 0;
1655 // check whether to scan the first and last line
1656 if (cave->border_scan_first_and_last)
1667 // the cave scan routine
1668 for (y = ymin; y <= ymax; y++)
1670 for (x = 0; x < cave->w; x++)
1672 // if we find a scanned element, change it to the normal one, and that's all.
1673 // this is required, for example for chasing stones, which have moved, always passing slime!
1674 if (get(cave, x, y) & SCANNED)
1676 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1681 // add the ckdelay correction value for every element seen.
1682 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1684 switch (get(cave, x, y))
1686 // ============================================================================
1688 // ============================================================================
1691 if (cave->kill_player)
1693 explode (cave, x, y);
1697 cave->player_seen_ago = 0;
1698 // bd4 intermission caves have many players. so if one of them has exited,
1699 // do not change the flag anymore. so this if () is needed
1700 if (cave->player_state != GD_PL_EXITED)
1701 cave->player_state = GD_PL_LIVING;
1703 // check for pneumatic hammer things
1704 // 1) press fire, 2) have pneumatic hammer 4) space on left or right
1705 // for hammer 5) stand on something
1706 if (player_fire && cave->got_pneumatic_hammer &&
1707 is_space_dir(cave, x, y, player_move) &&
1708 !is_space_dir(cave, x, y, GD_MV_DOWN))
1710 if (player_move == GD_MV_LEFT &&
1711 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1713 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1714 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1715 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1719 if (player_move == GD_MV_RIGHT &&
1720 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1722 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1723 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1724 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1729 if (player_move != GD_MV_STILL)
1731 // only do every check if he is not moving
1732 GdElement what = get_dir(cave, x, y, player_move);
1733 GdElement remains = what;
1736 // if we are 'eating' a teleporter, and the function returns true
1737 // (teleporting worked), break here
1738 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1741 // try to push element; if successful, break
1742 push = do_push(cave, x, y, player_move, player_fire);
1752 // if its a bomb, remember he now has one.
1753 // we do not change the "remains" and "what" variables,
1754 // so that part of the code will be ineffective
1755 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1756 store_dir(cave, x, y, player_move, O_SPACE);
1759 store(cave, x, y, O_PLAYER_BOMB);
1761 move(cave, x, y, player_move, O_PLAYER_BOMB);
1764 case O_ROCKET_LAUNCHER:
1765 // if its a rocket launcher, remember he now has one.
1766 // we do not change the "remains" and "what" variables,
1767 // so that part of the code will be ineffective
1768 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1769 store_dir(cave, x, y, player_move, O_SPACE);
1772 store(cave, x, y, O_PLAYER_ROCKET_LAUNCHER);
1774 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
1778 // we do not change the "remains" and "what" variables,
1779 // so that part of the code will be ineffective
1780 if (!player_fire && !cave->gravity_switch_active &&
1781 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1783 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1784 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1785 cave->gravity_disabled = TRUE;
1789 case O_GRAVITY_SWITCH:
1790 // (we cannot use player_get for this as it does not have player_move parameter)
1791 // only allow changing direction if the new dir is not diagonal
1792 if (cave->gravity_switch_active &&
1793 (player_move == GD_MV_LEFT ||
1794 player_move == GD_MV_RIGHT ||
1795 player_move == GD_MV_UP ||
1796 player_move == GD_MV_DOWN))
1798 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1799 cave->gravity_will_change =
1800 cave->gravity_change_time * cave->timing_factor;
1801 cave->gravity_next_direction = player_move;
1802 cave->gravity_switch_active = FALSE;
1807 // get element - process others.
1808 // if cannot get, player_get_element will return the same
1809 remains = player_get_element(cave, what, x, y);
1814 if (remains != what || remains == O_SPACE)
1816 // if anything changed, apply the change.
1818 // if snapping anything and we have snapping explosions set.
1819 // but these is not true for pushing.
1820 if (remains == O_SPACE && player_fire && !push)
1821 remains = cave->snap_element;
1823 if (remains != O_SPACE || player_fire)
1824 // if any other element than space, player cannot move.
1825 // also if pressing fire, will not move.
1826 store_dir(cave, x, y, player_move, remains);
1828 // if space remains there, the player moves.
1829 move(cave, x, y, player_move, O_PLAYER);
1835 // much simpler; cannot steal stones
1836 if (cave->kill_player)
1838 explode(cave, x, y);
1842 cave->player_seen_ago = 0;
1843 // bd4 intermission caves have many players. so if one of them has exited,
1844 // do not change the flag anymore. so this if () is needed
1845 if (cave->player_state != GD_PL_EXITED)
1846 cave->player_state = GD_PL_LIVING;
1848 if (player_move != GD_MV_STILL)
1850 // if the player does not move, nothing to do
1851 GdElement what = get_dir(cave, x, y, player_move);
1852 GdElement remains = what;
1856 // placing a bomb into empty space or dirt
1857 if (is_space_dir(cave, x, y, player_move) ||
1858 is_element_dir(cave, x, y, player_move, O_DIRT))
1860 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1862 // placed bomb, he is normal player again
1863 store(cave, x, y, O_PLAYER);
1864 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1869 // pushing and collecting
1870 // if we are 'eating' a teleporter, and the function returns true
1871 // (teleporting worked), break here
1872 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1875 // player fire is false...
1876 if (do_push(cave, x, y, player_move, FALSE))
1884 case O_GRAVITY_SWITCH:
1885 // (we cannot use player_get for this as it does not have
1886 // player_move parameter)
1887 // only allow changing direction if the new dir is not diagonal
1888 if (cave->gravity_switch_active &&
1889 (player_move == GD_MV_LEFT ||
1890 player_move == GD_MV_RIGHT ||
1891 player_move == GD_MV_UP ||
1892 player_move == GD_MV_DOWN))
1894 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1895 cave->gravity_will_change =
1896 cave->gravity_change_time * cave->timing_factor;
1897 cave->gravity_next_direction = player_move;
1898 cave->gravity_switch_active = FALSE;
1903 // get element. if cannot get, player_get_element will return the same
1904 remains = player_get_element (cave, what, x, y);
1909 // if element changed, OR there is space, move.
1910 if (remains != what || remains == O_SPACE)
1912 // if anything changed, apply the change.
1913 move(cave, x, y, player_move, O_PLAYER_BOMB);
1918 case O_PLAYER_ROCKET_LAUNCHER:
1919 // much simpler; cannot snap-push stones
1920 if (cave->kill_player)
1922 explode(cave, x, y);
1926 cave->player_seen_ago = 0;
1927 // bd4 intermission caves have many players. so if one of them has exited,
1928 // do not change the flag anymore. so this if () is needed
1929 if (cave->player_state != GD_PL_EXITED)
1930 cave->player_state = GD_PL_LIVING;
1933 if (player_move != GD_MV_STILL)
1935 // if the player does not move, nothing to do
1936 GdElement what = get_dir(cave, x, y, player_move);
1937 GdElement remains = what;
1939 // to fire a rocket, diagonal movement should not be allowed.
1940 // so either x or y must be zero
1943 // placing a rocket into empty space
1944 if (is_space_dir(cave, x, y, player_move))
1946 switch (player_move)
1949 store_dir(cave, x, y, player_move, O_ROCKET_1);
1950 if (!cave->infinite_rockets)
1951 store(cave, x, y, O_PLAYER);
1955 store_dir(cave, x, y, player_move, O_ROCKET_2);
1956 if (!cave->infinite_rockets)
1957 store(cave, x, y, O_PLAYER);
1961 store_dir(cave, x, y, player_move, O_ROCKET_3);
1962 if (!cave->infinite_rockets)
1963 store(cave, x, y, O_PLAYER);
1967 store_dir(cave, x, y, player_move, O_ROCKET_4);
1968 if (!cave->infinite_rockets)
1969 store(cave, x, y, O_PLAYER);
1973 // cannot fire in other directions
1977 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1980 // a player with rocket launcher cannot snap elements, so stop here
1984 // pushing and collecting
1985 // if we are 'eating' a teleporter, and the function returns true
1986 // (teleporting worked), break here
1987 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1990 // player fire is false...
1991 if (do_push(cave, x, y, player_move, FALSE))
1997 // get element. if cannot get, player_get_element will return the same
1998 remains = player_get_element(cave, what, x, y);
2001 // if something changed, OR there is space, move.
2002 if (remains != what || remains == O_SPACE)
2004 // if anything changed, apply the change.
2005 move(cave, x, y, player_move, O_PLAYER_ROCKET_LAUNCHER);
2010 case O_PLAYER_STIRRING:
2011 if (cave->kill_player)
2013 explode(cave, x, y);
2017 // stirring sound, if no other walking sound or explosion
2018 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
2020 cave->player_seen_ago = 0;
2021 // bd4 intermission caves have many players. so if one of them has exited,
2022 // do not change the flag anymore. so this if () is needed
2023 if (cave->player_state != GD_PL_EXITED)
2024 cave->player_state = GD_PL_LIVING;
2028 // player "exits" stirring the pot by pressing fire
2029 cave->gravity_disabled = FALSE;
2030 store(cave, x, y, O_PLAYER);
2031 cave->gravity_switch_active = TRUE;
2035 // player holding pneumatic hammer
2036 case O_PLAYER_PNEUMATIC_LEFT:
2037 case O_PLAYER_PNEUMATIC_RIGHT:
2038 // usual player stuff
2039 if (cave->kill_player)
2041 explode(cave, x, y);
2045 cave->player_seen_ago = 0;
2046 if (cave->player_state != GD_PL_EXITED)
2047 cave->player_state = GD_PL_LIVING;
2049 // if hammering time is up, becomes a normal player again.
2050 if (cave->pneumatic_hammer_active_delay == 0)
2051 store(cave, x, y, O_PLAYER);
2054 // the active pneumatic hammer itself
2055 case O_PNEUMATIC_ACTIVE_RIGHT:
2056 case O_PNEUMATIC_ACTIVE_LEFT:
2057 if (cave->pneumatic_hammer_active_delay == 0)
2061 // pneumatic hammer element disappears
2062 store(cave, x, y, O_SPACE);
2064 // which is the new element which appears after that one is hammered?
2065 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
2067 // if there is a new element, display it
2068 // O_NONE might be returned, for example if the element being
2069 // hammered explodes during hammering (by a nearby explosion)
2070 if (new_elem != O_NONE)
2072 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
2074 // and if walls reappear, remember it in array
2075 if (cave->hammered_walls_reappear)
2079 wall_y = (y + 1) % cave->h;
2080 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
2086 // ============================================================================
2087 // S T O N E S, D I A M O N D S
2088 // ============================================================================
2090 case O_STONE: // standing stone
2091 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
2094 case O_MEGA_STONE: // standing mega_stone
2095 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
2098 case O_DIAMOND: // standing diamond
2099 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
2102 case O_NUT: // standing nut
2103 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
2106 case O_DIRT_BALL: // standing dirt ball
2107 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
2110 case O_DIRT_LOOSE: // standing loose dirt
2111 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
2114 case O_FLYING_STONE: // standing stone
2115 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
2118 case O_FLYING_DIAMOND: // standing diamond
2119 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2122 // ============================================================================
2123 // 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
2124 // ============================================================================
2126 case O_DIRT_BALL_F: // falling dirt ball
2127 if (!cave->gravity_disabled)
2128 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2131 case O_DIRT_LOOSE_F: // falling loose dirt
2132 if (!cave->gravity_disabled)
2133 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2136 case O_STONE_F: // falling stone
2137 if (!cave->gravity_disabled)
2139 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2142 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2145 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2148 if (do_fall_try_crush(cave, x, y, cave->gravity))
2151 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2155 case O_MEGA_STONE_F: // falling mega
2156 if (!cave->gravity_disabled)
2158 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2161 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2164 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2167 if (do_fall_try_crush(cave, x, y, cave->gravity))
2170 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2174 case O_DIAMOND_F: // falling diamond
2175 if (!cave->gravity_disabled)
2177 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2180 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2183 if (do_fall_try_crush(cave, x, y, cave->gravity))
2186 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2190 case O_NUT_F: // falling nut
2191 if (!cave->gravity_disabled)
2193 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2196 if (do_fall_try_crush(cave, x, y, cave->gravity))
2199 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2203 case O_FLYING_STONE_F: // falling stone
2204 if (!cave->gravity_disabled)
2206 GdDirection fall_dir = opposite[cave->gravity];
2208 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2211 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2214 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2217 if (do_fall_try_crush(cave, x, y, fall_dir))
2220 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2224 case O_FLYING_DIAMOND_F: // falling diamond
2225 if (!cave->gravity_disabled)
2227 GdDirection fall_dir = opposite[cave->gravity];
2229 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2232 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2235 if (do_fall_try_crush(cave, x, y, fall_dir))
2238 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2242 // ============================================================================
2243 // N I T R O P A C K
2244 // ============================================================================
2246 case O_NITRO_PACK: // standing nitro pack
2247 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2250 case O_NITRO_PACK_F: // falling nitro pack
2251 if (!cave->gravity_disabled)
2253 if (is_space_dir(cave, x, y, cave->gravity)) // if space, falling further
2254 move(cave, x, y, cave->gravity, get(cave, x, y));
2255 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2257 // try magic wall; if true, function did the work
2259 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2261 // falling on a dirt, it does NOT explode - just stops at its place.
2262 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2263 store(cave, x, y, O_NITRO_PACK);
2266 // falling on any other element it explodes
2267 explode(cave, x, y);
2271 case O_NITRO_PACK_EXPLODE: // a triggered nitro pack
2272 explode(cave, x, y);
2275 // ============================================================================
2276 // C R E A T U R E S
2277 // ============================================================================
2283 // if cannot move in any direction, becomes an enclosed cow
2284 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2285 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2286 store(cave, x, y, O_COW_ENCLOSED_1);
2289 // THIS IS THE CREATURE MOVE thing copied.
2290 const GdDirection *creature_move;
2291 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2292 GdElement base; // base element number (which is like O_***_1)
2293 int dir, dirn, dirp; // direction
2297 dir = get(cave, x, y)-base; // facing where
2298 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2300 // now change direction if backwards
2301 if (cave->creatures_backwards)
2306 dirn = (dir + 3) & 3; // fast turn
2307 dirp = (dir + 1) & 3; // slow turn
2311 dirn = (dir + 1) & 3; // fast turn
2312 dirp = (dir + 3) & 3; // slow turn
2315 if (is_space_dir(cave, x, y, creature_move[dirn]))
2316 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2317 else if (is_space_dir(cave, x, y, creature_move[dir]))
2318 move(cave, x, y, creature_move[dir], base + dir); // go on
2320 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2324 // enclosed cows wait some time before turning to a skeleton
2325 case O_COW_ENCLOSED_1:
2326 case O_COW_ENCLOSED_2:
2327 case O_COW_ENCLOSED_3:
2328 case O_COW_ENCLOSED_4:
2329 case O_COW_ENCLOSED_5:
2330 case O_COW_ENCLOSED_6:
2331 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2332 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2333 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2334 is_space_dir(cave, x, y, GD_MV_DOWN))
2335 store(cave, x, y, O_COW_1);
2340 case O_COW_ENCLOSED_7:
2341 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2342 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2343 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2344 is_space_dir(cave, x, y, GD_MV_DOWN))
2345 store(cave, x, y, O_COW_1);
2347 store(cave, x, y, O_SKELETON);
2354 case O_ALT_FIREFLY_1:
2355 case O_ALT_FIREFLY_2:
2356 case O_ALT_FIREFLY_3:
2357 case O_ALT_FIREFLY_4:
2362 case O_ALT_BUTTER_1:
2363 case O_ALT_BUTTER_2:
2364 case O_ALT_BUTTER_3:
2365 case O_ALT_BUTTER_4:
2370 // check if touches a voodoo
2371 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2372 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2373 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2374 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2375 cave->voodoo_touched = TRUE;
2377 // check if touches something bad and should explode (includes voodoo by the flags)
2378 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2379 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2380 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2381 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2382 explode (cave, x, y);
2386 const GdDirection *creature_move;
2387 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2388 GdElement base = -1; // base element number (which is like O_***_1)
2389 int dir, dirn, dirp; // direction
2391 if (get(cave, x, y) >= O_FIREFLY_1 &&
2392 get(cave, x, y) <= O_FIREFLY_4)
2394 else if (get(cave, x, y) >= O_BUTTER_1 &&
2395 get(cave, x, y) <= O_BUTTER_4)
2397 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2398 get(cave, x, y) <= O_STONEFLY_4)
2399 base = O_STONEFLY_1;
2400 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2401 get(cave, x, y) <= O_ALT_FIREFLY_4)
2402 base = O_ALT_FIREFLY_1;
2403 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2404 get(cave, x, y) <= O_ALT_BUTTER_4)
2405 base = O_ALT_BUTTER_1;
2407 dir = get(cave, x, y) - base; // facing where
2408 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2410 // now change direction if backwards
2411 if (cave->creatures_backwards)
2416 dirn = (dir + 3) & 3; // fast turn
2417 dirp = (dir + 1) & 3; // slow turn
2421 dirn = (dir + 1) & 3; // fast turn
2422 dirp = (dir + 3) & 3; // slow turn
2425 if (is_space_dir(cave, x, y, creature_move[dirn]))
2426 move(cave, x, y, creature_move[dirn], base + dirn); // turn and move to preferred dir
2427 else if (is_space_dir(cave, x, y, creature_move[dir]))
2428 move(cave, x, y, creature_move[dir], base + dir); // go on
2430 store(cave, x, y, base + dirp); // turn in place if nothing else possible
2434 case O_WAITING_STONE:
2435 if (is_space_dir(cave, x, y, grav_compat))
2437 // beginning to fall
2439 move(cave, x, y, grav_compat, O_CHASING_STONE);
2441 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2443 // rolling down a brick wall or a stone
2444 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2445 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2446 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2448 // maybe rolling left - see case O_STONE to understand why we use cw_fourth here
2449 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2451 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2452 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2453 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2456 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2461 case O_CHASING_STONE:
2463 int px = cave->px[0];
2464 int py = cave->py[0];
2465 boolean horizontal = gd_rand_boolean(cave->random);
2466 boolean dont_move = FALSE;
2474 // ------------------------------------------------------------
2475 // check for a horizontal movement
2476 // ------------------------------------------------------------
2479 // if coordinates are the same
2481 horizontal = !horizontal;
2488 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2490 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2494 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2496 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2505 horizontal = !horizontal;
2513 // ------------------------------------------------------------
2514 // check for a vertical movement
2515 // ------------------------------------------------------------
2518 // if coordinates are the same
2520 horizontal = !horizontal;
2526 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2528 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2532 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2534 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2543 horizontal = !horizontal;
2556 // if we should move in both directions, but can not move in any, stop.
2561 // check for horizontal
2564 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2565 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2566 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2567 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2568 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2569 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2573 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2574 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2575 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2576 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2577 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2578 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2583 // check for vertical
2586 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2587 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2588 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2589 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2590 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2591 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2595 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2596 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2597 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2598 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2599 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2600 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2608 if (cave->replicators_wait_frame == 0 &&
2609 cave->replicators_active &&
2610 !cave->gravity_disabled)
2612 // only replicate, if space is under it.
2613 // do not replicate players!
2614 // also obeys gravity settings.
2615 // only replicate element if it is not a scanned one
2616 // do not replicate space... that condition looks like it
2617 // makes no sense, but otherwise it generates SCANNED spaces,
2618 // which cannot be "collected" by the player, so he cannot run
2619 // under a replicator
2620 if (is_space_dir(cave, x, y, cave->gravity) &&
2621 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2622 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2624 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2625 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2634 if (cave->biters_wait_frame == 0)
2636 static GdDirection biter_move[] =
2644 // direction, last two bits 0..3
2645 int dir = get(cave, x, y) - O_BITER_1;
2646 int dirn = (dir + 3) & 3;
2647 int dirp = (dir + 1) & 3;
2649 GdElement made_sound_of = O_NONE;
2651 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2653 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2655 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2656 if (biter_try[i] != O_SPACE)
2657 made_sound_of = O_BITER_1; // sound of a biter eating
2660 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2662 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2663 if (biter_try[i] != O_SPACE)
2664 made_sound_of = O_BITER_1; // sound of a biter eating
2667 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2669 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2670 if (biter_try[i] != O_SPACE)
2671 made_sound_of = O_BITER_1; // sound of a biter eating
2676 if (i == ARRAY_SIZE(biter_try))
2677 // i = number of elements in array: could not move, so just turn
2678 store(cave, x, y, O_BITER_1 + dirp);
2679 else if (biter_try[i] == O_STONE)
2681 // if there was a stone there, where we moved...
2682 // do not eat stones, just throw them back
2683 store(cave, x, y, O_STONE);
2684 made_sound_of = O_STONE;
2687 // if biter did move, we had sound. play it.
2688 if (made_sound_of != O_NONE)
2689 play_sound_of_element(cave, made_sound_of, x, y);
2697 // check if touches a voodoo
2698 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2699 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2700 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2701 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2702 cave->voodoo_touched = TRUE;
2704 // check if touches something bad and should explode (includes voodoo by the flags)
2705 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2706 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2707 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2708 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2709 explode (cave, x, y);
2713 const GdDirection *creature_move;
2714 boolean ccw = rotates_ccw(cave, x, y); // check if default is counterclockwise
2715 GdElement base = O_DRAGONFLY_1; // base element number (which is like O_***_1)
2716 int dir, dirn; // direction
2718 dir = get(cave, x, y)-base; // facing where
2719 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2721 // now change direction if backwards
2722 if (cave->creatures_backwards)
2726 dirn = (dir + 3) & 3; // fast turn
2728 dirn = (dir + 1) & 3; // fast turn
2730 // if can move forward, does so.
2731 if (is_space_dir(cave, x, y, creature_move[dir]))
2732 move(cave, x, y, creature_move[dir], base + dir);
2734 // otherwise turns 90 degrees in place.
2735 store(cave, x, y, base + dirn);
2740 store(cave, x, y, O_BLADDER_1);
2751 // bladder with any delay state: try to convert to clock.
2752 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2753 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))
2755 // if touches the specified element, let it be a clock
2756 store(cave, x, y, O_PRE_CLOCK_1);
2758 // plays the bladder convert sound
2759 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2763 // is space over the bladder?
2764 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2766 if (get(cave, x, y) == O_BLADDER_8)
2768 // if it is a bladder 8, really move up
2769 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2770 play_sound_of_element(cave, O_BLADDER, x, y);
2773 // if smaller delay, just increase delay.
2777 // if not space, is something sloped over the bladder?
2778 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2779 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2781 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2782 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2783 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2785 // rolling up, to left
2786 if (get(cave, x, y) == O_BLADDER_8)
2788 // if it is a bladder 8, really roll
2789 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2790 play_sound_of_element(cave, O_BLADDER, x, y);
2793 // if smaller delay, just increase delay.
2796 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2797 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2798 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2800 // rolling up, to left
2801 if (get(cave, x, y) == O_BLADDER_8)
2803 // if it is a bladder 8, really roll
2804 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2805 play_sound_of_element(cave, O_BLADDER, x, y);
2808 // if smaller delay, just increase delay.
2813 // no space, no sloped thing over it - store bladder 1 and that is for now.
2815 store(cave, x, y, O_BLADDER_1);
2820 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2821 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2822 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2823 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2824 explode (cave, x, y);
2829 // the ghost is given four possibilities to move.
2830 for (i = 0; i < 4; i++)
2832 static GdDirection dirs[] =
2839 GdDirection random_dir;
2841 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2842 if (is_space_dir(cave, x, y, random_dir))
2844 move(cave, x, y, random_dir, O_GHOST);
2845 break; // ghost did move -> exit loop
2851 // ============================================================================
2852 // A C T I V E E L E M E N T S
2853 // ============================================================================
2856 // emulating BD1 amoeba+magic wall bug
2857 if (cave->convert_amoeba_this_frame && amoeba_found_enclosed)
2859 store(cave, x, y, cave->amoeba_enclosed_effect);
2864 switch (cave->amoeba_state)
2867 store(cave, x, y, cave->amoeba_too_big_effect);
2870 case GD_AM_ENCLOSED:
2871 store(cave, x, y, cave->amoeba_enclosed_effect);
2874 case GD_AM_SLEEPING:
2876 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2877 if (amoeba_found_enclosed)
2878 // if still found enclosed, check all four directions, if this one is able to grow.
2879 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2880 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2881 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2882 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2884 // not enclosed. this is a local (per scan) flag!
2885 amoeba_found_enclosed = FALSE;
2886 cave->amoeba_state = GD_AM_AWAKE;
2889 // if alive, check in which dir to grow (or not)
2890 if (cave->amoeba_state == GD_AM_AWAKE)
2892 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2894 switch (gd_rand_int_range(cave->random, 0, 4))
2896 // decided to grow, choose a random direction.
2897 case 0: // let this be up. numbers indifferent.
2898 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2899 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2903 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2904 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2908 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2909 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2913 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2914 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2926 // check if it is touching an amoeba, and explosion is enabled
2927 if (cave->amoeba_2_explodes_by_amoeba &&
2928 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2929 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2930 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2931 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2932 explode (cave, x, y);
2934 switch (cave->amoeba_2_state)
2937 store(cave, x, y, cave->amoeba_2_too_big_effect);
2940 case GD_AM_ENCLOSED:
2941 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2944 case GD_AM_SLEEPING:
2946 // if no amoeba found during THIS SCAN yet, which was able to grow, check this one.
2947 if (amoeba_2_found_enclosed)
2948 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2949 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2950 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2951 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2953 // not enclosed. this is a local (per scan) flag!
2954 amoeba_2_found_enclosed = FALSE;
2955 cave->amoeba_2_state = GD_AM_AWAKE;
2958 // if it is alive, decide if it attempts to grow
2959 if (cave->amoeba_2_state == GD_AM_AWAKE)
2960 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2962 switch (gd_rand_int_range(cave->random, 0, 4))
2964 // decided to grow, choose a random direction.
2965 case 0: // let this be up. numbers indifferent.
2966 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2967 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2971 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2972 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2976 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2977 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2981 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2982 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2992 // choose randomly, if it spreads
2993 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2995 // the current one explodes
2996 store(cave, x, y, cave->acid_turns_to);
2998 // and if neighbours are eaten, put acid there.
2999 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
3001 play_sound_of_element(cave, O_ACID, x, y);
3002 store_dir(cave, x, y, GD_MV_UP, O_ACID);
3005 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
3007 play_sound_of_element(cave, O_ACID, x, y);
3008 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
3011 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
3013 play_sound_of_element(cave, O_ACID, x, y);
3014 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
3017 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
3019 play_sound_of_element(cave, O_ACID, x, y);
3020 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
3027 if (!cave->water_does_not_flow_down &&
3028 is_space_dir(cave, x, y, GD_MV_DOWN))
3029 // emulating the odd behaviour in crdr
3030 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
3032 if (is_space_dir(cave, x, y, GD_MV_UP))
3033 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
3035 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3036 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
3038 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3039 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
3043 store(cave, x, y, O_WATER);
3046 case O_H_EXPANDING_WALL:
3047 case O_V_EXPANDING_WALL:
3048 case O_H_EXPANDING_STEEL_WALL:
3049 case O_V_EXPANDING_STEEL_WALL:
3050 // checks first if direction is changed.
3051 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
3052 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
3053 !cave->expanding_wall_changed) ||
3054 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
3055 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
3056 cave->expanding_wall_changed))
3058 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3060 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3061 play_sound_of_element(cave, get(cave, x, y), x, y);
3064 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3065 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3066 play_sound_of_element(cave, get(cave, x, y), x, y);
3071 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3072 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3073 play_sound_of_element(cave, get(cave, x, y), x, y);
3076 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3077 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3078 play_sound_of_element(cave, get(cave, x, y), x, y);
3083 case O_EXPANDING_WALL:
3084 case O_EXPANDING_STEEL_WALL:
3085 // the wall which grows in all four directions.
3086 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3088 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
3089 play_sound_of_element(cave, get(cave, x, y), x, y);
3092 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
3093 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
3094 play_sound_of_element(cave, get(cave, x, y), x, y);
3097 if (is_space_dir(cave, x, y, GD_MV_UP)) {
3098 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
3099 play_sound_of_element(cave, get(cave, x, y), x, y);
3102 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
3103 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
3104 play_sound_of_element(cave, get(cave, x, y), x, y);
3110 ; // to make compilers happy ...
3112 Info("Step[%03d]", cave->frame); // XXX
3114 int rrr = gd_cave_c64_random(cave);
3117 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
3118 (rrr & cave->slime_permeability_c64) == 0);
3121 * unpredictable: gd_rand_int
3122 * predictable: c64 predictable random generator.
3123 * for predictable, a random number is generated,
3124 * whether or not it is even possible that the stone will be able to pass.
3126 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
3128 GdDirection grav = cave->gravity;
3129 GdDirection oppos = opposite[cave->gravity];
3131 // space under the slime? elements may pass from top to bottom then.
3132 if (is_space_dir(cave, x, y, grav))
3134 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3136 // output a falling xy under
3137 store_dir(cave, x, y, grav, cave->slime_converts_1);
3139 store_dir(cave, x, y, oppos, O_SPACE);
3140 play_sound_of_element(cave, O_SLIME, x, y);
3142 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3144 store_dir(cave, x, y, grav, cave->slime_converts_2);
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) == cave->slime_eats_3)
3150 store_dir(cave, x, y, grav, cave->slime_converts_3);
3151 store_dir(cave, x, y, oppos, O_SPACE);
3152 play_sound_of_element(cave, O_SLIME, x, y);
3154 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3156 // waiting stones pass without awakening
3157 store_dir(cave, x, y, grav, O_WAITING_STONE);
3158 store_dir(cave, x, y, oppos, O_SPACE);
3159 play_sound_of_element(cave, O_SLIME, x, y);
3161 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3163 // chasing stones pass
3164 store_dir(cave, x, y, grav, O_CHASING_STONE);
3165 store_dir(cave, x, y, oppos, O_SPACE);
3166 play_sound_of_element(cave, O_SLIME, x, y);
3171 // or space over the slime? elements may pass from bottom to up then.
3172 if (is_space_dir(cave, x, y, oppos))
3174 if (get_dir(cave, x, y, grav) == O_BLADDER)
3176 // bladders move UP the slime
3177 store_dir(cave, x, y, grav, O_SPACE);
3178 store_dir(cave, x, y, oppos, O_BLADDER_1);
3179 play_sound_of_element(cave, O_SLIME, x, y);
3181 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3183 store_dir(cave, x, y, grav, O_SPACE);
3184 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3185 play_sound_of_element(cave, O_SLIME, x, y);
3187 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3189 store_dir(cave, x, y, grav, O_SPACE);
3190 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3191 play_sound_of_element(cave, O_SLIME, x, y);
3198 case O_FALLING_WALL:
3199 if (is_space_dir(cave, x, y, grav_compat))
3201 // try falling if space under.
3204 for (yy = y + 1; yy < y + cave->h; yy++)
3205 // yy < y + cave->h is to check everything OVER the wall - since caves wrap around !!
3206 if (get(cave, x, yy) != O_SPACE)
3207 // stop cycle when other than space
3210 // if scanning stopped by a player... start falling!
3211 if (get(cave, x, yy) == O_PLAYER ||
3212 get(cave, x, yy) == O_PLAYER_GLUED ||
3213 get(cave, x, yy) == O_PLAYER_BOMB)
3215 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3216 // no sound when the falling wall starts falling!
3221 case O_FALLING_WALL_F:
3222 switch (get_dir(cave, x, y, grav_compat))
3225 case O_PLAYER_GLUED:
3227 // if player under, it explodes - the falling wall, not the player!
3228 explode(cave, x, y);
3233 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3238 play_sound_of_element(cave, get(cave, x, y), x, y);
3239 store(cave, x, y, O_FALLING_WALL);
3244 // ============================================================================
3245 // C O N V E Y O R B E L T S
3246 // ============================================================================
3248 case O_CONVEYOR_RIGHT:
3249 case O_CONVEYOR_LEFT:
3250 // only works if gravity is up or down!!!
3251 // first, check for gravity and running belts.
3252 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3254 const GdDirection *dir;
3258 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3259 if (cave->conveyor_belts_direction_changed)
3261 dir = left ? ccw_eighth : cw_eighth;
3263 // CHECK IF IT CONVEYS THE ELEMENT ABOVE IT
3264 // if gravity is normal, and the conveyor belt has something
3265 // ABOVE which can be moved
3267 // the gravity is up, so anything that should float now goes
3268 // DOWN and touches the conveyor
3269 if ((cave->gravity == GD_MV_DOWN &&
3270 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3271 (cave->gravity == GD_MV_UP &&
3272 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3274 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3275 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3277 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); // move
3278 store_dir(cave, x, y, GD_MV_UP, O_SPACE); // and place a space.
3282 // CHECK IF IT CONVEYS THE ELEMENT BELOW IT
3283 if ((cave->gravity == GD_MV_UP &&
3284 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3285 (cave->gravity == GD_MV_DOWN &&
3286 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3288 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3289 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3291 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); // move
3292 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); // and clear.
3298 // ============================================================================
3300 // ============================================================================
3303 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
3304 move(cave, x, y, GD_MV_RIGHT, O_ROCKET_1);
3306 explode(cave, x, y);
3310 if (is_space_dir(cave, x, y, GD_MV_UP))
3311 move(cave, x, y, GD_MV_UP, O_ROCKET_2);
3313 explode(cave, x, y);
3317 if (is_space_dir(cave, x, y, GD_MV_LEFT))
3318 move(cave, x, y, GD_MV_LEFT, O_ROCKET_3);
3320 explode(cave, x, y);
3324 if (is_space_dir(cave, x, y, GD_MV_DOWN))
3325 move(cave, x, y, GD_MV_DOWN, O_ROCKET_4);
3327 explode(cave, x, y);
3330 // ============================================================================
3331 // S I M P L E C H A N G I N G; E X P L O S I O N S
3332 // ============================================================================
3335 store(cave, x, y, cave->explosion_effect);
3339 store(cave, x, y, O_DIAMOND);
3343 store(cave, x, y, cave->diamond_birth_effect);
3347 store(cave, x, y, O_STONE);
3350 case O_NITRO_EXPL_4:
3351 store(cave, x, y, cave->nitro_explosion_effect);
3355 store(cave, x, y, cave->bomb_explosion_effect);
3358 case O_AMOEBA_2_EXPL_4:
3359 store(cave, x, y, cave->amoeba_2_explosion_effect);
3362 case O_GHOST_EXPL_4:
3364 static GdElement ghost_explode[] =
3366 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3367 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3368 O_WAITING_STONE, O_BITER_1
3371 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3376 store(cave, x, y, O_STEEL);
3380 store(cave, x, y, O_CLOCK);
3384 explode(cave, x, y);
3387 case O_TRAPPED_DIAMOND:
3388 if (cave->diamond_key_collected)
3389 store(cave, x, y, O_DIAMOND);
3393 if (cave->gate_open) // if no more diamonds needed
3394 store(cave, x, y, O_OUTBOX); // open outbox
3397 case O_PRE_INVIS_OUTBOX:
3398 if (cave->gate_open) // if no more diamonds needed
3399 store(cave, x, y, O_INVIS_OUTBOX); // open outbox. invisible one :P
3403 if (cave->hatched && !inbox_toggle) // if it is time of birth
3404 store(cave, x, y, O_PRE_PL_1);
3405 inbox_toggle = !inbox_toggle;
3409 store(cave, x, y, O_PLAYER);
3434 case O_GHOST_EXPL_1:
3435 case O_GHOST_EXPL_2:
3436 case O_GHOST_EXPL_3:
3446 case O_NITRO_EXPL_1:
3447 case O_NITRO_EXPL_2:
3448 case O_NITRO_EXPL_3:
3449 case O_AMOEBA_2_EXPL_1:
3450 case O_AMOEBA_2_EXPL_2:
3451 case O_AMOEBA_2_EXPL_3:
3452 // simply the next identifier
3471 found_water = TRUE; // for sound
3472 // simply the next identifier
3476 case O_BLADDER_SPENDER:
3477 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3479 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3480 store(cave, x, y, O_PRE_STEEL_1);
3481 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3486 // other inanimate elements that do nothing
3493 // ============================================================================
3495 // ============================================================================
3497 // another scan-like routine:
3498 // short explosions (for example, in bd1) started with explode_2.
3499 // internally we use explode_1; and change it to explode_2 if needed.
3500 if (cave->short_explosions)
3502 for (y = 0; y < cave->h; y++)
3504 for (x = 0; x < cave->w; x++)
3506 if (is_first_stage_of_explosion(cave, x, y))
3508 next(cave, x, y); // select next frame of explosion
3510 // forget scanned flag immediately
3511 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3517 // finally: forget "scanned" flags for objects.
3518 // also, check for time penalties.
3519 // these is something like an effect table, but we do not really use one.
3520 for (y = 0; y < cave->h; y++)
3522 for (x = 0; x < cave->w; x++)
3524 if (get(cave, x, y) & SCANNED)
3525 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3527 if (get(cave, x, y) == O_TIME_PENALTY)
3529 store(cave, x, y, O_GRAVESTONE);
3531 // there is time penalty for destroying the voodoo
3532 time_decrement_sec += cave->time_penalty;
3537 // this loop finds the coordinates of the player. needed for scrolling and chasing stone.
3538 // but we only do this, if a living player was found. if not yet, the setup
3539 // routine coordinates are used
3540 if (cave->player_state == GD_PL_LIVING)
3542 if (cave->active_is_first_found)
3544 // to be 1stb compatible, we do everything backwards.
3545 for (y = cave->h - 1; y >= 0; y--)
3547 for (x = cave->w - 1; x >= 0; x--)
3549 if (is_player(cave, x, y))
3551 // here we remember the coordinates.
3560 // as in the original: look for the last one
3561 for (y = 0; y < cave->h; y++)
3563 for (x = 0; x < cave->w; x++)
3565 if (is_player(cave, x, y))
3567 // here we remember the coordinates.
3576 // record coordinates of player for chasing stone
3577 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3579 cave->px[i] = cave->px[i + 1];
3580 cave->py[i] = cave->py[i + 1];
3583 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3584 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3588 // update timing calculated by iterating and counting elements
3589 update_cave_speed(cave);
3591 // cave 3 sounds. precedence is controlled by the sound_play function.
3592 // but we have to check amoeba&magic together as they had a different gritty sound when mixed
3594 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3596 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3597 cave->magic_wall_sound);
3599 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3600 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3601 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3603 if (amoeba_sound && magic_sound)
3605 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3610 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3611 else if (magic_sound)
3612 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3617 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3618 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3619 play_sound_of_element(cave, O_AMOEBA, x, y);
3622 // pneumatic hammer sound - overrides everything.
3623 if (cave->pneumatic_hammer_active_delay > 0)
3624 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3627 // ============================================================================
3629 // ============================================================================
3633 // check if player is alive.
3634 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 1) || cave->kill_player)
3635 cave->player_state = GD_PL_DIED;
3637 // check if any voodoo exploded, and kill players the next scan if that happended.
3638 if (cave->voodoo_touched)
3639 cave->kill_player = TRUE;
3643 // check flags after evaluating.
3644 if (cave->amoeba_state == GD_AM_AWAKE)
3646 if (amoeba_count >= cave->amoeba_max_count)
3647 cave->amoeba_state = GD_AM_TOO_BIG;
3648 if (amoeba_found_enclosed)
3649 cave->amoeba_state = GD_AM_ENCLOSED;
3652 // amoeba can also be turned into diamond by magic wall
3653 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3654 cave->amoeba_state = GD_AM_ENCLOSED;
3657 if (cave->amoeba_2_state == GD_AM_AWAKE)
3659 // check flags after evaluating.
3660 if (amoeba_2_count >= cave->amoeba_2_max_count)
3661 cave->amoeba_2_state = GD_AM_TOO_BIG;
3663 if (amoeba_2_found_enclosed)
3664 cave->amoeba_2_state = GD_AM_ENCLOSED;
3667 // amoeba 2 can also be turned into diamond by magic wall
3668 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3669 cave->amoeba_2_state = GD_AM_ENCLOSED;
3671 // now check times. ---------------------------
3672 // decrement time if a voodoo was killed.
3673 cave->time -= time_decrement_sec * cave->timing_factor;
3677 // only decrement time when player is already born.
3680 int secondsbefore, secondsafter;
3682 secondsbefore = cave->time / cave->timing_factor;
3683 cave->time -= cave->speed;
3684 if (cave->time <= 0)
3687 secondsafter = cave->time / cave->timing_factor;
3688 if (cave->time / cave->timing_factor < 10)
3689 // if less than 10 seconds, no walking sound, but play explosion sound
3690 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3692 if (secondsbefore != secondsafter)
3693 gd_cave_set_seconds_sound(cave);
3696 // a gravity switch was activated; seconds counting down
3697 if (cave->gravity_will_change > 0)
3699 cave->gravity_will_change -= cave->speed;
3700 if (cave->gravity_will_change < 0)
3701 cave->gravity_will_change = 0;
3703 if (cave->gravity_will_change == 0)
3705 cave->gravity = cave->gravity_next_direction;
3706 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); // takes precedence over amoeba and magic wall sound
3710 // creatures direction automatically change
3711 if (cave->creatures_direction_will_change > 0)
3713 cave->creatures_direction_will_change -= cave->speed;
3714 if (cave->creatures_direction_will_change < 0)
3715 cave->creatures_direction_will_change = 0;
3717 if (cave->creatures_direction_will_change == 0)
3719 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3721 cave->creatures_backwards = !cave->creatures_backwards;
3722 cave->creatures_direction_will_change =
3723 cave->creatures_direction_auto_change_time * cave->timing_factor;
3727 // magic wall; if active&wait or not wait for hatching
3728 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3729 (cave->hatched || !cave->magic_timer_wait_for_hatching) &&
3730 !(cave->magic_wall_time == 0 && cave->magic_timer_zero_is_infinite))
3732 cave->magic_wall_time -= cave->speed;
3733 if (cave->magic_wall_time < 0)
3734 cave->magic_wall_time = 0;
3735 if (cave->magic_wall_time == 0)
3736 cave->magic_wall_state = GD_MW_EXPIRED;
3739 // we may wait for hatching, when starting amoeba
3740 if (cave->amoeba_timer_started_immediately ||
3741 (cave->amoeba_state == GD_AM_AWAKE &&
3742 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3744 cave->amoeba_time -= cave->speed;
3745 if (cave->amoeba_time < 0)
3746 cave->amoeba_time = 0;
3747 if (cave->amoeba_time == 0)
3748 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3751 // we may wait for hatching, when starting amoeba
3752 if (cave->amoeba_timer_started_immediately ||
3753 (cave->amoeba_2_state == GD_AM_AWAKE &&
3754 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3756 cave->amoeba_2_time -= cave->speed;
3757 if (cave->amoeba_2_time < 0)
3758 cave->amoeba_2_time = 0;
3759 if (cave->amoeba_2_time == 0)
3760 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3763 // check for player hatching.
3764 start_signal = FALSE;
3766 // if not the c64 scheduling, but the correct frametime is used,
3767 // hatching delay should always be decremented.
3768 // otherwise, the if (millisecs...) condition below will set this.
3769 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3771 // NON-C64 scheduling
3772 if (cave->hatching_delay_frame > 0)
3774 // for milliseconds-based, non-c64 schedulings, hatching delay means frames.
3775 cave->hatching_delay_frame--;
3776 if (cave->hatching_delay_frame == 0)
3777 start_signal = TRUE;
3783 if (cave->hatching_delay_time > 0)
3785 // for c64 schedulings, hatching delay means milliseconds.
3786 cave->hatching_delay_time -= cave->speed;
3787 if (cave->hatching_delay_time <= 0)
3789 cave->hatching_delay_time = 0;
3790 start_signal = TRUE;
3795 // if decremented hatching, and it became zero:
3798 // THIS IS THE CAVE START SIGNAL
3800 // record that now the cave is in its normal state
3801 cave->hatched = TRUE;
3803 // if diamonds needed is below zero, we count the available diamonds now.
3804 gd_cave_count_diamonds(cave);
3806 // setup direction auto change
3807 if (cave->creatures_direction_auto_change_time)
3809 cave->creatures_direction_will_change =
3810 cave->creatures_direction_auto_change_time * cave->timing_factor;
3812 if (cave->creatures_direction_auto_change_on_start)
3813 cave->creatures_backwards = !cave->creatures_backwards;
3816 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3820 if (cave->biters_wait_frame == 0)
3821 cave->biters_wait_frame = cave->biter_delay_frame;
3823 cave->biters_wait_frame--;
3825 // replicators delay
3826 if (cave->replicators_wait_frame == 0)
3827 cave->replicators_wait_frame = cave->replicator_delay_frame;
3829 cave->replicators_wait_frame--;
3832 // ============================================================================
3834 // ============================================================================
3837 // check if cave failed by timeout is done in main game engine
3839 // check if cave failed by timeout
3840 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3842 gd_cave_clear_sounds(cave);
3843 cave->player_state = GD_PL_TIMEOUT;
3844 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3848 // set these for drawing.
3849 cave->last_direction = player_move;
3851 // here we remember last movements for animation. this is needed here,
3852 // as animation is in sync with the game, not the keyboard directly.
3853 // (for example, after exiting the cave, the player was "running" in the
3854 // original, till bonus points were counted for remaining time and so on.
3855 if (player_move == GD_MV_LEFT ||
3856 player_move == GD_MV_UP_LEFT ||
3857 player_move == GD_MV_DOWN_LEFT)
3858 cave->last_horizontal_direction = GD_MV_LEFT;
3860 if (player_move == GD_MV_RIGHT ||
3861 player_move == GD_MV_UP_RIGHT ||
3862 player_move == GD_MV_DOWN_RIGHT)
3863 cave->last_horizontal_direction = GD_MV_RIGHT;
3865 cave->frame++; // XXX
3868 void set_initial_cave_speed(GdCave *cave)
3873 // set cave get function; to implement perfect or lineshifting borders
3874 if (cave->lineshift)
3875 cave->getp = getp_shift;
3877 cave->getp = getp_perfect;
3879 // check whether to scan the first and last line
3880 if (cave->border_scan_first_and_last)
3891 for (y = ymin; y <= ymax; y++)
3893 for (x = 0; x < cave->w; x++)
3895 // add the ckdelay correction value for every element seen.
3896 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3900 // update timing calculated by iterating and counting elements
3901 update_cave_speed(cave);