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];
532 /* final values with range correction */
533 int new_x = getx(cave, raw_x, raw_y);
534 int new_y = gety(cave, raw_x, raw_y);
535 int new_dir = (dir > GD_MV_TWICE ? dir - GD_MV_TWICE : dir);
537 game_bd.game->dir_buffer[new_y][new_x] = new_dir;
540 /* store an element at the given position */
541 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
543 GdElement *e = getp(cave, x, y);
547 play_sound_of_element(cave, O_LAVA, x, y);
555 /* store an element with SCANNED flag turned on */
556 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
558 store(cave, x, y, element | SCANNED);
561 /* store an element to a neighbouring cell */
562 static inline void store_dir(GdCave *cave, const int x, const int y,
563 const GdDirection dir, const GdElement element)
565 store_dir_buffer(cave, x, y, dir);
566 store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED);
569 /* store an element to a neighbouring cell */
570 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
571 const GdDirection dir, const GdElement element)
573 store_dir_buffer(cave, x, y, dir);
574 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
577 /* move element to direction; then place space at x, y */
578 static inline void move(GdCave *cave, const int x, const int y,
579 const GdDirection dir, const GdElement e)
581 store_dir(cave, x, y, dir, e);
582 store(cave, x, y, O_SPACE);
585 /* increment a cave element; can be used for elements which are one after
586 the other, for example bladder1, bladder2, bladder3... */
587 static inline void next(GdCave *cave, const int x, const int y)
589 (*getp(cave, x, y))++;
592 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
594 if (non_explodable (cave, x, y))
597 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
598 cave->voodoo_touched = TRUE;
600 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
601 /* voodoo turns into a time penalty */
602 store_sc(cave, x, y, O_TIME_PENALTY);
603 else if (get(cave, x, y) == O_NITRO_PACK ||
604 get(cave, x, y) == O_NITRO_PACK_F)
605 /* nitro pack inside an explosion - it is now triggered */
606 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
608 /* for everything else */
609 store_sc(cave, x, y, explode_to);
612 /* a creature explodes to a 3x3 something. */
613 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
617 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
618 cave->ckdelay += 1200;
619 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
621 for (yy = y - 1; yy <= y + 1; yy++)
622 for (xx = x - 1; xx <= x + 1; xx++)
623 cell_explode(cave, xx, yy, explode_to);
626 static void nitro_explode(GdCave *cave, int x, int y)
630 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
631 cave->ckdelay += 1200;
632 gd_sound_play(cave, GD_S_NITRO_PACK_EXPLODING, get(cave, x, y), x, y);
634 for (yy = y - 1; yy <= y + 1; yy++)
635 for (xx = x - 1; xx <= x + 1; xx++)
636 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
638 /* the current cell is explicitly changed into a nitro expl,
639 as cell_explode changes it to a triggered nitro pack */
640 store_sc(cave, x, y, O_NITRO_EXPL_1);
643 /* a voodoo explodes, leaving a 3x3 steel and a time penalty behind. */
644 static void voodoo_explode(GdCave *cave, int x, int y)
648 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
649 cave->ckdelay += 1000;
651 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
652 if (cave->voodoo_any_hurt_kills_player)
653 cave->voodoo_touched = TRUE;
655 /* voodoo explodes to 3x3 steel */
656 for (yy = y - 1; yy <= y + 1; yy++)
657 for (xx = x - 1; xx <= x + 1; xx++)
658 store_sc(cave, xx, yy, O_PRE_STEEL_1);
660 /* middle is a time penalty (which will be turned into a gravestone) */
661 store_sc(cave, x, y, O_TIME_PENALTY);
664 /* a bomb does not explode the voodoo, neither does the ghost.
665 this function check this, and stores the new element or not.
666 destroying the voodoo is also controlled by the
667 voodoo_disappear_in_explosion flag. */
668 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
670 if (non_explodable (cave, x, y))
673 /* bomb does not explode voodoo */
674 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
677 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
678 cave->voodoo_touched = TRUE;
680 store_sc (cave, x, y, expl);
683 /* X shaped ghost explosion; does not touch voodoo! */
684 static void ghost_explode(GdCave *cave, const int x, const int y)
686 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
688 /* the processing of an explosion took pretty much time: processing 5 elements */
689 cave->ckdelay += 650;
691 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
692 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
693 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
694 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
695 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
698 /* +shaped bomb explosion; does not touch voodoo! */
699 static void bomb_explode(GdCave *cave, const int x, const int y)
701 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
703 /* the processing of an explosion took pretty much time: processing 5 elements */
704 cave->ckdelay += 650;
706 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
707 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
708 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
709 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
710 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
714 explode an element with the appropriate type of exlposion.
716 static void explode(GdCave *cave, int x, int y)
718 GdElement e = get(cave, x, y) & O_MASK;
723 ghost_explode(cave, x, y);
727 bomb_explode(cave, x, y);
731 voodoo_explode(cave, x, y);
736 case O_NITRO_PACK_EXPLODE:
737 nitro_explode(cave, x, y);
741 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
744 case O_FALLING_WALL_F:
745 creature_explode(cave, x, y, O_EXPLODE_1);
752 creature_explode(cave, x, y, cave->butterfly_explode_to);
759 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
766 creature_explode(cave, x, y, cave->firefly_explode_to);
769 case O_ALT_FIREFLY_1:
770 case O_ALT_FIREFLY_2:
771 case O_ALT_FIREFLY_3:
772 case O_ALT_FIREFLY_4:
773 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
779 case O_PLAYER_STIRRING:
780 case O_PLAYER_PNEUMATIC_LEFT:
781 case O_PLAYER_PNEUMATIC_RIGHT:
782 creature_explode(cave, x, y, O_EXPLODE_1);
789 creature_explode(cave, x, y, cave->stonefly_explode_to);
796 creature_explode(cave, x, y, cave->dragonfly_explode_to);
804 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
806 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
810 player eats specified object.
811 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
812 returns other element if something other appears there and he can't move.
813 cave pointer is needed to know the diamond values.
815 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
822 cave->diamond_key_collected = TRUE;
823 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
828 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
833 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
838 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
845 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
852 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
859 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
864 case O_CREATURE_SWITCH: /* creatures change direction. */
865 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
866 cave->creatures_backwards = !cave->creatures_backwards;
869 case O_EXPANDING_WALL_SWITCH: /* expanding wall change direction. */
870 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
871 cave->expanding_wall_changed = !cave->expanding_wall_changed;
874 case O_BITER_SWITCH: /* biter change delay */
875 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
876 cave->biter_delay_frame++;
877 if (cave->biter_delay_frame == 4)
878 cave->biter_delay_frame = 0;
881 case O_REPLICATOR_SWITCH: /* replicator on/off switch */
882 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
883 cave->replicators_active = !cave->replicators_active;
886 case O_CONVEYOR_SWITCH: /* conveyor belts on/off */
887 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
888 cave->conveyor_belts_active = !cave->conveyor_belts_active;
891 case O_CONVEYOR_DIR_SWITCH: /* conveyor belts switch direction */
892 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
893 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
899 case O_STEEL_EATABLE:
900 case O_BRICK_EATABLE:
901 case O_DIRT_SLOPED_UP_RIGHT:
902 case O_DIRT_SLOPED_UP_LEFT:
903 case O_DIRT_SLOPED_DOWN_LEFT:
904 case O_DIRT_SLOPED_DOWN_RIGHT:
907 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
911 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
912 cave->sweet_eaten = TRUE;
915 case O_PNEUMATIC_HAMMER:
916 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
917 cave->got_pneumatic_hammer = TRUE;
922 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
923 cave->time += cave->time_bonus * cave->timing_factor;
924 if (cave->time > cave->max_time * cave->timing_factor)
925 cave->time -= cave->max_time * cave->timing_factor;
926 /* no space, rather a dirt remains there... */
930 case O_FLYING_DIAMOND:
931 // prevent diamond sounds for O_SKELETON (see below)
932 if (x != -1 && y != -1)
933 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
934 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
936 cave->score += cave->diamond_value;
937 cave->diamonds_collected++;
939 if (cave->diamonds_needed == cave->diamonds_collected)
941 cave->gate_open = TRUE;
943 /* extra is worth more points. */
944 cave->diamond_value = cave->extra_diamond_value;
946 cave->gate_open_flash = 1;
947 cave->sound3 = GD_S_CRACKING;
948 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
953 cave->skeletons_collected++;
955 /* as if player got a diamond */
956 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
957 player_get_element(cave, O_DIAMOND, -1, -1);
959 /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
960 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
965 cave->player_state = GD_PL_EXITED; /* player now exits the cave! */
969 case O_LAVA: /* player goes into lava, as if it was space */
970 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
974 /* the object will remain there. */
980 process a crazy dream-style teleporter.
981 called from gd_cave_iterate, for a player or a player_bomb.
982 player is standing at px, py, and trying to move in the direction player_move,
983 where there is a teleporter.
984 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
985 around). the first teleporter we find, and which is suitable, will be the destination.
986 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
988 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
997 /* jump to next element; wrap around columns and rows. */
1009 /* if we found a teleporter... */
1010 if (get(cave, tx, ty) == O_TELEPORTER &&
1011 is_space_dir(cave, tx, ty, player_move))
1013 /* new player appears near teleporter found */
1014 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1016 /* current player disappears */
1017 store(cave, px, py, O_SPACE);
1019 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1021 return TRUE; /* return true as teleporter worked */
1024 /* loop until we get back to original coordinates */
1025 while (tx != px || ty != py);
1027 /* return false as we did not find any usable teleporter */
1032 try to push an element.
1033 returns true if the push is possible; also does move the specified _element_.
1034 up to the caller to move the _player_itself_.
1036 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1039 GdElement what = get_dir(cave, x, y, player_move);
1041 /* gravity for falling wall, bladder, ... */
1042 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1048 case O_WAITING_STONE:
1051 case O_CHASING_STONE:
1053 case O_FLYING_STONE:
1055 /* pushing some kind of stone or nut */
1056 /* directions possible: 90degrees cw or ccw to current gravity. */
1057 /* only push if player dir is orthogonal to gravity,
1058 ie. gravity down, pushing left & right possible */
1059 if (player_move == ccw_fourth[cave->gravity] ||
1060 player_move == cw_fourth[cave->gravity])
1066 /* different probabilities for different elements. */
1069 case O_WAITING_STONE:
1070 /* waiting stones are light, can always push */
1074 case O_CHASING_STONE:
1075 /* chasing can be pushed if player is turbo */
1076 if (cave->sweet_eaten)
1081 /* mega may(!) be pushed if player is turbo */
1082 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1088 case O_FLYING_STONE:
1090 if (cave->sweet_eaten)
1091 prob = cave->pushing_stone_prob_sweet; /* probability with sweet */
1093 prob = cave->pushing_stone_prob; /* probability without sweet. */
1100 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1101 gd_rand_int_range(cave->random, 0, 1000000) < prob)
1103 /* if decided that he will be able to push, */
1104 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1105 play_sound_of_element_pushing(cave, what, x, y);
1120 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1121 * not an O_BLADDER_x. */
1122 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
1124 /* first check: we cannot push a bladder "up" */
1125 if (player_move != opposite[grav_compat])
1127 /* pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check. */
1128 /* player moving in the direction of gravity. */
1132 if (player_move == grav_compat)
1134 /* pushing bladder down */
1135 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1136 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1137 /* if no space to push down, maybe left (down-left to player) */
1138 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1140 /* left is "down, turned right (cw)" */
1141 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1142 /* if not, maybe right (down-right to player) */
1143 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1144 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1147 /* pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check. */
1151 else if (player_move == cw_fourth[grav_compat])
1153 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) /* pushing it left */
1154 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1155 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) /* maybe down, and player will move left */
1156 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1157 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) /* maybe up, and player will move left */
1158 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1161 /* pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check. */
1165 else if (player_move == ccw_fourth[grav_compat])
1167 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) /* pushing it right */
1168 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1169 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) /* maybe down, and player will move right */
1170 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1171 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) /* maybe up, and player will move right */
1172 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1176 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1181 /* a box is only pushed with the fire pressed */
1184 /* but always with 100% probability */
1185 switch (player_move)
1191 /* pushing in some dir, two steps in that dir - is there space? */
1192 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1195 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1197 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1202 /* push in no other directions possible */
1208 /* pushing of other elements not possible */
1216 /* from the key press booleans, create a direction */
1217 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1219 GdDirection player_move;
1221 /* from the key press booleans, create a direction */
1223 player_move = GD_MV_UP_RIGHT;
1224 else if (down && right)
1225 player_move = GD_MV_DOWN_RIGHT;
1226 else if (down && left)
1227 player_move = GD_MV_DOWN_LEFT;
1228 else if (up && left)
1229 player_move = GD_MV_UP_LEFT;
1231 player_move = GD_MV_UP;
1233 player_move = GD_MV_DOWN;
1235 player_move = GD_MV_LEFT;
1237 player_move = GD_MV_RIGHT;
1239 player_move = GD_MV_STILL;
1244 /* clear these to no sound; and they will be set during iteration. */
1245 void gd_cave_clear_sounds(GdCave *cave)
1247 cave->sound1 = GD_S_NONE;
1248 cave->sound2 = GD_S_NONE;
1249 cave->sound3 = GD_S_NONE;
1252 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1253 GdElement falling_element)
1255 if (cave->gravity_disabled)
1258 if (is_space_dir(cave, x, y, falling_direction))
1260 /* beginning to fall */
1261 play_sound_of_element(cave, get(cave, x, y), x, y);
1262 move(cave, x, y, falling_direction, falling_element);
1265 /* check if it is on a sloped element, and it can roll. */
1266 /* for example, sloped wall looks like: */
1269 /* this is tagged as sloped up&left. */
1270 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1271 /* then check the direction to roll (left or right) */
1272 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1273 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1275 /* rolling down, if sitting on a sloped object */
1276 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1277 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1278 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1280 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1281 so here we use cw_fourth */
1282 play_sound_of_element(cave, get(cave, x, y), x, y);
1283 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1285 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1286 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1287 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1289 /* rolling right? */
1290 play_sound_of_element(cave, get(cave, x, y), x, y);
1291 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1296 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1298 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1299 cave->voodoo_dies_by_stone)
1301 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1302 explode_dir(cave, x, y, fall_dir);
1309 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1311 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1312 cave->voodoo_collects_diamonds)
1314 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1315 player_get_element(cave, O_DIAMOND, x, y); /* as if player got diamond */
1316 store(cave, x, y, O_SPACE); /* diamond disappears */
1323 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1324 GdDirection fall_dir, GdElement bouncing)
1326 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1327 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1330 store(cave, x, y, bouncing);
1331 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1333 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1341 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1342 GdDirection fall_dir, GdElement magic)
1344 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1346 play_sound_of_element(cave, O_DIAMOND, x, y); /* always play diamond sound */
1348 if (cave->magic_wall_state == GD_MW_DORMANT)
1349 cave->magic_wall_state = GD_MW_ACTIVE;
1351 if (cave->magic_wall_state == GD_MW_ACTIVE &&
1352 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1354 /* if magic wall active and place underneath, it turns element
1355 into anything the effect says to do. */
1356 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1359 /* active or non-active or anything, element falling in will always disappear */
1360 store(cave, x, y, O_SPACE);
1368 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1370 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1372 explode_dir(cave, x, y, fall_dir);
1379 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1380 GdDirection fall_dir, GdElement bouncing)
1382 if (is_space_dir(cave, x, y, fall_dir))
1384 /* falling further */
1385 move(cave, x, y, fall_dir, get(cave, x, y));
1390 /* check if it is on a sloped element, and it can roll. */
1391 /* for example, sloped wall looks like: */
1394 /* this is tagged as sloped up&left. */
1395 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1396 /* then check the direction to roll (left or right) */
1397 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1399 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1401 /* sloped element, falling to left or right */
1402 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1403 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1404 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1406 play_sound_of_element(cave, get(cave, x, y), x, y);
1408 /* try to roll left first - see O_STONE to understand why cw_fourth */
1409 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1411 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1412 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1413 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1415 play_sound_of_element(cave, get(cave, x, y), x, y);
1417 /* if not, try to roll right */
1418 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1422 /* cannot roll in any direction, so it stops */
1423 play_sound_of_element(cave, get(cave, x, y), x, y);
1424 store(cave, x, y, bouncing);
1430 /* any other element, stops */
1431 play_sound_of_element(cave, get(cave, x, y), x, y);
1432 store(cave, x, y, bouncing);
1436 static void update_cave_speed(GdCave *cave)
1438 /* update timing calculated by iterating and counting elements which were slow to process on c64 */
1439 switch (cave->scheduling)
1441 case GD_SCHEDULING_MILLISECONDS:
1442 /* cave->speed already contains the milliseconds value, do not touch it */
1445 case GD_SCHEDULING_BD1:
1446 if (!cave->intermission)
1447 /* non-intermissions */
1448 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1450 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
1451 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1454 case GD_SCHEDULING_BD1_ATARI:
1455 /* about 20ms/frame faster than c64 version */
1456 if (!cave->intermission)
1457 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); /* non-intermissions */
1459 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); /* for intermissions */
1462 case GD_SCHEDULING_BD2:
1463 /* 60 is a guess. */
1464 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1467 case GD_SCHEDULING_PLCK:
1468 /* 65 is totally empty cave in construction kit, with delay = 0) */
1469 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1472 case GD_SCHEDULING_BD2_PLCK_ATARI:
1473 /* a really fast engine; timing works like c64 plck. */
1474 /* 40 ms was measured in the construction kit, with delay = 0 */
1475 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1478 case GD_SCHEDULING_CRDR:
1479 if (cave->hammered_walls_reappear) /* this made the engine very slow. */
1480 cave->ckdelay += 60000;
1481 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1484 case GD_SCHEDULING_MAX:
1489 /* process a cave. */
1490 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1494 /* for border scan */
1497 /* amoeba found to be enclosed. if not, this is cleared */
1498 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1500 /* counting the number of amoebas. after scan, check if too much */
1501 int amoeba_count, amoeba_2_count;
1503 /* cave scan found water - for sound */
1504 boolean found_water;
1506 boolean inbox_toggle;
1507 boolean start_signal;
1509 /* gravity for falling wall, bladder, ... */
1510 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1512 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1513 static const GdDirection creature_dir[] =
1520 static const GdDirection creature_chdir[] =
1527 int time_decrement_sec;
1529 /* biters eating elements preference, they try to go in this order */
1530 GdElement biter_try[] =
1537 boolean amoeba_sound, magic_sound;
1539 gd_cave_clear_sounds(cave);
1541 /* if diagonal movements not allowed, */
1542 /* horizontal movements have precedence. [BROADRIBB] */
1543 if (!cave->diagonal_movements)
1545 switch (player_move)
1547 case GD_MV_UP_RIGHT:
1548 case GD_MV_DOWN_RIGHT:
1549 player_move = GD_MV_RIGHT;
1553 case GD_MV_DOWN_LEFT:
1554 player_move = GD_MV_LEFT;
1558 /* no correction needed */
1563 /* set cave get function; to implement perfect or lineshifting borders */
1564 if (cave->lineshift)
1566 cave->getp = getp_shift;
1567 cave->getx = getx_shift;
1568 cave->gety = gety_shift;
1572 cave->getp = getp_perfect;
1573 cave->getx = getx_perfect;
1574 cave->gety = gety_perfect;
1577 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1578 if (cave->player_seen_ago < 100)
1579 cave->player_seen_ago++;
1581 if (cave->pneumatic_hammer_active_delay > 0)
1582 cave->pneumatic_hammer_active_delay--;
1584 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1585 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1586 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1587 inbox_toggle = cave->inbox_flash_toggle;
1589 if (cave->gate_open_flash > 0)
1590 cave->gate_open_flash--;
1592 /* score collected this frame */
1595 /* suicide only kills the active player */
1596 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1597 /* we must check if there is a player or not - he may have exploded or something like that */
1598 if (suicide && cave->player_state == GD_PL_LIVING &&
1599 is_player(cave, cave->player_x, cave->player_y))
1600 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1602 /* check for walls reappearing */
1603 if (cave->hammered_reappear)
1605 for (y = 0; y < cave->h; y++)
1607 for (x = 0; x < cave->w; x++)
1609 /* timer for the cell > 0? */
1610 if (cave->hammered_reappear[y][x] > 0)
1612 /* decrease timer */
1613 cave->hammered_reappear[y][x]--;
1615 /* check if it became zero */
1616 if (cave->hammered_reappear[y][x] == 0)
1618 store(cave, x, y, O_BRICK);
1619 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1626 /* variables to check during the scan */
1628 /* will be set to false if any of the amoeba is found free. */
1629 amoeba_found_enclosed = TRUE;
1630 amoeba_2_found_enclosed = TRUE;
1633 found_water = FALSE;
1635 time_decrement_sec = 0;
1637 /* check whether to scan the first and last line */
1638 if (cave->border_scan_first_and_last)
1649 /* the cave scan routine */
1650 for (y = ymin; y <= ymax; y++)
1652 for (x = 0; x < cave->w; x++)
1654 /* if we find a scanned element, change it to the normal one, and that's all. */
1655 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1656 if (get(cave, x, y) & SCANNED)
1658 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1663 /* add the ckdelay correction value for every element seen. */
1664 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1666 switch (get(cave, x, y))
1672 if (cave->kill_player)
1674 explode (cave, x, y);
1678 cave->player_seen_ago = 0;
1679 /* bd4 intermission caves have many players. so if one of them has exited,
1680 * do not change the flag anymore. so this if () is needed */
1681 if (cave->player_state != GD_PL_EXITED)
1682 cave->player_state = GD_PL_LIVING;
1684 /* check for pneumatic hammer things */
1685 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right
1686 for hammer 5) stand on something */
1687 if (player_fire && cave->got_pneumatic_hammer &&
1688 is_space_dir(cave, x, y, player_move) &&
1689 !is_space_dir(cave, x, y, GD_MV_DOWN))
1691 if (player_move == GD_MV_LEFT &&
1692 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1694 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1695 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1696 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1697 break; /* finished. */
1700 if (player_move == GD_MV_RIGHT &&
1701 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1703 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1704 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1705 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1706 break; /* finished. */
1710 if (player_move != GD_MV_STILL)
1712 /* only do every check if he is not moving */
1713 GdElement what = get_dir(cave, x, y, player_move);
1714 GdElement remains = what;
1717 /* if we are 'eating' a teleporter, and the function returns true
1718 (teleporting worked), break here */
1719 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1722 /* try to push element; if successful, break */
1723 push = do_push(cave, x, y, player_move, player_fire);
1733 /* if its a bomb, remember he now has one. */
1734 /* we do not change the "remains" and "what" variables,
1735 so that part of the code will be ineffective */
1736 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1737 store_dir(cave, x, y, player_move, O_SPACE);
1740 store(cave, x, y, O_PLAYER_BOMB);
1742 move(cave, x, y, player_move, O_PLAYER_BOMB);
1746 /* we do not change the "remains" and "what" variables,
1747 so that part of the code will be ineffective */
1748 if (!player_fire && !cave->gravity_switch_active &&
1749 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1751 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1752 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1753 cave->gravity_disabled = TRUE;
1757 case O_GRAVITY_SWITCH:
1758 /* (we cannot use player_get for this as it does not have player_move parameter) */
1759 /* only allow changing direction if the new dir is not diagonal */
1760 if (cave->gravity_switch_active &&
1761 (player_move == GD_MV_LEFT ||
1762 player_move == GD_MV_RIGHT ||
1763 player_move == GD_MV_UP ||
1764 player_move == GD_MV_DOWN))
1766 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1767 cave->gravity_will_change =
1768 cave->gravity_change_time * cave->timing_factor;
1769 cave->gravity_next_direction = player_move;
1770 cave->gravity_switch_active = FALSE;
1775 /* get element - process others.
1776 if cannot get, player_get_element will return the same */
1777 remains = player_get_element(cave, what, x, y);
1782 if (remains != what || remains == O_SPACE)
1784 /* if anything changed, apply the change. */
1786 /* if snapping anything and we have snapping explosions set.
1787 but these is not true for pushing. */
1788 if (remains == O_SPACE && player_fire && !push)
1789 remains = cave->snap_element;
1791 if (remains != O_SPACE || player_fire)
1792 /* if any other element than space, player cannot move.
1793 also if pressing fire, will not move. */
1794 store_dir(cave, x, y, player_move, remains);
1796 /* if space remains there, the player moves. */
1797 move(cave, x, y, player_move, O_PLAYER);
1803 /* much simpler; cannot steal stones */
1804 if (cave->kill_player)
1806 explode(cave, x, y);
1810 cave->player_seen_ago = 0;
1811 /* bd4 intermission caves have many players. so if one of them has exited,
1812 * do not change the flag anymore. so this if () is needed */
1813 if (cave->player_state != GD_PL_EXITED)
1814 cave->player_state = GD_PL_LIVING;
1816 if (player_move != GD_MV_STILL)
1818 /* if the player does not move, nothing to do */
1819 GdElement what = get_dir(cave, x, y, player_move);
1820 GdElement remains = what;
1824 /* placing a bomb into empty space or dirt */
1825 if (is_space_dir(cave, x, y, player_move) ||
1826 is_element_dir(cave, x, y, player_move, O_DIRT))
1828 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1830 /* placed bomb, he is normal player again */
1831 store(cave, x, y, O_PLAYER);
1832 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1837 /* pushing and collecting */
1838 /* if we are 'eating' a teleporter, and the function returns true
1839 (teleporting worked), break here */
1840 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1843 /* player fire is false... */
1844 if (do_push(cave, x, y, player_move, FALSE))
1852 case O_GRAVITY_SWITCH:
1853 /* (we cannot use player_get for this as it does not have
1854 player_move parameter) */
1855 /* only allow changing direction if the new dir is not diagonal */
1856 if (cave->gravity_switch_active &&
1857 (player_move == GD_MV_LEFT ||
1858 player_move == GD_MV_RIGHT ||
1859 player_move == GD_MV_UP ||
1860 player_move == GD_MV_DOWN))
1862 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1863 cave->gravity_will_change =
1864 cave->gravity_change_time * cave->timing_factor;
1865 cave->gravity_next_direction = player_move;
1866 cave->gravity_switch_active = FALSE;
1871 /* get element. if cannot get, player_get_element will return the same */
1872 remains = player_get_element (cave, what, x, y);
1877 /* if element changed, OR there is space, move. */
1878 if (remains != what || remains == O_SPACE)
1880 /* if anything changed, apply the change. */
1881 move(cave, x, y, player_move, O_PLAYER_BOMB);
1886 case O_PLAYER_STIRRING:
1887 if (cave->kill_player)
1889 explode(cave, x, y);
1893 /* stirring sound, if no other walking sound or explosion */
1894 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1896 cave->player_seen_ago = 0;
1897 /* bd4 intermission caves have many players. so if one of them has exited,
1898 * do not change the flag anymore. so this if () is needed */
1899 if (cave->player_state != GD_PL_EXITED)
1900 cave->player_state = GD_PL_LIVING;
1904 /* player "exits" stirring the pot by pressing fire */
1905 cave->gravity_disabled = FALSE;
1906 store(cave, x, y, O_PLAYER);
1907 cave->gravity_switch_active = TRUE;
1911 /* player holding pneumatic hammer */
1912 case O_PLAYER_PNEUMATIC_LEFT:
1913 case O_PLAYER_PNEUMATIC_RIGHT:
1914 /* usual player stuff */
1915 if (cave->kill_player)
1917 explode(cave, x, y);
1921 cave->player_seen_ago = 0;
1922 if (cave->player_state != GD_PL_EXITED)
1923 cave->player_state = GD_PL_LIVING;
1925 /* if hammering time is up, becomes a normal player again. */
1926 if (cave->pneumatic_hammer_active_delay == 0)
1927 store(cave, x, y, O_PLAYER);
1930 /* the active pneumatic hammer itself */
1931 case O_PNEUMATIC_ACTIVE_RIGHT:
1932 case O_PNEUMATIC_ACTIVE_LEFT:
1933 if (cave->pneumatic_hammer_active_delay == 0)
1937 /* pneumatic hammer element disappears */
1938 store(cave, x, y, O_SPACE);
1940 /* which is the new element which appears after that one is hammered? */
1941 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1943 /* if there is a new element, display it */
1944 /* O_NONE might be returned, for example if the element being
1945 hammered explodes during hammering (by a nearby explosion) */
1946 if (new_elem != O_NONE)
1948 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1950 /* and if walls reappear, remember it in array */
1951 if (cave->hammered_walls_reappear)
1955 wall_y = (y + 1) % cave->h;
1956 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1963 * S T O N E S, D I A M O N D S
1965 case O_STONE: /* standing stone */
1966 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1969 case O_MEGA_STONE: /* standing mega_stone */
1970 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1973 case O_DIAMOND: /* standing diamond */
1974 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1977 case O_NUT: /* standing nut */
1978 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1981 case O_DIRT_BALL: /* standing dirt ball */
1982 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1985 case O_DIRT_LOOSE: /* standing loose dirt */
1986 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1989 case O_FLYING_STONE: /* standing stone */
1990 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1993 case O_FLYING_DIAMOND: /* standing diamond */
1994 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1998 * 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
2000 case O_DIRT_BALL_F: /* falling dirt ball */
2001 if (!cave->gravity_disabled)
2002 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2005 case O_DIRT_LOOSE_F: /* falling loose dirt */
2006 if (!cave->gravity_disabled)
2007 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2010 case O_STONE_F: /* falling stone */
2011 if (!cave->gravity_disabled)
2013 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2016 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2019 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2022 if (do_fall_try_crush(cave, x, y, cave->gravity))
2025 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2029 case O_MEGA_STONE_F: /* falling mega */
2030 if (!cave->gravity_disabled)
2032 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2035 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2038 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2041 if (do_fall_try_crush(cave, x, y, cave->gravity))
2044 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2048 case O_DIAMOND_F: /* falling diamond */
2049 if (!cave->gravity_disabled)
2051 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2054 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2057 if (do_fall_try_crush(cave, x, y, cave->gravity))
2060 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2064 case O_NUT_F: /* falling nut */
2065 if (!cave->gravity_disabled)
2067 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2070 if (do_fall_try_crush(cave, x, y, cave->gravity))
2073 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2077 case O_FLYING_STONE_F: /* falling stone */
2078 if (!cave->gravity_disabled)
2080 GdDirection fall_dir = opposite[cave->gravity];
2082 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2085 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2088 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2091 if (do_fall_try_crush(cave, x, y, fall_dir))
2094 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2098 case O_FLYING_DIAMOND_F: /* falling diamond */
2099 if (!cave->gravity_disabled)
2101 GdDirection fall_dir = opposite[cave->gravity];
2103 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2106 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2109 if (do_fall_try_crush(cave, x, y, fall_dir))
2112 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2119 case O_NITRO_PACK: /* standing nitro pack */
2120 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2123 case O_NITRO_PACK_F: /* falling nitro pack */
2124 if (!cave->gravity_disabled)
2126 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
2127 move(cave, x, y, cave->gravity, get(cave, x, y));
2128 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2130 /* try magic wall; if true, function did the work */
2132 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2134 /* falling on a dirt, it does NOT explode - just stops at its place. */
2135 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2136 store(cave, x, y, O_NITRO_PACK);
2139 /* falling on any other element it explodes */
2140 explode(cave, x, y);
2144 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
2145 explode(cave, x, y);
2156 /* if cannot move in any direction, becomes an enclosed cow */
2157 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2158 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2159 store(cave, x, y, O_COW_ENCLOSED_1);
2162 /* THIS IS THE CREATURE MOVE thing copied. */
2163 const GdDirection *creature_move;
2164 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2165 GdElement base; /* base element number (which is like O_***_1) */
2166 int dir, dirn, dirp; /* direction */
2170 dir = get(cave, x, y)-base; /* facing where */
2171 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2173 /* now change direction if backwards */
2174 if (cave->creatures_backwards)
2179 dirn = (dir + 3) & 3; /* fast turn */
2180 dirp = (dir + 1) & 3; /* slow turn */
2184 dirn = (dir + 1) & 3; /* fast turn */
2185 dirp = (dir + 3) & 3; /* slow turn */
2188 if (is_space_dir(cave, x, y, creature_move[dirn]))
2189 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2190 else if (is_space_dir(cave, x, y, creature_move[dir]))
2191 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2193 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2197 /* enclosed cows wait some time before turning to a skeleton */
2198 case O_COW_ENCLOSED_1:
2199 case O_COW_ENCLOSED_2:
2200 case O_COW_ENCLOSED_3:
2201 case O_COW_ENCLOSED_4:
2202 case O_COW_ENCLOSED_5:
2203 case O_COW_ENCLOSED_6:
2204 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2205 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2206 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2207 is_space_dir(cave, x, y, GD_MV_DOWN))
2208 store(cave, x, y, O_COW_1);
2213 case O_COW_ENCLOSED_7:
2214 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2215 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2216 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2217 is_space_dir(cave, x, y, GD_MV_DOWN))
2218 store(cave, x, y, O_COW_1);
2220 store(cave, x, y, O_SKELETON);
2227 case O_ALT_FIREFLY_1:
2228 case O_ALT_FIREFLY_2:
2229 case O_ALT_FIREFLY_3:
2230 case O_ALT_FIREFLY_4:
2235 case O_ALT_BUTTER_1:
2236 case O_ALT_BUTTER_2:
2237 case O_ALT_BUTTER_3:
2238 case O_ALT_BUTTER_4:
2243 /* check if touches a voodoo */
2244 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2245 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2246 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2247 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2248 cave->voodoo_touched = TRUE;
2250 /* check if touches something bad and should explode (includes voodoo by the flags) */
2251 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2252 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2253 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2254 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2255 explode (cave, x, y);
2256 /* otherwise move */
2259 const GdDirection *creature_move;
2260 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2261 GdElement base = -1; /* base element number (which is like O_***_1) */
2262 int dir, dirn, dirp; /* direction */
2264 if (get(cave, x, y) >= O_FIREFLY_1 &&
2265 get(cave, x, y) <= O_FIREFLY_4)
2267 else if (get(cave, x, y) >= O_BUTTER_1 &&
2268 get(cave, x, y) <= O_BUTTER_4)
2270 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2271 get(cave, x, y) <= O_STONEFLY_4)
2272 base = O_STONEFLY_1;
2273 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2274 get(cave, x, y) <= O_ALT_FIREFLY_4)
2275 base = O_ALT_FIREFLY_1;
2276 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2277 get(cave, x, y) <= O_ALT_BUTTER_4)
2278 base = O_ALT_BUTTER_1;
2280 dir = get(cave, x, y) - base; /* facing where */
2281 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2283 /* now change direction if backwards */
2284 if (cave->creatures_backwards)
2289 dirn = (dir + 3) & 3; /* fast turn */
2290 dirp = (dir + 1) & 3; /* slow turn */
2294 dirn = (dir + 1) & 3; /* fast turn */
2295 dirp = (dir + 3) & 3; /* slow turn */
2298 if (is_space_dir(cave, x, y, creature_move[dirn]))
2299 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2300 else if (is_space_dir(cave, x, y, creature_move[dir]))
2301 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2303 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2307 case O_WAITING_STONE:
2308 if (is_space_dir(cave, x, y, grav_compat))
2310 /* beginning to fall */
2312 move(cave, x, y, grav_compat, O_CHASING_STONE);
2314 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2316 /* rolling down a brick wall or a stone */
2317 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2318 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2319 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2321 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2322 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2324 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2325 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2326 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2328 /* or maybe right */
2329 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2334 case O_CHASING_STONE:
2336 int px = cave->px[0];
2337 int py = cave->py[0];
2338 boolean horizontal = gd_rand_boolean(cave->random);
2339 boolean dont_move = FALSE;
2342 /* try to move... */
2347 /*********************************/
2348 /* check for a horizontal movement */
2351 /* if coordinates are the same */
2353 horizontal = !horizontal;
2360 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2362 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2366 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2368 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2377 horizontal = !horizontal;
2385 /********************************/
2386 /* check for a vertical movement */
2389 /* if coordinates are the same */
2391 horizontal = !horizontal;
2397 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2399 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2403 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2405 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2414 horizontal = !horizontal;
2427 /* if we should move in both directions, but can not move in any, stop. */
2432 /* check for horizontal */
2435 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2436 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2437 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2438 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2439 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2440 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2444 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2445 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2446 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2447 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2448 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2449 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2454 /* check for vertical */
2457 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2458 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2459 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2460 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2461 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2462 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2466 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2467 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2468 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2469 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2470 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2471 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2479 if (cave->replicators_wait_frame == 0 &&
2480 cave->replicators_active &&
2481 !cave->gravity_disabled)
2483 /* only replicate, if space is under it. */
2484 /* do not replicate players! */
2485 /* also obeys gravity settings. */
2486 /* only replicate element if it is not a scanned one */
2487 /* do not replicate space... that condition looks like it
2488 makes no sense, but otherwise it generates SCANNED spaces,
2489 which cannot be "collected" by the player, so he cannot run
2490 under a replicator */
2491 if (is_space_dir(cave, x, y, cave->gravity) &&
2492 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2493 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2495 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2496 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2505 if (cave->biters_wait_frame == 0)
2507 static GdDirection biter_move[] =
2515 /* direction, last two bits 0..3 */
2516 int dir = get(cave, x, y) - O_BITER_1;
2517 int dirn = (dir + 3) & 3;
2518 int dirp = (dir + 1) & 3;
2520 GdElement made_sound_of = O_NONE;
2522 for (i = 0; i < ARRAY_SIZE (biter_try); i++)
2524 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2526 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2527 if (biter_try[i] != O_SPACE)
2528 made_sound_of = O_BITER_1; /* sound of a biter eating */
2531 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2533 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2534 if (biter_try[i] != O_SPACE)
2535 made_sound_of = O_BITER_1; /* sound of a biter eating */
2538 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2540 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2541 if (biter_try[i] != O_SPACE)
2542 made_sound_of = O_BITER_1; /* sound of a biter eating */
2547 if (i == ARRAY_SIZE(biter_try))
2548 /* i = number of elements in array: could not move, so just turn */
2549 store(cave, x, y, O_BITER_1 + dirp);
2550 else if (biter_try[i] == O_STONE)
2552 /* if there was a stone there, where we moved...
2553 do not eat stones, just throw them back */
2554 store(cave, x, y, O_STONE);
2555 made_sound_of = O_STONE;
2558 /* if biter did move, we had sound. play it. */
2559 if (made_sound_of != O_NONE)
2560 play_sound_of_element(cave, made_sound_of, x, y);
2568 /* check if touches a voodoo */
2569 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2570 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2571 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2572 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2573 cave->voodoo_touched = TRUE;
2575 /* check if touches something bad and should explode (includes voodoo by the flags) */
2576 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2577 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2578 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2579 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2580 explode (cave, x, y);
2581 /* otherwise move */
2584 const GdDirection *creature_move;
2585 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2586 GdElement base = O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2587 int dir, dirn; /* direction */
2589 dir = get(cave, x, y)-base; /* facing where */
2590 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2592 /* now change direction if backwards */
2593 if (cave->creatures_backwards)
2597 dirn = (dir + 3) & 3; /* fast turn */
2599 dirn = (dir + 1) & 3; /* fast turn */
2601 /* if can move forward, does so. */
2602 if (is_space_dir(cave, x, y, creature_move[dir]))
2603 move(cave, x, y, creature_move[dir], base + dir);
2605 /* otherwise turns 90 degrees in place. */
2606 store(cave, x, y, base + dirn);
2611 store(cave, x, y, O_BLADDER_1);
2622 /* bladder with any delay state: try to convert to clock. */
2623 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2624 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))
2626 /* if touches the specified element, let it be a clock */
2627 store(cave, x, y, O_PRE_CLOCK_1);
2629 /* plays the bladder convert sound */
2630 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2634 /* is space over the bladder? */
2635 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2637 if (get(cave, x, y) == O_BLADDER_8)
2639 /* if it is a bladder 8, really move up */
2640 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2641 play_sound_of_element(cave, O_BLADDER, x, y);
2644 /* if smaller delay, just increase delay. */
2648 /* if not space, is something sloped over the bladder? */
2649 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2650 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2652 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2653 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2654 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2656 /* rolling up, to left */
2657 if (get(cave, x, y) == O_BLADDER_8)
2659 /* if it is a bladder 8, really roll */
2660 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2661 play_sound_of_element(cave, O_BLADDER, x, y);
2664 /* if smaller delay, just increase delay. */
2667 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2668 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2669 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2671 /* rolling up, to left */
2672 if (get(cave, x, y) == O_BLADDER_8)
2674 /* if it is a bladder 8, really roll */
2675 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2676 play_sound_of_element(cave, O_BLADDER, x, y);
2679 /* if smaller delay, just increase delay. */
2684 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2686 store(cave, x, y, O_BLADDER_1);
2691 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2692 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2693 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2694 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2695 explode (cave, x, y);
2700 /* the ghost is given four possibilities to move. */
2701 for (i = 0; i < 4; i++)
2703 static GdDirection dirs[] =
2710 GdDirection random_dir;
2712 random_dir = dirs[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(dirs))];
2713 if (is_space_dir(cave, x, y, random_dir))
2715 move(cave, x, y, random_dir, O_GHOST);
2716 break; /* ghost did move -> exit loop */
2723 * A C T I V E E L E M E N T S
2728 switch (cave->amoeba_state)
2731 store(cave, x, y, cave->amoeba_too_big_effect);
2734 case GD_AM_ENCLOSED:
2735 store(cave, x, y, cave->amoeba_enclosed_effect);
2738 case GD_AM_SLEEPING:
2740 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2741 if (amoeba_found_enclosed)
2742 /* if still found enclosed, check all four directions,
2743 if this one is able to grow. */
2744 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2745 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2746 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2747 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2749 /* not enclosed. this is a local (per scan) flag! */
2750 amoeba_found_enclosed = FALSE;
2751 cave->amoeba_state = GD_AM_AWAKE;
2754 /* if alive, check in which dir to grow (or not) */
2755 if (cave->amoeba_state == GD_AM_AWAKE)
2757 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2759 switch (gd_rand_int_range(cave->random, 0, 4))
2761 /* decided to grow, choose a random direction. */
2762 case 0: /* let this be up. numbers indifferent. */
2763 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2764 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2768 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2769 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2773 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2774 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2778 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2779 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2791 /* check if it is touching an amoeba, and explosion is enabled */
2792 if (cave->amoeba_2_explodes_by_amoeba &&
2793 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2794 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2795 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2796 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2797 explode (cave, x, y);
2799 switch (cave->amoeba_2_state)
2802 store(cave, x, y, cave->amoeba_2_too_big_effect);
2805 case GD_AM_ENCLOSED:
2806 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2809 case GD_AM_SLEEPING:
2811 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2812 if (amoeba_2_found_enclosed)
2813 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2814 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2815 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2816 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2818 /* not enclosed. this is a local (per scan) flag! */
2819 amoeba_2_found_enclosed = FALSE;
2820 cave->amoeba_2_state = GD_AM_AWAKE;
2823 /* if it is alive, decide if it attempts to grow */
2824 if (cave->amoeba_2_state == GD_AM_AWAKE)
2825 if (gd_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2827 switch (gd_rand_int_range(cave->random, 0, 4))
2829 /* decided to grow, choose a random direction. */
2830 case 0: /* let this be up. numbers indifferent. */
2831 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2832 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2836 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2837 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2841 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2842 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2846 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2847 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2857 /* choose randomly, if it spreads */
2858 if (gd_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2860 /* the current one explodes */
2861 store(cave, x, y, cave->acid_turns_to);
2863 /* and if neighbours are eaten, put acid there. */
2864 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2866 play_sound_of_element(cave, O_ACID, x, y);
2867 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2870 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2872 play_sound_of_element(cave, O_ACID, x, y);
2873 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2876 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2878 play_sound_of_element(cave, O_ACID, x, y);
2879 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2882 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2884 play_sound_of_element(cave, O_ACID, x, y);
2885 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2892 if (!cave->water_does_not_flow_down &&
2893 is_space_dir(cave, x, y, GD_MV_DOWN))
2894 /* emulating the odd behaviour in crdr */
2895 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2897 if (is_space_dir(cave, x, y, GD_MV_UP))
2898 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2900 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2901 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2903 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2904 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2908 store(cave, x, y, O_WATER);
2911 case O_H_EXPANDING_WALL:
2912 case O_V_EXPANDING_WALL:
2913 case O_H_EXPANDING_STEEL_WALL:
2914 case O_V_EXPANDING_STEEL_WALL:
2915 /* checks first if direction is changed. */
2916 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2917 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2918 !cave->expanding_wall_changed) ||
2919 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
2920 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
2921 cave->expanding_wall_changed))
2923 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2925 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2926 play_sound_of_element(cave, get(cave, x, y), x, y);
2929 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2930 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2931 play_sound_of_element(cave, get(cave, x, y), x, y);
2936 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2937 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2938 play_sound_of_element(cave, get(cave, x, y), x, y);
2941 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2942 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2943 play_sound_of_element(cave, get(cave, x, y), x, y);
2948 case O_EXPANDING_WALL:
2949 case O_EXPANDING_STEEL_WALL:
2950 /* the wall which grows in all four directions. */
2951 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2953 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2954 play_sound_of_element(cave, get(cave, x, y), x, y);
2957 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2958 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2959 play_sound_of_element(cave, get(cave, x, y), x, y);
2962 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2963 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2964 play_sound_of_element(cave, get(cave, x, y), x, y);
2967 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2968 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2969 play_sound_of_element(cave, get(cave, x, y), x, y);
2975 ; // to make compilers happy ...
2977 Info("Step[%03d]", cave->frame); /* XXX */
2979 int rrr = gd_cave_c64_random(cave);
2982 Info(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2983 (rrr & cave->slime_permeability_c64) == 0);
2986 * unpredictable: gd_rand_int
2987 * predictable: c64 predictable random generator.
2988 * for predictable, a random number is generated,
2989 * whether or not it is even possible that the stone will be able to pass.
2991 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : gd_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2993 GdDirection grav = cave->gravity;
2994 GdDirection oppos = opposite[cave->gravity];
2996 /* space under the slime? elements may pass from top to bottom then. */
2997 if (is_space_dir(cave, x, y, grav))
2999 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3001 /* output a falling xy under */
3002 store_dir(cave, x, y, grav, cave->slime_converts_1);
3004 store_dir(cave, x, y, oppos, O_SPACE);
3005 play_sound_of_element(cave, O_SLIME, x, y);
3007 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3009 store_dir(cave, x, y, grav, cave->slime_converts_2);
3010 store_dir(cave, x, y, oppos, O_SPACE);
3011 play_sound_of_element(cave, O_SLIME, x, y);
3013 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3015 store_dir(cave, x, y, grav, cave->slime_converts_3);
3016 store_dir(cave, x, y, oppos, O_SPACE);
3017 play_sound_of_element(cave, O_SLIME, x, y);
3019 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3021 /* waiting stones pass without awakening */
3022 store_dir(cave, x, y, grav, O_WAITING_STONE);
3023 store_dir(cave, x, y, oppos, O_SPACE);
3024 play_sound_of_element(cave, O_SLIME, x, y);
3026 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3028 /* chasing stones pass */
3029 store_dir(cave, x, y, grav, O_CHASING_STONE);
3030 store_dir(cave, x, y, oppos, O_SPACE);
3031 play_sound_of_element(cave, O_SLIME, x, y);
3035 /* or space over the slime? elements may pass from bottom to up then. */
3036 if (is_space_dir(cave, x, y, oppos))
3038 if (get_dir(cave, x, y, grav) == O_BLADDER)
3040 /* bladders move UP the slime */
3041 store_dir(cave, x, y, grav, O_SPACE);
3042 store_dir(cave, x, y, oppos, O_BLADDER_1);
3043 play_sound_of_element(cave, O_SLIME, x, y);
3045 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3047 store_dir(cave, x, y, grav, O_SPACE);
3048 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3049 play_sound_of_element(cave, O_SLIME, x, y);
3051 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3053 store_dir(cave, x, y, grav, O_SPACE);
3054 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3055 play_sound_of_element(cave, O_SLIME, x, y);
3061 case O_FALLING_WALL:
3062 if (is_space_dir(cave, x, y, grav_compat))
3064 /* try falling if space under. */
3067 for (yy = y + 1; yy < y + cave->h; yy++)
3068 /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
3069 if (get(cave, x, yy) != O_SPACE)
3070 /* stop cycle when other than space */
3073 /* if scanning stopped by a player... start falling! */
3074 if (get(cave, x, yy) == O_PLAYER ||
3075 get(cave, x, yy) == O_PLAYER_GLUED ||
3076 get(cave, x, yy) == O_PLAYER_BOMB)
3078 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3079 /* no sound when the falling wall starts falling! */
3084 case O_FALLING_WALL_F:
3085 switch (get_dir(cave, x, y, grav_compat))
3088 case O_PLAYER_GLUED:
3090 /* if player under, it explodes - the falling wall, not the player! */
3091 explode(cave, x, y);
3095 /* continue falling */
3096 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3101 play_sound_of_element(cave, get(cave, x, y), x, y);
3102 store(cave, x, y, O_FALLING_WALL);
3108 * C O N V E Y O R B E L T S
3111 case O_CONVEYOR_RIGHT:
3112 case O_CONVEYOR_LEFT:
3113 /* only works if gravity is up or down!!! */
3114 /* first, check for gravity and running belts. */
3115 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3117 const GdDirection *dir;
3120 /* decide direction */
3121 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3122 if (cave->conveyor_belts_direction_changed)
3124 dir = left ? ccw_eighth : cw_eighth;
3126 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
3127 /* if gravity is normal, and the conveyor belt has something
3128 ABOVE which can be moved
3130 the gravity is up, so anything that should float now goes
3131 DOWN and touches the conveyor */
3132 if ((cave->gravity == GD_MV_DOWN &&
3133 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3134 (cave->gravity == GD_MV_UP &&
3135 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3137 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3138 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3140 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); /* move */
3141 store_dir(cave, x, y, GD_MV_UP, O_SPACE); /* and place a space. */
3145 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3146 if ((cave->gravity == GD_MV_UP &&
3147 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3148 (cave->gravity == GD_MV_DOWN &&
3149 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3151 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3152 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3154 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); /* move */
3155 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); /* and clear. */
3162 * S I M P L E C H A N G I N G; E X P L O S I O N S
3166 store(cave, x, y, cave->explosion_effect);
3170 store(cave, x, y, O_DIAMOND);
3174 store(cave, x, y, cave->diamond_birth_effect);
3178 store(cave, x, y, O_STONE);
3181 case O_NITRO_EXPL_4:
3182 store(cave, x, y, cave->nitro_explosion_effect);
3186 store(cave, x, y, cave->bomb_explosion_effect);
3189 case O_AMOEBA_2_EXPL_4:
3190 store(cave, x, y, cave->amoeba_2_explosion_effect);
3193 case O_GHOST_EXPL_4:
3195 static GdElement ghost_explode[] =
3197 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3198 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3199 O_WAITING_STONE, O_BITER_1
3202 store(cave, x, y, ghost_explode[gd_rand_int_range(cave->random, 0, ARRAY_SIZE(ghost_explode))]);
3207 store(cave, x, y, O_STEEL);
3211 store(cave, x, y, O_CLOCK);
3215 explode(cave, x, y);
3218 case O_TRAPPED_DIAMOND:
3219 if (cave->diamond_key_collected)
3220 store(cave, x, y, O_DIAMOND);
3224 if (cave->gate_open) /* if no more diamonds needed */
3225 store(cave, x, y, O_OUTBOX); /* open outbox */
3228 case O_PRE_INVIS_OUTBOX:
3229 if (cave->gate_open) /* if no more diamonds needed */
3230 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
3234 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
3235 store(cave, x, y, O_PRE_PL_1);
3236 inbox_toggle = !inbox_toggle;
3240 store(cave, x, y, O_PLAYER);
3265 case O_GHOST_EXPL_1:
3266 case O_GHOST_EXPL_2:
3267 case O_GHOST_EXPL_3:
3277 case O_NITRO_EXPL_1:
3278 case O_NITRO_EXPL_2:
3279 case O_NITRO_EXPL_3:
3280 case O_AMOEBA_2_EXPL_1:
3281 case O_AMOEBA_2_EXPL_2:
3282 case O_AMOEBA_2_EXPL_3:
3283 /* simply the next identifier */
3302 found_water = TRUE; /* for sound */
3303 /* simply the next identifier */
3307 case O_BLADDER_SPENDER:
3308 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3310 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3311 store(cave, x, y, O_PRE_STEEL_1);
3312 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3317 /* other inanimate elements that do nothing */
3323 /* POSTPROCESSING */
3325 /* another scan-like routine: */
3326 /* short explosions (for example, in bd1) started with explode_2. */
3327 /* internally we use explode_1; and change it to explode_2 if needed. */
3328 if (cave->short_explosions)
3330 for (y = 0; y < cave->h; y++)
3332 for (x = 0; x < cave->w; x++)
3334 if (is_first_stage_of_explosion(cave, x, y))
3336 next(cave, x, y); /* select next frame of explosion */
3338 /* forget scanned flag immediately */
3339 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3345 /* finally: forget "scanned" flags for objects. */
3346 /* also, check for time penalties. */
3347 /* these is something like an effect table, but we do not really use one. */
3348 for (y = 0; y < cave->h; y++)
3350 for (x = 0; x < cave->w; x++)
3352 if (get(cave, x, y) & SCANNED)
3353 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3355 if (get(cave, x, y) == O_TIME_PENALTY)
3357 store(cave, x, y, O_GRAVESTONE);
3359 /* there is time penalty for destroying the voodoo */
3360 time_decrement_sec += cave->time_penalty;
3365 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3366 /* but we only do this, if a living player was found. if not yet, the setup
3367 routine coordinates are used */
3368 if (cave->player_state == GD_PL_LIVING)
3370 if (cave->active_is_first_found)
3372 /* to be 1stb compatible, we do everything backwards. */
3373 for (y = cave->h - 1; y >= 0; y--)
3375 for (x = cave->w - 1; x >= 0; x--)
3377 if (is_player(cave, x, y))
3379 /* here we remember the coordinates. */
3388 /* as in the original: look for the last one */
3389 for (y = 0; y < cave->h; y++)
3391 for (x = 0; x < cave->w; x++)
3393 if (is_player(cave, x, y))
3395 /* here we remember the coordinates. */
3404 /* record coordinates of player for chasing stone */
3405 for (i = 0; i < ARRAY_SIZE(cave->px) - 1; i++)
3407 cave->px[i] = cave->px[i + 1];
3408 cave->py[i] = cave->py[i + 1];
3411 cave->px[ARRAY_SIZE(cave->px) - 1] = cave->player_x;
3412 cave->py[ARRAY_SIZE(cave->py) - 1] = cave->player_y;
3416 /* update timing calculated by iterating and counting elements */
3417 update_cave_speed(cave);
3419 /* cave 3 sounds. precedence is controlled by the sound_play function. */
3420 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3422 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3424 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3425 cave->magic_wall_sound);
3427 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3428 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3429 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3431 if (amoeba_sound && magic_sound)
3433 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3438 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3439 else if (magic_sound)
3440 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3445 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3446 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3447 play_sound_of_element(cave, O_AMOEBA, x, y);
3450 /* pneumatic hammer sound - overrides everything. */
3451 if (cave->pneumatic_hammer_active_delay > 0)
3452 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3454 /* CAVE VARIABLES */
3458 /* check if player is alive. */
3459 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3460 cave->player_state = GD_PL_DIED;
3462 /* check if any voodoo exploded, and kill players the next scan if that happended. */
3463 if (cave->voodoo_touched)
3464 cave->kill_player = TRUE;
3468 /* check flags after evaluating. */
3469 if (cave->amoeba_state == GD_AM_AWAKE)
3471 if (amoeba_count >= cave->amoeba_max_count)
3472 cave->amoeba_state = GD_AM_TOO_BIG;
3473 if (amoeba_found_enclosed)
3474 cave->amoeba_state = GD_AM_ENCLOSED;
3477 /* amoeba can also be turned into diamond by magic wall */
3478 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3479 cave->amoeba_state = GD_AM_ENCLOSED;
3482 if (cave->amoeba_2_state == GD_AM_AWAKE)
3484 /* check flags after evaluating. */
3485 if (amoeba_2_count >= cave->amoeba_2_max_count)
3486 cave->amoeba_2_state = GD_AM_TOO_BIG;
3488 if (amoeba_2_found_enclosed)
3489 cave->amoeba_2_state = GD_AM_ENCLOSED;
3492 /* amoeba 2 can also be turned into diamond by magic wall */
3493 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3494 cave->amoeba_2_state = GD_AM_ENCLOSED;
3496 /* now check times. --------------------------- */
3497 /* decrement time if a voodoo was killed. */
3498 cave->time -= time_decrement_sec * cave->timing_factor;
3502 /* only decrement time when player is already born. */
3505 int secondsbefore, secondsafter;
3507 secondsbefore = cave->time / cave->timing_factor;
3508 cave->time -= cave->speed;
3509 if (cave->time <= 0)
3512 secondsafter = cave->time / cave->timing_factor;
3513 if (cave->time / cave->timing_factor < 10)
3514 /* if less than 10 seconds, no walking sound, but play explosion sound */
3515 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3517 if (secondsbefore != secondsafter)
3518 gd_cave_set_seconds_sound(cave);
3521 /* a gravity switch was activated; seconds counting down */
3522 if (cave->gravity_will_change > 0)
3524 cave->gravity_will_change -= cave->speed;
3525 if (cave->gravity_will_change < 0)
3526 cave->gravity_will_change = 0;
3528 if (cave->gravity_will_change == 0)
3530 cave->gravity = cave->gravity_next_direction;
3531 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); /* takes precedence over amoeba and magic wall sound */
3535 /* creatures direction automatically change */
3536 if (cave->creatures_direction_will_change > 0)
3538 cave->creatures_direction_will_change -= cave->speed;
3539 if (cave->creatures_direction_will_change < 0)
3540 cave->creatures_direction_will_change = 0;
3542 if (cave->creatures_direction_will_change == 0)
3544 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3546 cave->creatures_backwards = !cave->creatures_backwards;
3547 cave->creatures_direction_will_change =
3548 cave->creatures_direction_auto_change_time * cave->timing_factor;
3552 /* magic wall; if active&wait or not wait for hatching */
3553 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3554 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3556 cave->magic_wall_time -= cave->speed;
3557 if (cave->magic_wall_time < 0)
3558 cave->magic_wall_time = 0;
3559 if (cave->magic_wall_time == 0)
3560 cave->magic_wall_state = GD_MW_EXPIRED;
3563 /* we may wait for hatching, when starting amoeba */
3564 if (cave->amoeba_timer_started_immediately ||
3565 (cave->amoeba_state == GD_AM_AWAKE &&
3566 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3568 cave->amoeba_time -= cave->speed;
3569 if (cave->amoeba_time < 0)
3570 cave->amoeba_time = 0;
3571 if (cave->amoeba_time == 0)
3572 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3575 /* we may wait for hatching, when starting amoeba */
3576 if (cave->amoeba_timer_started_immediately ||
3577 (cave->amoeba_2_state == GD_AM_AWAKE &&
3578 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3580 cave->amoeba_2_time -= cave->speed;
3581 if (cave->amoeba_2_time < 0)
3582 cave->amoeba_2_time = 0;
3583 if (cave->amoeba_2_time == 0)
3584 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3587 /* check for player hatching. */
3588 start_signal = FALSE;
3590 /* if not the c64 scheduling, but the correct frametime is used,
3591 hatching delay should always be decremented. */
3592 /* otherwise, the if (millisecs...) condition below will set this. */
3593 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3595 /* NON-C64 scheduling */
3596 if (cave->hatching_delay_frame > 0)
3598 /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3599 cave->hatching_delay_frame--;
3600 if (cave->hatching_delay_frame == 0)
3601 start_signal = TRUE;
3606 /* C64 scheduling */
3607 if (cave->hatching_delay_time > 0)
3609 /* for c64 schedulings, hatching delay means milliseconds. */
3610 cave->hatching_delay_time -= cave->speed;
3611 if (cave->hatching_delay_time <= 0)
3613 cave->hatching_delay_time = 0;
3614 start_signal = TRUE;
3619 /* if decremented hatching, and it became zero: */
3622 /* THIS IS THE CAVE START SIGNAL */
3624 /* record that now the cave is in its normal state */
3625 cave->hatched = TRUE;
3627 /* if diamonds needed is below zero, we count the available diamonds now. */
3628 gd_cave_count_diamonds(cave);
3630 /* setup direction auto change */
3631 if (cave->creatures_direction_auto_change_time)
3633 cave->creatures_direction_will_change =
3634 cave->creatures_direction_auto_change_time * cave->timing_factor;
3636 if (cave->creatures_direction_auto_change_on_start)
3637 cave->creatures_backwards = !cave->creatures_backwards;
3640 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3644 if (cave->biters_wait_frame == 0)
3645 cave->biters_wait_frame = cave->biter_delay_frame;
3647 cave->biters_wait_frame--;
3649 /* replicators delay */
3650 if (cave->replicators_wait_frame == 0)
3651 cave->replicators_wait_frame = cave->replicator_delay_frame;
3653 cave->replicators_wait_frame--;
3658 /* check if cave failed by timeout is done in main game engine */
3660 /* check if cave failed by timeout */
3661 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3663 gd_cave_clear_sounds(cave);
3664 cave->player_state = GD_PL_TIMEOUT;
3665 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3669 /* set these for drawing. */
3670 cave->last_direction = player_move;
3671 /* here we remember last movements for animation. this is needed here,
3672 as animation is in sync with the game, not the keyboard directly.
3673 (for example, after exiting the cave, the player was "running" in the
3674 original, till bonus points were counted for remaining time and so on. */
3675 if (player_move == GD_MV_LEFT ||
3676 player_move == GD_MV_UP_LEFT ||
3677 player_move == GD_MV_DOWN_LEFT)
3678 cave->last_horizontal_direction = GD_MV_LEFT;
3680 if (player_move == GD_MV_RIGHT ||
3681 player_move == GD_MV_UP_RIGHT ||
3682 player_move == GD_MV_DOWN_RIGHT)
3683 cave->last_horizontal_direction = GD_MV_RIGHT;
3685 cave->frame++; /* XXX */
3688 void set_initial_cave_speed(GdCave *cave)
3693 /* set cave get function; to implement perfect or lineshifting borders */
3694 if (cave->lineshift)
3695 cave->getp = getp_shift;
3697 cave->getp = getp_perfect;
3699 /* check whether to scan the first and last line */
3700 if (cave->border_scan_first_and_last)
3711 for (y = ymin; y <= ymax; y++)
3713 for (x = 0; x < cave->w; x++)
3715 /* add the ckdelay correction value for every element seen. */
3716 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3720 /* update timing calculated by iterating and counting elements */
3721 update_cave_speed(cave);