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.
39 static const GdDirection ccw_eighth[] =
51 static const GdDirection ccw_fourth[] =
65 static const GdDirection cw_eighth[] =
78 static const GdDirection cw_fourth[] =
91 static const GdDirection opposite[] =
104 /* sets timeout sound. */
105 void gd_cave_set_seconds_sound(GdCave *cave)
107 // when not counting bonus time, timeout sounds will be played by main game engine;
108 // also skip timeout sounds when not using native sound engine
109 if (game_bd.game == NULL || game_bd.game->state_counter != GAME_INT_CHECK_BONUS_TIME ||
110 !game.use_native_bd_sound_engine)
113 /* this is an integer division, so 0 seconds can be 0.5 seconds... */
114 /* also, when this reaches 8, the player still has 8.9999 seconds.
115 so the sound is played at almost t = 9s. */
116 switch (cave->time / cave->timing_factor)
118 case 9: gd_sound_play(cave, GD_S_TIMEOUT_10, O_NONE, -1, -1); break;
119 case 8: gd_sound_play(cave, GD_S_TIMEOUT_9, O_NONE, -1, -1); break;
120 case 7: gd_sound_play(cave, GD_S_TIMEOUT_8, O_NONE, -1, -1); break;
121 case 6: gd_sound_play(cave, GD_S_TIMEOUT_7, O_NONE, -1, -1); break;
122 case 5: gd_sound_play(cave, GD_S_TIMEOUT_6, O_NONE, -1, -1); break;
123 case 4: gd_sound_play(cave, GD_S_TIMEOUT_5, O_NONE, -1, -1); break;
124 case 3: gd_sound_play(cave, GD_S_TIMEOUT_4, O_NONE, -1, -1); break;
125 case 2: gd_sound_play(cave, GD_S_TIMEOUT_3, O_NONE, -1, -1); break;
126 case 1: gd_sound_play(cave, GD_S_TIMEOUT_2, O_NONE, -1, -1); break;
127 case 0: gd_sound_play(cave, GD_S_TIMEOUT_1, O_NONE, -1, -1); break;
131 /* play diamond or stone sound of given element. */
132 static void play_sound_of_element(GdCave *cave, GdElement element, int x, int y)
134 /* stone and diamond fall sounds. */
138 gd_sound_play(cave, GD_S_NUT_FALLING, element, x, y);
142 gd_sound_play(cave, GD_S_NUT_IMPACT, element, x, y);
146 gd_sound_play(cave, GD_S_STONE_FALLING, element, x, y);
150 gd_sound_play(cave, GD_S_STONE_IMPACT, element, x, y);
154 gd_sound_play(cave, GD_S_FLYING_STONE_FALLING, element, x, y);
157 case O_FLYING_STONE_F:
158 gd_sound_play(cave, GD_S_FLYING_STONE_IMPACT, element, x, y);
162 gd_sound_play(cave, GD_S_MEGA_STONE_FALLING, element, x, y);
166 gd_sound_play(cave, GD_S_MEGA_STONE_IMPACT, element, x, y);
170 gd_sound_play(cave, GD_S_NITRO_PACK_FALLING, element, x, y);
174 gd_sound_play(cave, GD_S_NITRO_PACK_IMPACT, element, x, y);
178 gd_sound_play(cave, GD_S_FALLING_WALL_FALLING, element, x, y);
181 case O_FALLING_WALL_F:
182 gd_sound_play(cave, GD_S_FALLING_WALL_IMPACT, element, x, y);
185 case O_H_EXPANDING_WALL:
186 case O_V_EXPANDING_WALL:
187 case O_EXPANDING_WALL:
188 case O_H_EXPANDING_STEEL_WALL:
189 case O_V_EXPANDING_STEEL_WALL:
190 case O_EXPANDING_STEEL_WALL:
191 gd_sound_play(cave, GD_S_EXPANDING_WALL, element, x, y);
195 gd_sound_play(cave, GD_S_DIAMOND_FALLING_RANDOM, element, x, y);
199 gd_sound_play(cave, GD_S_DIAMOND_IMPACT_RANDOM, element, x, y);
202 case O_FLYING_DIAMOND:
203 gd_sound_play(cave, GD_S_FLYING_DIAMOND_FALLING_RANDOM, element, x, y);
206 case O_FLYING_DIAMOND_F:
207 gd_sound_play(cave, GD_S_FLYING_DIAMOND_IMPACT_RANDOM, element, x, y);
210 case O_BLADDER_SPENDER:
211 gd_sound_play(cave, GD_S_BLADDER_SPENDER, element, x, y);
215 gd_sound_play(cave, GD_S_BLADDER_CONVERTING, element, x, y);
219 gd_sound_play(cave, GD_S_SLIME, element, x, y);
223 gd_sound_play(cave, GD_S_LAVA, element, x, y);
227 gd_sound_play(cave, GD_S_ACID_SPREADING, element, x, y);
231 gd_sound_play(cave, GD_S_BLADDER_MOVING, element, x, y);
238 gd_sound_play(cave, GD_S_BITER_EATING, element, x, y);
242 gd_sound_play(cave, GD_S_DIRT_BALL_FALLING, element, x, y);
246 gd_sound_play(cave, GD_S_DIRT_BALL_IMPACT, element, x, y);
250 gd_sound_play(cave, GD_S_DIRT_LOOSE_FALLING, element, x, y);
254 gd_sound_play(cave, GD_S_DIRT_LOOSE_IMPACT, element, x, y);
263 /* play sound of given element being pushed. */
264 static void play_sound_of_element_pushing(GdCave *cave, GdElement element, int x, int y)
269 gd_sound_play(cave, GD_S_NUT_PUSHING, element, x, y);
273 gd_sound_play(cave, GD_S_STONE_PUSHING, element, x, y);
277 gd_sound_play(cave, GD_S_FLYING_STONE_PUSHING, element, x, y);
281 gd_sound_play(cave, GD_S_MEGA_STONE_PUSHING, element, x, y);
284 case O_WAITING_STONE:
285 gd_sound_play(cave, GD_S_WAITING_STONE_PUSHING, element, x, y);
288 case O_CHASING_STONE:
289 gd_sound_play(cave, GD_S_CHASING_STONE_PUSHING, element, x, y);
293 gd_sound_play(cave, GD_S_NITRO_PACK_PUSHING, element, x, y);
297 gd_sound_play(cave, GD_S_BLADDER_PUSHING, element, x, y);
306 static inline int getx(const GdCave *cave, const int x, const int y)
308 return cave->getx(cave, x, y);
311 static inline int gety(const GdCave *cave, const int x, const int y)
313 return cave->gety(cave, x, y);
316 /* perfect (non-lineshifting) GET x/y functions; returns range corrected x/y position */
317 static inline int getx_perfect(const GdCave *cave, const int x, const int y)
319 return (x + cave->w) % cave->w;
322 static inline int gety_perfect(const GdCave *cave, const int x, const int y)
324 return (y + cave->h) % cave->h;
327 /* line shifting GET x/y function; returns range corrected x/y position */
328 static inline int getx_shift(const GdCave *cave, int x, int y)
330 return (x + cave->w) % cave->w;
333 static inline int gety_shift(const GdCave *cave, int x, int y)
335 return ((x < 0 ? y - 1 : x >= cave->w ? y + 1 : y) + cave->h) % cave->h;
338 static inline GdElement *getp(const GdCave *cave, const int x, const int y)
340 return cave->getp(cave, x, y);
344 perfect (non-lineshifting) GET function.
345 returns a pointer to a selected cave element by its coordinates.
347 static inline GdElement *getp_perfect(const GdCave *cave, const int x, const int y)
349 /* (x + n) mod n: this works also for x >= n and -n + 1 < x < 0 */
350 return &(cave->map[(y + cave->h) % cave->h][(x + cave->w) % cave->w]);
354 line shifting GET function; returns a pointer to the selected cave element.
355 this is used to emulate the line-shifting behaviour of original games, so that
356 the player entering one side will appear one row above or below on the other.
358 static inline GdElement *getp_shift(const GdCave *cave, int x, int y)
371 y = (y + cave->h) % cave->h;
373 return &(cave->map[y][x]);
376 static inline GdElement get(const GdCave *cave, const int x, const int y)
378 return *getp(cave, x, y);
381 /* returns an element which is somewhere near x,y */
382 static inline GdElement get_dir(const GdCave *cave, const int x, const int y,
383 const GdDirection dir)
385 return get(cave, x + gd_dx[dir], y + gd_dy[dir]);
388 static inline boolean explodes_by_hit_dir(const GdCave *cave, const int x,
389 const int y, GdDirection dir)
391 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_EXPLODES_BY_HIT) != 0;
394 /* returns true if the element is not explodable, for example the steel wall */
395 static inline boolean non_explodable(const GdCave *cave, const int x, const int y)
397 return (gd_elements[get(cave, x,y) & O_MASK].properties & P_NON_EXPLODABLE) != 0;
400 /* returns true if the element can be eaten by the amoeba, eg. space and dirt. */
401 static inline boolean amoeba_eats_dir(const GdCave *cave, const int x, const int y,
402 const GdDirection dir)
404 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_AMOEBA_CONSUMES) != 0;
407 /* returns true if the element is sloped, so stones and diamonds roll down on it.
408 for example a stone or brick wall */
409 static inline boolean sloped_dir(const GdCave *cave, const int x, const int y,
410 const GdDirection dir, const GdDirection slop)
415 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_LEFT) != 0;
418 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_RIGHT) != 0;
421 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_UP) != 0;
424 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_DOWN) != 0;
433 /* returns true if the element is sloped for bladder movement
434 (brick = yes, diamond = no, for example) */
435 static inline boolean sloped_for_bladder_dir (const GdCave *cave, const int x, const int y,
436 const GdDirection dir)
438 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLADDER_SLOPED) != 0;
441 static inline boolean blows_up_flies_dir(const GdCave *cave, const int x, const int y,
442 const GdDirection dir)
444 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLOWS_UP_FLIES) != 0;
447 /* returns true if the element is a counter-clockwise creature */
448 static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
450 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_CCW) != 0;
453 /* returns true if the element is a player */
454 static inline boolean is_player(const GdCave *cave, const int x, const int y)
456 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
459 /* returns true if the element is a player */
460 static inline boolean is_player_dir(const GdCave *cave, const int x, const int y,
461 const GdDirection dir)
463 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PLAYER) != 0;
466 static inline boolean can_be_hammered_dir(const GdCave *cave, const int x, const int y,
467 const GdDirection dir)
469 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
472 /* returns true if the element is explodable and explodes to space, for example the player */
473 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
475 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
478 /* returns true if the element is moved by the conveyor belt */
479 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
480 const GdDirection dir)
482 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
485 /* returns true if the element is moved by the conveyor belt */
486 static inline boolean moved_by_conveyor_bottom_dir(const GdCave *cave, const int x, const int y,
487 const GdDirection dir)
489 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_BOTTOM) != 0;
492 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
493 const GdDirection dir)
495 return (get_dir(cave, x, y, dir) & SCANNED) != 0;
498 /* returns true if neighbouring element is "e" */
499 /* treats dirt specially */
500 /* treats lava specially */
501 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
502 const GdDirection dir, GdElement e)
504 GdElement examined = get_dir(cave, x, y, dir);
506 /* if it is a dirt-like, change to dirt, so equality will evaluate to true */
507 if (gd_elements[examined & O_MASK].properties & P_DIRT)
510 if (gd_elements[e & O_MASK].properties & P_DIRT)
513 /* if the element on the map is a lava, it should be like space */
514 if (examined == O_LAVA)
517 return (e == examined);
520 /* returns true if neighbouring element is space */
521 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
522 const GdDirection dir)
524 GdElement e = get_dir(cave, x, y, dir) & O_MASK;
526 return (e == O_SPACE || e == O_LAVA);
529 /* store an element at the given position */
530 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
532 GdElement *e = getp(cave, x, y);
536 play_sound_of_element(cave, O_LAVA, x, y);
544 /* store an element with SCANNED flag turned on */
545 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
547 store(cave, x, y, element | SCANNED);
550 /* store an element to a neighbouring cell */
551 static inline void store_dir(GdCave *cave, const int x, const int y,
552 const GdDirection dir, const GdElement element)
554 store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED);
557 /* store an element to a neighbouring cell */
558 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
559 const GdDirection dir, const GdElement element)
561 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
564 /* move element to direction; then place space at x, y */
565 static inline void move(GdCave *cave, const int x, const int y,
566 const GdDirection dir, const GdElement e)
568 store_dir(cave, x, y, dir, e);
569 store(cave, x, y, O_SPACE);
572 /* increment a cave element; can be used for elements which are one after
573 the other, for example bladder1, bladder2, bladder3... */
574 static inline void next(GdCave *cave, const int x, const int y)
576 (*getp(cave, x, y))++;
579 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
581 if (non_explodable (cave, x, y))
584 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
585 cave->voodoo_touched = TRUE;
587 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
588 /* voodoo turns into a time penalty */
589 store_sc(cave, x, y, O_TIME_PENALTY);
590 else if (get(cave, x, y) == O_NITRO_PACK ||
591 get(cave, x, y) == O_NITRO_PACK_F)
592 /* nitro pack inside an explosion - it is now triggered */
593 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
595 /* for everything else */
596 store_sc(cave, x, y, explode_to);
599 /* a creature explodes to a 3x3 something. */
600 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
604 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
605 cave->ckdelay += 1200;
606 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
608 for (yy = y - 1; yy <= y + 1; yy++)
609 for (xx = x - 1; xx <= x + 1; xx++)
610 cell_explode(cave, xx, yy, explode_to);
613 static void nitro_explode(GdCave *cave, int x, int y)
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_NITRO_PACK_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, O_NITRO_EXPL_1);
625 /* the current cell is explicitly changed into a nitro expl,
626 as cell_explode changes it to a triggered nitro pack */
627 store_sc(cave, x, y, O_NITRO_EXPL_1);
630 /* a voodoo explodes, leaving a 3x3 steel and a time penalty behind. */
631 static void voodoo_explode(GdCave *cave, int x, int y)
635 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
636 cave->ckdelay += 1000;
638 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
639 if (cave->voodoo_any_hurt_kills_player)
640 cave->voodoo_touched = TRUE;
642 /* voodoo explodes to 3x3 steel */
643 for (yy = y - 1; yy <= y + 1; yy++)
644 for (xx = x - 1; xx <= x + 1; xx++)
645 store_sc(cave, xx, yy, O_PRE_STEEL_1);
647 /* middle is a time penalty (which will be turned into a gravestone) */
648 store_sc(cave, x, y, O_TIME_PENALTY);
651 /* a bomb does not explode the voodoo, neither does the ghost.
652 this function check this, and stores the new element or not.
653 destroying the voodoo is also controlled by the
654 voodoo_disappear_in_explosion flag. */
655 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
657 if (non_explodable (cave, x, y))
660 /* bomb does not explode voodoo */
661 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
664 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
665 cave->voodoo_touched = TRUE;
667 store_sc (cave, x, y, expl);
670 /* X shaped ghost explosion; does not touch voodoo! */
671 static void ghost_explode(GdCave *cave, const int x, const int y)
673 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
675 /* the processing of an explosion took pretty much time: processing 5 elements */
676 cave->ckdelay += 650;
678 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
679 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
680 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
681 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
682 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
685 /* +shaped bomb explosion; does not touch voodoo! */
686 static void bomb_explode(GdCave *cave, const int x, const int y)
688 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
690 /* the processing of an explosion took pretty much time: processing 5 elements */
691 cave->ckdelay += 650;
693 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
694 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
695 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
696 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
697 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
701 explode an element with the appropriate type of exlposion.
703 static void explode(GdCave *cave, int x, int y)
705 GdElement e = get(cave, x, y) & O_MASK;
710 ghost_explode(cave, x, y);
714 bomb_explode(cave, x, y);
718 voodoo_explode(cave, x, y);
723 case O_NITRO_PACK_EXPLODE:
724 nitro_explode(cave, x, y);
728 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
731 case O_FALLING_WALL_F:
732 creature_explode(cave, x, y, O_EXPLODE_1);
739 creature_explode(cave, x, y, cave->butterfly_explode_to);
746 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
753 creature_explode(cave, x, y, cave->firefly_explode_to);
756 case O_ALT_FIREFLY_1:
757 case O_ALT_FIREFLY_2:
758 case O_ALT_FIREFLY_3:
759 case O_ALT_FIREFLY_4:
760 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
766 case O_PLAYER_STIRRING:
767 case O_PLAYER_PNEUMATIC_LEFT:
768 case O_PLAYER_PNEUMATIC_RIGHT:
769 creature_explode(cave, x, y, O_EXPLODE_1);
776 creature_explode(cave, x, y, cave->stonefly_explode_to);
783 creature_explode(cave, x, y, cave->dragonfly_explode_to);
791 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
793 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
797 player eats specified object.
798 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
799 returns other element if something other appears there and he can't move.
800 cave pointer is needed to know the diamond values.
802 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
809 cave->diamond_key_collected = TRUE;
810 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
815 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
820 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
825 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
832 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
839 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
846 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
851 case O_CREATURE_SWITCH: /* creatures change direction. */
852 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
853 cave->creatures_backwards = !cave->creatures_backwards;
856 case O_EXPANDING_WALL_SWITCH: /* expanding wall change direction. */
857 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
858 cave->expanding_wall_changed = !cave->expanding_wall_changed;
861 case O_BITER_SWITCH: /* biter change delay */
862 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
863 cave->biter_delay_frame++;
864 if (cave->biter_delay_frame == 4)
865 cave->biter_delay_frame = 0;
868 case O_REPLICATOR_SWITCH: /* replicator on/off switch */
869 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
870 cave->replicators_active = !cave->replicators_active;
873 case O_CONVEYOR_SWITCH: /* conveyor belts on/off */
874 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
875 cave->conveyor_belts_active = !cave->conveyor_belts_active;
878 case O_CONVEYOR_DIR_SWITCH: /* conveyor belts switch direction */
879 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
880 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
886 case O_STEEL_EATABLE:
887 case O_BRICK_EATABLE:
888 case O_DIRT_SLOPED_UP_RIGHT:
889 case O_DIRT_SLOPED_UP_LEFT:
890 case O_DIRT_SLOPED_DOWN_LEFT:
891 case O_DIRT_SLOPED_DOWN_RIGHT:
894 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
898 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
899 cave->sweet_eaten = TRUE;
902 case O_PNEUMATIC_HAMMER:
903 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
904 cave->got_pneumatic_hammer = TRUE;
909 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
910 cave->time += cave->time_bonus * cave->timing_factor;
911 if (cave->time > cave->max_time * cave->timing_factor)
912 cave->time -= cave->max_time * cave->timing_factor;
913 /* no space, rather a dirt remains there... */
917 case O_FLYING_DIAMOND:
918 // prevent diamond sounds for O_SKELETON (see below)
919 if (x != -1 && y != -1)
920 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
921 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
923 cave->score += cave->diamond_value;
924 cave->diamonds_collected++;
926 if (cave->diamonds_needed == cave->diamonds_collected)
928 cave->gate_open = TRUE;
930 /* extra is worth more points. */
931 cave->diamond_value = cave->extra_diamond_value;
933 cave->gate_open_flash = 1;
934 cave->sound3 = GD_S_CRACKING;
935 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
940 cave->skeletons_collected++;
942 /* as if player got a diamond */
943 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
944 player_get_element(cave, O_DIAMOND, -1, -1);
946 /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
947 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
952 cave->player_state = GD_PL_EXITED; /* player now exits the cave! */
956 case O_LAVA: /* player goes into lava, as if it was space */
957 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
961 /* the object will remain there. */
967 process a crazy dream-style teleporter.
968 called from gd_cave_iterate, for a player or a player_bomb.
969 player is standing at px, py, and trying to move in the direction player_move,
970 where there is a teleporter.
971 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
972 around). the first teleporter we find, and which is suitable, will be the destination.
973 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
975 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
984 /* jump to next element; wrap around columns and rows. */
996 /* if we found a teleporter... */
997 if (get(cave, tx, ty) == O_TELEPORTER &&
998 is_space_dir(cave, tx, ty, player_move))
1000 /* new player appears near teleporter found */
1001 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1003 /* current player disappears */
1004 store(cave, px, py, O_SPACE);
1006 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1008 return TRUE; /* return true as teleporter worked */
1011 /* loop until we get back to original coordinates */
1012 while (tx != px || ty != py);
1014 /* return false as we did not find any usable teleporter */
1019 try to push an element.
1020 returns true if the push is possible; also does move the specified _element_.
1021 up to the caller to move the _player_itself_.
1023 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1026 GdElement what = get_dir(cave, x, y, player_move);
1028 /* gravity for falling wall, bladder, ... */
1029 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1035 case O_WAITING_STONE:
1038 case O_CHASING_STONE:
1040 case O_FLYING_STONE:
1042 /* pushing some kind of stone or nut */
1043 /* directions possible: 90degrees cw or ccw to current gravity. */
1044 /* only push if player dir is orthogonal to gravity,
1045 ie. gravity down, pushing left & right possible */
1046 if (player_move == ccw_fourth[cave->gravity] ||
1047 player_move == cw_fourth[cave->gravity])
1053 /* different probabilities for different elements. */
1056 case O_WAITING_STONE:
1057 /* waiting stones are light, can always push */
1061 case O_CHASING_STONE:
1062 /* chasing can be pushed if player is turbo */
1063 if (cave->sweet_eaten)
1068 /* mega may(!) be pushed if player is turbo */
1069 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1075 case O_FLYING_STONE:
1077 if (cave->sweet_eaten)
1078 prob = cave->pushing_stone_prob_sweet; /* probability with sweet */
1080 prob = cave->pushing_stone_prob; /* probability without sweet. */
1087 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1088 g_rand_int_range(cave->random, 0, 1000000) < prob)
1090 /* if decided that he will be able to push, */
1091 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1092 play_sound_of_element_pushing(cave, what, x, y);
1107 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1108 * not an O_BLADDER_x. */
1109 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
1111 /* first check: we cannot push a bladder "up" */
1112 if (player_move != opposite[grav_compat])
1114 /* pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check. */
1115 /* player moving in the direction of gravity. */
1119 if (player_move == grav_compat)
1121 /* pushing bladder down */
1122 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1123 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1124 /* if no space to push down, maybe left (down-left to player) */
1125 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1127 /* left is "down, turned right (cw)" */
1128 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1129 /* if not, maybe right (down-right to player) */
1130 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1131 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1134 /* pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check. */
1138 else if (player_move == cw_fourth[grav_compat])
1140 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) /* pushing it left */
1141 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1142 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) /* maybe down, and player will move left */
1143 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1144 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) /* maybe up, and player will move left */
1145 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1148 /* pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check. */
1152 else if (player_move == ccw_fourth[grav_compat])
1154 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) /* pushing it right */
1155 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1156 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) /* maybe down, and player will move right */
1157 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1158 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) /* maybe up, and player will move right */
1159 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1163 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1168 /* a box is only pushed with the fire pressed */
1171 /* but always with 100% probability */
1172 switch (player_move)
1178 /* pushing in some dir, two steps in that dir - is there space? */
1179 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1182 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1184 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1189 /* push in no other directions possible */
1195 /* pushing of other elements not possible */
1203 /* from the key press booleans, create a direction */
1204 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1206 GdDirection player_move;
1208 /* from the key press booleans, create a direction */
1210 player_move = GD_MV_UP_RIGHT;
1211 else if (down && right)
1212 player_move = GD_MV_DOWN_RIGHT;
1213 else if (down && left)
1214 player_move = GD_MV_DOWN_LEFT;
1215 else if (up && left)
1216 player_move = GD_MV_UP_LEFT;
1218 player_move = GD_MV_UP;
1220 player_move = GD_MV_DOWN;
1222 player_move = GD_MV_LEFT;
1224 player_move = GD_MV_RIGHT;
1226 player_move = GD_MV_STILL;
1231 /* clear these to no sound; and they will be set during iteration. */
1232 void gd_cave_clear_sounds(GdCave *cave)
1234 cave->sound1 = GD_S_NONE;
1235 cave->sound2 = GD_S_NONE;
1236 cave->sound3 = GD_S_NONE;
1239 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1240 GdElement falling_element)
1242 if (cave->gravity_disabled)
1245 if (is_space_dir(cave, x, y, falling_direction))
1247 /* beginning to fall */
1248 play_sound_of_element(cave, get(cave, x, y), x, y);
1249 move(cave, x, y, falling_direction, falling_element);
1252 /* check if it is on a sloped element, and it can roll. */
1253 /* for example, sloped wall looks like: */
1256 /* this is tagged as sloped up&left. */
1257 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1258 /* then check the direction to roll (left or right) */
1259 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1260 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1262 /* rolling down, if sitting on a sloped object */
1263 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1264 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1265 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1267 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1268 so here we use cw_fourth */
1269 play_sound_of_element(cave, get(cave, x, y), x, y);
1270 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1272 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1273 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1274 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1276 /* rolling right? */
1277 play_sound_of_element(cave, get(cave, x, y), x, y);
1278 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1283 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1285 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1286 cave->voodoo_dies_by_stone)
1288 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1289 explode_dir(cave, x, y, fall_dir);
1296 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1298 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1299 cave->voodoo_collects_diamonds)
1301 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1302 player_get_element(cave, O_DIAMOND, x, y); /* as if player got diamond */
1303 store(cave, x, y, O_SPACE); /* diamond disappears */
1310 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1311 GdDirection fall_dir, GdElement bouncing)
1313 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1314 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1317 store(cave, x, y, bouncing);
1318 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1320 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1328 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1329 GdDirection fall_dir, GdElement magic)
1331 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1333 play_sound_of_element(cave, O_DIAMOND, x, y); /* always play diamond sound */
1335 if (cave->magic_wall_state==GD_MW_DORMANT)
1336 cave->magic_wall_state = GD_MW_ACTIVE;
1338 if (cave->magic_wall_state==GD_MW_ACTIVE &&
1339 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1341 /* if magic wall active and place underneath, it turns element
1342 into anything the effect says to do. */
1343 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1346 /* active or non-active or anything, element falling in will always disappear */
1347 store(cave, x, y, O_SPACE);
1355 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1357 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1359 explode_dir(cave, x, y, fall_dir);
1366 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1367 GdDirection fall_dir, GdElement bouncing)
1369 if (is_space_dir(cave, x, y, fall_dir))
1371 /* falling further */
1372 move(cave, x, y, fall_dir, get(cave, x, y));
1377 /* check if it is on a sloped element, and it can roll. */
1378 /* for example, sloped wall looks like: */
1381 /* this is tagged as sloped up&left. */
1382 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1383 /* then check the direction to roll (left or right) */
1384 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1386 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1388 /* sloped element, falling to left or right */
1389 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1390 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1391 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1393 play_sound_of_element(cave, get(cave, x, y), x, y);
1395 /* try to roll left first - see O_STONE to understand why cw_fourth */
1396 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1398 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1399 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1400 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1402 play_sound_of_element(cave, get(cave, x, y), x, y);
1404 /* if not, try to roll right */
1405 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1409 /* cannot roll in any direction, so it stops */
1410 play_sound_of_element(cave, get(cave, x, y), x, y);
1411 store(cave, x, y, bouncing);
1417 /* any other element, stops */
1418 play_sound_of_element(cave, get(cave, x, y), x, y);
1419 store(cave, x, y, bouncing);
1423 static void update_cave_speed(GdCave *cave)
1425 /* update timing calculated by iterating and counting elements which were slow to process on c64 */
1426 switch (cave->scheduling)
1428 case GD_SCHEDULING_MILLISECONDS:
1429 /* cave->speed already contains the milliseconds value, do not touch it */
1432 case GD_SCHEDULING_BD1:
1433 if (!cave->intermission)
1434 /* non-intermissions */
1435 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1437 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
1438 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1441 case GD_SCHEDULING_BD1_ATARI:
1442 /* about 20ms/frame faster than c64 version */
1443 if (!cave->intermission)
1444 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); /* non-intermissions */
1446 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); /* for intermissions */
1449 case GD_SCHEDULING_BD2:
1450 /* 60 is a guess. */
1451 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1454 case GD_SCHEDULING_PLCK:
1455 /* 65 is totally empty cave in construction kit, with delay = 0) */
1456 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1459 case GD_SCHEDULING_BD2_PLCK_ATARI:
1460 /* a really fast engine; timing works like c64 plck. */
1461 /* 40 ms was measured in the construction kit, with delay = 0 */
1462 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1465 case GD_SCHEDULING_CRDR:
1466 if (cave->hammered_walls_reappear) /* this made the engine very slow. */
1467 cave->ckdelay += 60000;
1468 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1471 case GD_SCHEDULING_MAX:
1476 /* process a cave. */
1477 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1481 /* for border scan */
1484 /* amoeba found to be enclosed. if not, this is cleared */
1485 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1487 /* counting the number of amoebas. after scan, check if too much */
1488 int amoeba_count, amoeba_2_count;
1490 /* cave scan found water - for sound */
1491 boolean found_water;
1493 boolean inbox_toggle;
1494 boolean start_signal;
1496 /* gravity for falling wall, bladder, ... */
1497 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1499 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1500 static const GdDirection creature_dir[] =
1507 static const GdDirection creature_chdir[] =
1514 int time_decrement_sec;
1516 /* biters eating elements preference, they try to go in this order */
1517 GdElement biter_try[] =
1524 boolean amoeba_sound, magic_sound;
1526 gd_cave_clear_sounds(cave);
1528 /* if diagonal movements not allowed, */
1529 /* horizontal movements have precedence. [BROADRIBB] */
1530 if (!cave->diagonal_movements)
1532 switch (player_move)
1534 case GD_MV_UP_RIGHT:
1535 case GD_MV_DOWN_RIGHT:
1536 player_move = GD_MV_RIGHT;
1540 case GD_MV_DOWN_LEFT:
1541 player_move = GD_MV_LEFT;
1545 /* no correction needed */
1550 /* set cave get function; to implement perfect or lineshifting borders */
1551 if (cave->lineshift)
1553 cave->getp = getp_shift;
1554 cave->getx = getx_shift;
1555 cave->gety = gety_shift;
1559 cave->getp = getp_perfect;
1560 cave->getx = getx_perfect;
1561 cave->gety = gety_perfect;
1564 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1565 if (cave->player_seen_ago < 100)
1566 cave->player_seen_ago++;
1568 if (cave->pneumatic_hammer_active_delay > 0)
1569 cave->pneumatic_hammer_active_delay--;
1571 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1572 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1573 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1574 inbox_toggle = cave->inbox_flash_toggle;
1576 if (cave->gate_open_flash > 0)
1577 cave->gate_open_flash--;
1579 /* score collected this frame */
1582 /* suicide only kills the active player */
1583 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1584 /* we must check if there is a player or not - he may have exploded or something like that */
1585 if (suicide && cave->player_state == GD_PL_LIVING &&
1586 is_player(cave, cave->player_x, cave->player_y))
1587 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1589 /* check for walls reappearing */
1590 if (cave->hammered_reappear)
1592 for (y = 0; y < cave->h; y++)
1594 for (x = 0; x < cave->w; x++)
1596 /* timer for the cell > 0? */
1597 if (cave->hammered_reappear[y][x]>0)
1599 /* decrease timer */
1600 cave->hammered_reappear[y][x]--;
1602 /* check if it became zero */
1603 if (cave->hammered_reappear[y][x] == 0)
1605 store(cave, x, y, O_BRICK);
1606 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1613 /* variables to check during the scan */
1615 /* will be set to false if any of the amoeba is found free. */
1616 amoeba_found_enclosed = TRUE;
1617 amoeba_2_found_enclosed = TRUE;
1620 found_water = FALSE;
1622 time_decrement_sec = 0;
1624 /* check whether to scan the first and last line */
1625 if (cave->border_scan_first_and_last)
1636 /* the cave scan routine */
1637 for (y = ymin; y <= ymax; y++)
1639 for (x = 0; x < cave->w; x++)
1641 /* if we find a scanned element, change it to the normal one, and that's all. */
1642 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1643 if (get(cave, x, y) & SCANNED)
1645 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1650 /* add the ckdelay correction value for every element seen. */
1651 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1653 switch (get(cave, x, y))
1659 if (cave->kill_player)
1661 explode (cave, x, y);
1665 cave->player_seen_ago = 0;
1666 /* bd4 intermission caves have many players. so if one of them has exited,
1667 * do not change the flag anymore. so this if () is needed */
1668 if (cave->player_state!=GD_PL_EXITED)
1669 cave->player_state = GD_PL_LIVING;
1671 /* check for pneumatic hammer things */
1672 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right
1673 for hammer 5) stand on something */
1674 if (player_fire && cave->got_pneumatic_hammer &&
1675 is_space_dir(cave, x, y, player_move) &&
1676 !is_space_dir(cave, x, y, GD_MV_DOWN))
1678 if (player_move == GD_MV_LEFT &&
1679 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1681 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1682 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1683 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1684 break; /* finished. */
1687 if (player_move == GD_MV_RIGHT &&
1688 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1690 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1691 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1692 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1693 break; /* finished. */
1697 if (player_move != GD_MV_STILL)
1699 /* only do every check if he is not moving */
1700 GdElement what = get_dir(cave, x, y, player_move);
1701 GdElement remains = what;
1704 /* if we are 'eating' a teleporter, and the function returns true
1705 (teleporting worked), break here */
1706 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1709 /* try to push element; if successful, break */
1710 push = do_push(cave, x, y, player_move, player_fire);
1720 /* if its a bomb, remember he now has one. */
1721 /* we do not change the "remains" and "what" variables,
1722 so that part of the code will be ineffective */
1723 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1724 store_dir(cave, x, y, player_move, O_SPACE);
1727 store(cave, x, y, O_PLAYER_BOMB);
1729 move(cave, x, y, player_move, O_PLAYER_BOMB);
1733 /* we do not change the "remains" and "what" variables,
1734 so that part of the code will be ineffective */
1735 if (!player_fire && !cave->gravity_switch_active &&
1736 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1738 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1739 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1740 cave->gravity_disabled = TRUE;
1744 case O_GRAVITY_SWITCH:
1745 /* (we cannot use player_get for this as it does not have player_move parameter) */
1746 /* only allow changing direction if the new dir is not diagonal */
1747 if (cave->gravity_switch_active &&
1748 (player_move == GD_MV_LEFT ||
1749 player_move == GD_MV_RIGHT ||
1750 player_move == GD_MV_UP ||
1751 player_move == GD_MV_DOWN))
1753 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1754 cave->gravity_will_change =
1755 cave->gravity_change_time * cave->timing_factor;
1756 cave->gravity_next_direction = player_move;
1757 cave->gravity_switch_active = FALSE;
1762 /* get element - process others.
1763 if cannot get, player_get_element will return the same */
1764 remains = player_get_element(cave, what, x, y);
1769 if (remains != what || remains == O_SPACE)
1771 /* if anything changed, apply the change. */
1773 /* if snapping anything and we have snapping explosions set.
1774 but these is not true for pushing. */
1775 if (remains == O_SPACE && player_fire && !push)
1776 remains = cave->snap_element;
1778 if (remains != O_SPACE || player_fire)
1779 /* if any other element than space, player cannot move.
1780 also if pressing fire, will not move. */
1781 store_dir(cave, x, y, player_move, remains);
1783 /* if space remains there, the player moves. */
1784 move(cave, x, y, player_move, O_PLAYER);
1790 /* much simpler; cannot steal stones */
1791 if (cave->kill_player)
1793 explode(cave, x, y);
1797 cave->player_seen_ago = 0;
1798 /* bd4 intermission caves have many players. so if one of them has exited,
1799 * do not change the flag anymore. so this if () is needed */
1800 if (cave->player_state != GD_PL_EXITED)
1801 cave->player_state = GD_PL_LIVING;
1803 if (player_move != GD_MV_STILL)
1805 /* if the player does not move, nothing to do */
1806 GdElement what = get_dir(cave, x, y, player_move);
1807 GdElement remains = what;
1811 /* placing a bomb into empty space or dirt */
1812 if (is_space_dir(cave, x, y, player_move) ||
1813 is_element_dir(cave, x, y, player_move, O_DIRT))
1815 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1817 /* placed bomb, he is normal player again */
1818 store(cave, x, y, O_PLAYER);
1819 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1824 /* pushing and collecting */
1825 /* if we are 'eating' a teleporter, and the function returns true
1826 (teleporting worked), break here */
1827 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1830 /* player fire is false... */
1831 if (do_push(cave, x, y, player_move, FALSE))
1839 case O_GRAVITY_SWITCH:
1840 /* (we cannot use player_get for this as it does not have
1841 player_move parameter) */
1842 /* only allow changing direction if the new dir is not diagonal */
1843 if (cave->gravity_switch_active &&
1844 (player_move==GD_MV_LEFT ||
1845 player_move==GD_MV_RIGHT ||
1846 player_move==GD_MV_UP ||
1847 player_move==GD_MV_DOWN))
1849 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1850 cave->gravity_will_change =
1851 cave->gravity_change_time * cave->timing_factor;
1852 cave->gravity_next_direction = player_move;
1853 cave->gravity_switch_active = FALSE;
1858 /* get element. if cannot get, player_get_element will return the same */
1859 remains = player_get_element (cave, what, x, y);
1864 /* if element changed, OR there is space, move. */
1865 if (remains != what || remains == O_SPACE)
1867 /* if anything changed, apply the change. */
1868 move(cave, x, y, player_move, O_PLAYER_BOMB);
1873 case O_PLAYER_STIRRING:
1874 if (cave->kill_player)
1876 explode(cave, x, y);
1880 /* stirring sound, if no other walking sound or explosion */
1881 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1883 cave->player_seen_ago = 0;
1884 /* bd4 intermission caves have many players. so if one of them has exited,
1885 * do not change the flag anymore. so this if () is needed */
1886 if (cave->player_state!=GD_PL_EXITED)
1887 cave->player_state = GD_PL_LIVING;
1891 /* player "exits" stirring the pot by pressing fire */
1892 cave->gravity_disabled = FALSE;
1893 store(cave, x, y, O_PLAYER);
1894 cave->gravity_switch_active = TRUE;
1898 /* player holding pneumatic hammer */
1899 case O_PLAYER_PNEUMATIC_LEFT:
1900 case O_PLAYER_PNEUMATIC_RIGHT:
1901 /* usual player stuff */
1902 if (cave->kill_player)
1904 explode(cave, x, y);
1908 cave->player_seen_ago = 0;
1909 if (cave->player_state!=GD_PL_EXITED)
1910 cave->player_state = GD_PL_LIVING;
1912 /* if hammering time is up, becomes a normal player again. */
1913 if (cave->pneumatic_hammer_active_delay == 0)
1914 store(cave, x, y, O_PLAYER);
1917 /* the active pneumatic hammer itself */
1918 case O_PNEUMATIC_ACTIVE_RIGHT:
1919 case O_PNEUMATIC_ACTIVE_LEFT:
1920 if (cave->pneumatic_hammer_active_delay == 0)
1924 /* pneumatic hammer element disappears */
1925 store(cave, x, y, O_SPACE);
1927 /* which is the new element which appears after that one is hammered? */
1928 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1930 /* if there is a new element, display it */
1931 /* O_NONE might be returned, for example if the element being
1932 hammered explodes during hammering (by a nearby explosion) */
1933 if (new_elem != O_NONE)
1935 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1937 /* and if walls reappear, remember it in array */
1938 if (cave->hammered_walls_reappear)
1942 wall_y = (y + 1) % cave->h;
1943 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1950 * S T O N E S, D I A M O N D S
1952 case O_STONE: /* standing stone */
1953 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1956 case O_MEGA_STONE: /* standing mega_stone */
1957 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1960 case O_DIAMOND: /* standing diamond */
1961 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1964 case O_NUT: /* standing nut */
1965 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1968 case O_DIRT_BALL: /* standing dirt ball */
1969 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1972 case O_DIRT_LOOSE: /* standing loose dirt */
1973 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1976 case O_FLYING_STONE: /* standing stone */
1977 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1980 case O_FLYING_DIAMOND: /* standing diamond */
1981 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1985 * 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
1987 case O_DIRT_BALL_F: /* falling dirt ball */
1988 if (!cave->gravity_disabled)
1989 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
1992 case O_DIRT_LOOSE_F: /* falling loose dirt */
1993 if (!cave->gravity_disabled)
1994 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
1997 case O_STONE_F: /* falling stone */
1998 if (!cave->gravity_disabled)
2000 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2003 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2006 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2009 if (do_fall_try_crush(cave, x, y, cave->gravity))
2012 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2016 case O_MEGA_STONE_F: /* falling mega */
2017 if (!cave->gravity_disabled)
2019 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2022 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2025 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2028 if (do_fall_try_crush(cave, x, y, cave->gravity))
2031 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2035 case O_DIAMOND_F: /* falling diamond */
2036 if (!cave->gravity_disabled)
2038 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2041 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2044 if (do_fall_try_crush(cave, x, y, cave->gravity))
2047 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2051 case O_NUT_F: /* falling nut */
2052 if (!cave->gravity_disabled)
2054 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2057 if (do_fall_try_crush(cave, x, y, cave->gravity))
2060 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2064 case O_FLYING_STONE_F: /* falling stone */
2065 if (!cave->gravity_disabled)
2067 GdDirection fall_dir = opposite[cave->gravity];
2069 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2072 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2075 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2078 if (do_fall_try_crush(cave, x, y, fall_dir))
2081 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2085 case O_FLYING_DIAMOND_F: /* falling diamond */
2086 if (!cave->gravity_disabled)
2088 GdDirection fall_dir = opposite[cave->gravity];
2090 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2093 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2096 if (do_fall_try_crush(cave, x, y, fall_dir))
2099 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2106 case O_NITRO_PACK: /* standing nitro pack */
2107 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2110 case O_NITRO_PACK_F: /* falling nitro pack */
2111 if (!cave->gravity_disabled)
2113 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
2114 move(cave, x, y, cave->gravity, get(cave, x, y));
2115 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2117 /* try magic wall; if true, function did the work */
2119 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2121 /* falling on a dirt, it does NOT explode - just stops at its place. */
2122 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2123 store(cave, x, y, O_NITRO_PACK);
2126 /* falling on any other element it explodes */
2127 explode(cave, x, y);
2131 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
2132 explode(cave, x, y);
2143 /* if cannot move in any direction, becomes an enclosed cow */
2144 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2145 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2146 store(cave, x, y, O_COW_ENCLOSED_1);
2149 /* THIS IS THE CREATURE MOVE thing copied. */
2150 const GdDirection *creature_move;
2151 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2152 GdElement base; /* base element number (which is like O_***_1) */
2153 int dir, dirn, dirp; /* direction */
2157 dir = get(cave, x, y)-base; /* facing where */
2158 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2160 /* now change direction if backwards */
2161 if (cave->creatures_backwards)
2166 dirn = (dir + 3) & 3; /* fast turn */
2167 dirp = (dir + 1) & 3; /* slow turn */
2171 dirn = (dir + 1) & 3; /* fast turn */
2172 dirp = (dir + 3) & 3; /* slow turn */
2175 if (is_space_dir(cave, x, y, creature_move[dirn]))
2176 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2177 else if (is_space_dir(cave, x, y, creature_move[dir]))
2178 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2180 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2184 /* enclosed cows wait some time before turning to a skeleton */
2185 case O_COW_ENCLOSED_1:
2186 case O_COW_ENCLOSED_2:
2187 case O_COW_ENCLOSED_3:
2188 case O_COW_ENCLOSED_4:
2189 case O_COW_ENCLOSED_5:
2190 case O_COW_ENCLOSED_6:
2191 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2192 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2193 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2194 is_space_dir(cave, x, y, GD_MV_DOWN))
2195 store(cave, x, y, O_COW_1);
2200 case O_COW_ENCLOSED_7:
2201 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2202 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2203 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2204 is_space_dir(cave, x, y, GD_MV_DOWN))
2205 store(cave, x, y, O_COW_1);
2207 store(cave, x, y, O_SKELETON);
2214 case O_ALT_FIREFLY_1:
2215 case O_ALT_FIREFLY_2:
2216 case O_ALT_FIREFLY_3:
2217 case O_ALT_FIREFLY_4:
2222 case O_ALT_BUTTER_1:
2223 case O_ALT_BUTTER_2:
2224 case O_ALT_BUTTER_3:
2225 case O_ALT_BUTTER_4:
2230 /* check if touches a voodoo */
2231 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2232 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2233 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2234 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2235 cave->voodoo_touched = TRUE;
2237 /* check if touches something bad and should explode (includes voodoo by the flags) */
2238 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2239 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2240 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2241 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2242 explode (cave, x, y);
2243 /* otherwise move */
2246 const GdDirection *creature_move;
2247 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2248 GdElement base; /* base element number (which is like O_***_1) */
2249 int dir, dirn, dirp; /* direction */
2251 if (get(cave, x, y) >= O_FIREFLY_1 &&
2252 get(cave, x, y) <= O_FIREFLY_4)
2254 else if (get(cave, x, y) >= O_BUTTER_1 &&
2255 get(cave, x, y) <= O_BUTTER_4)
2257 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2258 get(cave, x, y) <= O_STONEFLY_4)
2259 base = O_STONEFLY_1;
2260 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2261 get(cave, x, y) <= O_ALT_FIREFLY_4)
2262 base = O_ALT_FIREFLY_1;
2263 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2264 get(cave, x, y) <= O_ALT_BUTTER_4)
2265 base = O_ALT_BUTTER_1;
2267 dir = get(cave, x, y)-base; /* facing where */
2268 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2270 /* now change direction if backwards */
2271 if (cave->creatures_backwards)
2276 dirn = (dir + 3) & 3; /* fast turn */
2277 dirp = (dir + 1) & 3; /* slow turn */
2281 dirn = (dir + 1) & 3; /* fast turn */
2282 dirp = (dir + 3) & 3; /* slow turn */
2285 if (is_space_dir(cave, x, y, creature_move[dirn]))
2286 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2287 else if (is_space_dir(cave, x, y, creature_move[dir]))
2288 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2290 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2294 case O_WAITING_STONE:
2295 if (is_space_dir(cave, x, y, grav_compat))
2297 /* beginning to fall */
2299 move(cave, x, y, grav_compat, O_CHASING_STONE);
2301 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2303 /* rolling down a brick wall or a stone */
2304 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2305 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2306 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2308 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2309 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2311 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2312 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2313 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2315 /* or maybe right */
2316 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2321 case O_CHASING_STONE:
2323 int px = cave->px[0];
2324 int py = cave->py[0];
2325 boolean horizontal = g_rand_boolean(cave->random);
2326 boolean dont_move = FALSE;
2329 /* try to move... */
2334 /*********************************/
2335 /* check for a horizontal movement */
2338 /* if coordinates are the same */
2340 horizontal = !horizontal;
2347 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2349 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2353 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2355 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2364 horizontal = !horizontal;
2372 /********************************/
2373 /* check for a vertical movement */
2376 /* if coordinates are the same */
2378 horizontal = !horizontal;
2384 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2386 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2390 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2392 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2401 horizontal = !horizontal;
2414 /* if we should move in both directions, but can not move in any, stop. */
2419 /* check for horizontal */
2422 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2423 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2424 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2425 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2426 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2427 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2431 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2432 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2433 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2434 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2435 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2436 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2441 /* check for vertical */
2444 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2445 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2446 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2447 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2448 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2449 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2453 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2454 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2455 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2456 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2457 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2458 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2466 if (cave->replicators_wait_frame == 0 &&
2467 cave->replicators_active &&
2468 !cave->gravity_disabled)
2470 /* only replicate, if space is under it. */
2471 /* do not replicate players! */
2472 /* also obeys gravity settings. */
2473 /* only replicate element if it is not a scanned one */
2474 /* do not replicate space... that condition looks like it
2475 makes no sense, but otherwise it generates SCANNED spaces,
2476 which cannot be "collected" by the player, so he cannot run
2477 under a replicator */
2478 if (is_space_dir(cave, x, y, cave->gravity) &&
2479 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2480 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2482 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2483 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2492 if (cave->biters_wait_frame == 0)
2494 static GdDirection biter_move[] =
2502 /* direction, last two bits 0..3 */
2503 int dir = get(cave, x, y) - O_BITER_1;
2504 int dirn = (dir + 3) & 3;
2505 int dirp = (dir + 1) & 3;
2507 GdElement made_sound_of = O_NONE;
2509 for (i = 0; i < G_N_ELEMENTS (biter_try); i++)
2511 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2513 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2514 if (biter_try[i] != O_SPACE)
2515 made_sound_of = O_BITER_1; /* sound of a biter eating */
2518 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2520 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2521 if (biter_try[i] != O_SPACE)
2522 made_sound_of = O_BITER_1; /* sound of a biter eating */
2525 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2527 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2528 if (biter_try[i] != O_SPACE)
2529 made_sound_of = O_BITER_1; /* sound of a biter eating */
2534 if (i == G_N_ELEMENTS(biter_try))
2535 /* i = number of elements in array: could not move, so just turn */
2536 store(cave, x, y, O_BITER_1 + dirp);
2537 else if (biter_try[i] == O_STONE)
2539 /* if there was a stone there, where we moved...
2540 do not eat stones, just throw them back */
2541 store(cave, x, y, O_STONE);
2542 made_sound_of = O_STONE;
2545 /* if biter did move, we had sound. play it. */
2546 if (made_sound_of != O_NONE)
2547 play_sound_of_element(cave, made_sound_of, x, y);
2555 /* check if touches a voodoo */
2556 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2557 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2558 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2559 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2560 cave->voodoo_touched = TRUE;
2562 /* check if touches something bad and should explode (includes voodoo by the flags) */
2563 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2564 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2565 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2566 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2567 explode (cave, x, y);
2568 /* otherwise move */
2571 const GdDirection *creature_move;
2572 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2573 GdElement base = O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2574 int dir, dirn; /* direction */
2576 dir = get(cave, x, y)-base; /* facing where */
2577 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2579 /* now change direction if backwards */
2580 if (cave->creatures_backwards)
2584 dirn = (dir + 3) & 3; /* fast turn */
2586 dirn = (dir + 1) & 3; /* fast turn */
2588 /* if can move forward, does so. */
2589 if (is_space_dir(cave, x, y, creature_move[dir]))
2590 move(cave, x, y, creature_move[dir], base + dir);
2592 /* otherwise turns 90 degrees in place. */
2593 store(cave, x, y, base + dirn);
2598 store(cave, x, y, O_BLADDER_1);
2609 /* bladder with any delay state: try to convert to clock. */
2610 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2611 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))
2613 /* if touches the specified element, let it be a clock */
2614 store(cave, x, y, O_PRE_CLOCK_1);
2616 /* plays the bladder convert sound */
2617 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2621 /* is space over the bladder? */
2622 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2624 if (get(cave, x, y)==O_BLADDER_8)
2626 /* if it is a bladder 8, really move up */
2627 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2628 play_sound_of_element(cave, O_BLADDER, x, y);
2631 /* if smaller delay, just increase delay. */
2635 /* if not space, is something sloped over the bladder? */
2636 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2637 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2639 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2640 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2641 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2643 /* rolling up, to left */
2644 if (get(cave, x, y) == O_BLADDER_8)
2646 /* if it is a bladder 8, really roll */
2647 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2648 play_sound_of_element(cave, O_BLADDER, x, y);
2651 /* if smaller delay, just increase delay. */
2654 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2655 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2656 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2658 /* rolling up, to left */
2659 if (get(cave, x, y) == O_BLADDER_8)
2661 /* if it is a bladder 8, really roll */
2662 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2663 play_sound_of_element(cave, O_BLADDER, x, y);
2666 /* if smaller delay, just increase delay. */
2671 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2673 store(cave, x, y, O_BLADDER_1);
2678 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2679 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2680 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2681 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2682 explode (cave, x, y);
2687 /* the ghost is given four possibilities to move. */
2688 for (i = 0; i < 4; i++)
2690 static GdDirection dirs[] =
2697 GdDirection random_dir;
2699 random_dir = dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2700 if (is_space_dir(cave, x, y, random_dir))
2702 move(cave, x, y, random_dir, O_GHOST);
2703 break; /* ghost did move -> exit loop */
2710 * A C T I V E E L E M E N T S
2715 switch (cave->amoeba_state)
2718 store(cave, x, y, cave->amoeba_too_big_effect);
2721 case GD_AM_ENCLOSED:
2722 store(cave, x, y, cave->amoeba_enclosed_effect);
2725 case GD_AM_SLEEPING:
2727 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2728 if (amoeba_found_enclosed)
2729 /* if still found enclosed, check all four directions,
2730 if this one is able to grow. */
2731 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2732 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2733 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2734 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2736 /* not enclosed. this is a local (per scan) flag! */
2737 amoeba_found_enclosed = FALSE;
2738 cave->amoeba_state = GD_AM_AWAKE;
2741 /* if alive, check in which dir to grow (or not) */
2742 if (cave->amoeba_state==GD_AM_AWAKE)
2744 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2746 switch (g_rand_int_range(cave->random, 0, 4))
2748 /* decided to grow, choose a random direction. */
2749 case 0: /* let this be up. numbers indifferent. */
2750 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2751 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2755 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2756 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2760 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2761 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2765 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2766 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2778 /* check if it is touching an amoeba, and explosion is enabled */
2779 if (cave->amoeba_2_explodes_by_amoeba &&
2780 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2781 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2782 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2783 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2784 explode (cave, x, y);
2786 switch (cave->amoeba_2_state)
2789 store(cave, x, y, cave->amoeba_2_too_big_effect);
2792 case GD_AM_ENCLOSED:
2793 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2796 case GD_AM_SLEEPING:
2798 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2799 if (amoeba_2_found_enclosed)
2800 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2801 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2802 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2803 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2805 /* not enclosed. this is a local (per scan) flag! */
2806 amoeba_2_found_enclosed = FALSE;
2807 cave->amoeba_2_state = GD_AM_AWAKE;
2810 /* if it is alive, decide if it attempts to grow */
2811 if (cave->amoeba_2_state == GD_AM_AWAKE)
2812 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2814 switch (g_rand_int_range(cave->random, 0, 4))
2816 /* decided to grow, choose a random direction. */
2817 case 0: /* let this be up. numbers indifferent. */
2818 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2819 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2823 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2824 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2828 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2829 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2833 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2834 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2844 /* choose randomly, if it spreads */
2845 if (g_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2847 /* the current one explodes */
2848 store(cave, x, y, cave->acid_turns_to);
2850 /* and if neighbours are eaten, put acid there. */
2851 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2853 play_sound_of_element(cave, O_ACID, x, y);
2854 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2857 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2859 play_sound_of_element(cave, O_ACID, x, y);
2860 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2863 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2865 play_sound_of_element(cave, O_ACID, x, y);
2866 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2869 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2871 play_sound_of_element(cave, O_ACID, x, y);
2872 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2879 if (!cave->water_does_not_flow_down &&
2880 is_space_dir(cave, x, y, GD_MV_DOWN))
2881 /* emulating the odd behaviour in crdr */
2882 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2884 if (is_space_dir(cave, x, y, GD_MV_UP))
2885 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2887 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2888 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2890 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2891 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2895 store(cave, x, y, O_WATER);
2898 case O_H_EXPANDING_WALL:
2899 case O_V_EXPANDING_WALL:
2900 case O_H_EXPANDING_STEEL_WALL:
2901 case O_V_EXPANDING_STEEL_WALL:
2902 /* checks first if direction is changed. */
2903 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2904 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2905 !cave->expanding_wall_changed) ||
2906 ((get(cave, x, y)==O_V_EXPANDING_WALL ||
2907 get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) &&
2908 cave->expanding_wall_changed))
2910 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2912 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2913 play_sound_of_element(cave, get(cave, x, y), x, y);
2916 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2917 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2918 play_sound_of_element(cave, get(cave, x, y), x, y);
2923 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2924 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2925 play_sound_of_element(cave, get(cave, x, y), x, y);
2928 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2929 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2930 play_sound_of_element(cave, get(cave, x, y), x, y);
2935 case O_EXPANDING_WALL:
2936 case O_EXPANDING_STEEL_WALL:
2937 /* the wall which grows in all four directions. */
2938 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2940 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2941 play_sound_of_element(cave, get(cave, x, y), x, y);
2944 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2945 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2946 play_sound_of_element(cave, get(cave, x, y), x, y);
2949 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2950 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2951 play_sound_of_element(cave, get(cave, x, y), x, y);
2954 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2955 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2956 play_sound_of_element(cave, get(cave, x, y), x, y);
2962 ; // to make compilers happy ...
2964 g_print("Step[%03d]", cave->frame); /* XXX */
2966 int rrr = gd_cave_c64_random(cave);
2969 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2970 (rrr & cave->slime_permeability_c64) == 0);
2973 * unpredictable: g_rand_int
2974 * predictable: c64 predictable random generator.
2975 * for predictable, a random number is generated,
2976 * whether or not it is even possible that the stone will be able to pass.
2978 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : g_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2980 GdDirection grav = cave->gravity;
2981 GdDirection oppos = opposite[cave->gravity];
2983 /* space under the slime? elements may pass from top to bottom then. */
2984 if (is_space_dir(cave, x, y, grav))
2986 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
2988 /* output a falling xy under */
2989 store_dir(cave, x, y, grav, cave->slime_converts_1);
2991 store_dir(cave, x, y, oppos, O_SPACE);
2992 play_sound_of_element(cave, O_SLIME, x, y);
2994 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
2996 store_dir(cave, x, y, grav, cave->slime_converts_2);
2997 store_dir(cave, x, y, oppos, O_SPACE);
2998 play_sound_of_element(cave, O_SLIME, x, y);
3000 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3002 store_dir(cave, x, y, grav, cave->slime_converts_3);
3003 store_dir(cave, x, y, oppos, O_SPACE);
3004 play_sound_of_element(cave, O_SLIME, x, y);
3006 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3008 /* waiting stones pass without awakening */
3009 store_dir(cave, x, y, grav, O_WAITING_STONE);
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) == O_CHASING_STONE)
3015 /* chasing stones pass */
3016 store_dir(cave, x, y, grav, O_CHASING_STONE);
3017 store_dir(cave, x, y, oppos, O_SPACE);
3018 play_sound_of_element(cave, O_SLIME, x, y);
3022 /* or space over the slime? elements may pass from bottom to up then. */
3023 if (is_space_dir(cave, x, y, oppos))
3025 if (get_dir(cave, x, y, grav) == O_BLADDER)
3027 /* bladders move UP the slime */
3028 store_dir(cave, x, y, grav, O_SPACE);
3029 store_dir(cave, x, y, oppos, O_BLADDER_1);
3030 play_sound_of_element(cave, O_SLIME, x, y);
3032 else if (get_dir(cave, x, y, grav)==O_FLYING_STONE)
3034 store_dir(cave, x, y, grav, O_SPACE);
3035 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3036 play_sound_of_element(cave, O_SLIME, x, y);
3038 else if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND)
3040 store_dir(cave, x, y, grav, O_SPACE);
3041 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3042 play_sound_of_element(cave, O_SLIME, x, y);
3048 case O_FALLING_WALL:
3049 if (is_space_dir(cave, x, y, grav_compat))
3051 /* try falling if space under. */
3054 for (yy = y + 1; yy < y + cave->h; yy++)
3055 /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
3056 if (get(cave, x, yy) != O_SPACE)
3057 /* stop cycle when other than space */
3060 /* if scanning stopped by a player... start falling! */
3061 if (get(cave, x, yy) == O_PLAYER ||
3062 get(cave, x, yy) == O_PLAYER_GLUED ||
3063 get(cave, x, yy) == O_PLAYER_BOMB)
3065 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3066 /* no sound when the falling wall starts falling! */
3071 case O_FALLING_WALL_F:
3072 switch (get_dir(cave, x, y, grav_compat))
3075 case O_PLAYER_GLUED:
3077 /* if player under, it explodes - the falling wall, not the player! */
3078 explode(cave, x, y);
3082 /* continue falling */
3083 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3088 play_sound_of_element(cave, get(cave, x, y), x, y);
3089 store(cave, x, y, O_FALLING_WALL);
3095 * C O N V E Y O R B E L T S
3098 case O_CONVEYOR_RIGHT:
3099 case O_CONVEYOR_LEFT:
3100 /* only works if gravity is up or down!!! */
3101 /* first, check for gravity and running belts. */
3102 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3104 const GdDirection *dir;
3107 /* decide direction */
3108 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3109 if (cave->conveyor_belts_direction_changed)
3111 dir = left ? ccw_eighth : cw_eighth;
3113 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
3114 /* if gravity is normal, and the conveyor belt has something
3115 ABOVE which can be moved
3117 the gravity is up, so anything that should float now goes
3118 DOWN and touches the conveyor */
3119 if ((cave->gravity == GD_MV_DOWN &&
3120 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3121 (cave->gravity == GD_MV_UP &&
3122 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3124 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3125 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3127 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); /* move */
3128 store_dir(cave, x, y, GD_MV_UP, O_SPACE); /* and place a space. */
3132 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3133 if ((cave->gravity == GD_MV_UP &&
3134 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3135 (cave->gravity == GD_MV_DOWN &&
3136 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3138 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3139 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3141 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); /* move */
3142 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); /* and clear. */
3149 * S I M P L E C H A N G I N G; E X P L O S I O N S
3153 store(cave, x, y, cave->explosion_effect);
3157 store(cave, x, y, O_DIAMOND);
3161 store(cave, x, y, cave->diamond_birth_effect);
3165 store(cave, x, y, O_STONE);
3168 case O_NITRO_EXPL_4:
3169 store(cave, x, y, cave->nitro_explosion_effect);
3173 store(cave, x, y, cave->bomb_explosion_effect);
3176 case O_AMOEBA_2_EXPL_4:
3177 store(cave, x, y, cave->amoeba_2_explosion_effect);
3180 case O_GHOST_EXPL_4:
3182 static GdElement ghost_explode[] =
3184 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3185 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3186 O_WAITING_STONE, O_BITER_1
3189 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
3194 store(cave, x, y, O_STEEL);
3198 store(cave, x, y, O_CLOCK);
3202 explode(cave, x, y);
3205 case O_TRAPPED_DIAMOND:
3206 if (cave->diamond_key_collected)
3207 store(cave, x, y, O_DIAMOND);
3211 if (cave->gate_open) /* if no more diamonds needed */
3212 store(cave, x, y, O_OUTBOX); /* open outbox */
3215 case O_PRE_INVIS_OUTBOX:
3216 if (cave->gate_open) /* if no more diamonds needed */
3217 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
3221 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
3222 store(cave, x, y, O_PRE_PL_1);
3223 inbox_toggle = !inbox_toggle;
3227 store(cave, x, y, O_PLAYER);
3252 case O_GHOST_EXPL_1:
3253 case O_GHOST_EXPL_2:
3254 case O_GHOST_EXPL_3:
3264 case O_NITRO_EXPL_1:
3265 case O_NITRO_EXPL_2:
3266 case O_NITRO_EXPL_3:
3267 case O_AMOEBA_2_EXPL_1:
3268 case O_AMOEBA_2_EXPL_2:
3269 case O_AMOEBA_2_EXPL_3:
3270 /* simply the next identifier */
3289 found_water = TRUE; /* for sound */
3290 /* simply the next identifier */
3294 case O_BLADDER_SPENDER:
3295 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3297 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3298 store(cave, x, y, O_PRE_STEEL_1);
3299 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3304 /* other inanimate elements that do nothing */
3310 /* POSTPROCESSING */
3312 /* another scan-like routine: */
3313 /* short explosions (for example, in bd1) started with explode_2. */
3314 /* internally we use explode_1; and change it to explode_2 if needed. */
3315 if (cave->short_explosions)
3317 for (y = 0; y < cave->h; y++)
3319 for (x = 0; x < cave->w; x++)
3321 if (is_first_stage_of_explosion(cave, x, y))
3323 next(cave, x, y); /* select next frame of explosion */
3325 /* forget scanned flag immediately */
3326 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3332 /* finally: forget "scanned" flags for objects. */
3333 /* also, check for time penalties. */
3334 /* these is something like an effect table, but we do not really use one. */
3335 for (y = 0; y < cave->h; y++)
3337 for (x = 0; x < cave->w; x++)
3339 if (get(cave, x, y) & SCANNED)
3340 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3342 if (get(cave, x, y) == O_TIME_PENALTY)
3344 store(cave, x, y, O_GRAVESTONE);
3346 /* there is time penalty for destroying the voodoo */
3347 time_decrement_sec += cave->time_penalty;
3352 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3353 /* but we only do this, if a living player was found. if not yet, the setup
3354 routine coordinates are used */
3355 if (cave->player_state==GD_PL_LIVING)
3357 if (cave->active_is_first_found)
3359 /* to be 1stb compatible, we do everything backwards. */
3360 for (y = cave->h - 1; y >= 0; y--)
3362 for (x = cave->w - 1; x >= 0; x--)
3364 if (is_player(cave, x, y))
3366 /* here we remember the coordinates. */
3375 /* as in the original: look for the last one */
3376 for (y = 0; y < cave->h; y++)
3378 for (x = 0; x < cave->w; x++)
3380 if (is_player(cave, x, y))
3382 /* here we remember the coordinates. */
3391 /* record coordinates of player for chasing stone */
3392 for (i = 0; i < G_N_ELEMENTS(cave->px) - 1; i++)
3394 cave->px[i] = cave->px[i + 1];
3395 cave->py[i] = cave->py[i + 1];
3398 cave->px[G_N_ELEMENTS(cave->px) - 1] = cave->player_x;
3399 cave->py[G_N_ELEMENTS(cave->py) - 1] = cave->player_y;
3403 /* update timing calculated by iterating and counting elements */
3404 update_cave_speed(cave);
3406 /* cave 3 sounds. precedence is controlled by the sound_play function. */
3407 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3409 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3411 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3412 cave->magic_wall_sound);
3414 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3415 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3416 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3418 if (amoeba_sound && magic_sound)
3420 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3425 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3426 else if (magic_sound)
3427 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3432 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3433 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3434 play_sound_of_element(cave, O_AMOEBA, x, y);
3437 /* pneumatic hammer sound - overrides everything. */
3438 if (cave->pneumatic_hammer_active_delay > 0)
3439 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3441 /* CAVE VARIABLES */
3445 /* check if player is alive. */
3446 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3447 cave->player_state = GD_PL_DIED;
3449 /* check if any voodoo exploded, and kill players the next scan if that happended. */
3450 if (cave->voodoo_touched)
3451 cave->kill_player = TRUE;
3455 /* check flags after evaluating. */
3456 if (cave->amoeba_state == GD_AM_AWAKE)
3458 if (amoeba_count >= cave->amoeba_max_count)
3459 cave->amoeba_state = GD_AM_TOO_BIG;
3460 if (amoeba_found_enclosed)
3461 cave->amoeba_state = GD_AM_ENCLOSED;
3464 /* amoeba can also be turned into diamond by magic wall */
3465 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3466 cave->amoeba_state = GD_AM_ENCLOSED;
3469 if (cave->amoeba_2_state == GD_AM_AWAKE)
3471 /* check flags after evaluating. */
3472 if (amoeba_2_count >= cave->amoeba_2_max_count)
3473 cave->amoeba_2_state = GD_AM_TOO_BIG;
3475 if (amoeba_2_found_enclosed)
3476 cave->amoeba_2_state = GD_AM_ENCLOSED;
3479 /* amoeba 2 can also be turned into diamond by magic wall */
3480 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3481 cave->amoeba_2_state = GD_AM_ENCLOSED;
3483 /* now check times. --------------------------- */
3484 /* decrement time if a voodoo was killed. */
3485 cave->time -= time_decrement_sec * cave->timing_factor;
3489 /* only decrement time when player is already born. */
3492 int secondsbefore, secondsafter;
3494 secondsbefore = cave->time / cave->timing_factor;
3495 cave->time -= cave->speed;
3496 if (cave->time <= 0)
3499 secondsafter = cave->time / cave->timing_factor;
3500 if (cave->time / cave->timing_factor < 10)
3501 /* if less than 10 seconds, no walking sound, but play explosion sound */
3502 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3504 if (secondsbefore != secondsafter)
3505 gd_cave_set_seconds_sound(cave);
3508 /* a gravity switch was activated; seconds counting down */
3509 if (cave->gravity_will_change > 0)
3511 cave->gravity_will_change -= cave->speed;
3512 if (cave->gravity_will_change < 0)
3513 cave->gravity_will_change = 0;
3515 if (cave->gravity_will_change == 0)
3517 cave->gravity = cave->gravity_next_direction;
3518 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); /* takes precedence over amoeba and magic wall sound */
3522 /* creatures direction automatically change */
3523 if (cave->creatures_direction_will_change > 0)
3525 cave->creatures_direction_will_change -= cave->speed;
3526 if (cave->creatures_direction_will_change < 0)
3527 cave->creatures_direction_will_change = 0;
3529 if (cave->creatures_direction_will_change == 0)
3531 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3533 cave->creatures_backwards = !cave->creatures_backwards;
3534 cave->creatures_direction_will_change =
3535 cave->creatures_direction_auto_change_time * cave->timing_factor;
3539 /* magic wall; if active&wait or not wait for hatching */
3540 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3541 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3543 cave->magic_wall_time -= cave->speed;
3544 if (cave->magic_wall_time < 0)
3545 cave->magic_wall_time = 0;
3546 if (cave->magic_wall_time == 0)
3547 cave->magic_wall_state = GD_MW_EXPIRED;
3550 /* we may wait for hatching, when starting amoeba */
3551 if (cave->amoeba_timer_started_immediately ||
3552 (cave->amoeba_state == GD_AM_AWAKE &&
3553 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3555 cave->amoeba_time -= cave->speed;
3556 if (cave->amoeba_time < 0)
3557 cave->amoeba_time = 0;
3558 if (cave->amoeba_time == 0)
3559 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3562 /* we may wait for hatching, when starting amoeba */
3563 if (cave->amoeba_timer_started_immediately ||
3564 (cave->amoeba_2_state == GD_AM_AWAKE &&
3565 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3567 cave->amoeba_2_time -= cave->speed;
3568 if (cave->amoeba_2_time < 0)
3569 cave->amoeba_2_time = 0;
3570 if (cave->amoeba_2_time == 0)
3571 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3574 /* check for player hatching. */
3575 start_signal = FALSE;
3577 /* if not the c64 scheduling, but the correct frametime is used,
3578 hatching delay should always be decremented. */
3579 /* otherwise, the if (millisecs...) condition below will set this. */
3580 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3582 /* NON-C64 scheduling */
3583 if (cave->hatching_delay_frame > 0)
3585 /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3586 cave->hatching_delay_frame--;
3587 if (cave->hatching_delay_frame == 0)
3588 start_signal = TRUE;
3593 /* C64 scheduling */
3594 if (cave->hatching_delay_time > 0)
3596 /* for c64 schedulings, hatching delay means milliseconds. */
3597 cave->hatching_delay_time -= cave->speed;
3598 if (cave->hatching_delay_time <= 0)
3600 cave->hatching_delay_time = 0;
3601 start_signal = TRUE;
3606 /* if decremented hatching, and it became zero: */
3609 /* THIS IS THE CAVE START SIGNAL */
3611 /* record that now the cave is in its normal state */
3612 cave->hatched = TRUE;
3614 /* if diamonds needed is below zero, we count the available diamonds now. */
3615 gd_cave_count_diamonds(cave);
3617 /* setup direction auto change */
3618 if (cave->creatures_direction_auto_change_time)
3620 cave->creatures_direction_will_change =
3621 cave->creatures_direction_auto_change_time * cave->timing_factor;
3623 if (cave->creatures_direction_auto_change_on_start)
3624 cave->creatures_backwards = !cave->creatures_backwards;
3627 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3631 if (cave->biters_wait_frame == 0)
3632 cave->biters_wait_frame = cave->biter_delay_frame;
3634 cave->biters_wait_frame--;
3636 /* replicators delay */
3637 if (cave->replicators_wait_frame == 0)
3638 cave->replicators_wait_frame = cave->replicator_delay_frame;
3640 cave->replicators_wait_frame--;
3645 /* check if cave failed by timeout is done in main game engine */
3647 /* check if cave failed by timeout */
3648 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3650 gd_cave_clear_sounds(cave);
3651 cave->player_state = GD_PL_TIMEOUT;
3652 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3656 /* set these for drawing. */
3657 cave->last_direction = player_move;
3658 /* here we remember last movements for animation. this is needed here,
3659 as animation is in sync with the game, not the keyboard directly.
3660 (for example, after exiting the cave, the player was "running" in the
3661 original, till bonus points were counted for remaining time and so on. */
3662 if (player_move == GD_MV_LEFT ||
3663 player_move == GD_MV_UP_LEFT ||
3664 player_move == GD_MV_DOWN_LEFT)
3665 cave->last_horizontal_direction = GD_MV_LEFT;
3667 if (player_move == GD_MV_RIGHT ||
3668 player_move == GD_MV_UP_RIGHT ||
3669 player_move == GD_MV_DOWN_RIGHT)
3670 cave->last_horizontal_direction = GD_MV_RIGHT;
3672 cave->frame++; /* XXX */
3675 void set_initial_cave_speed(GdCave *cave)
3680 /* set cave get function; to implement perfect or lineshifting borders */
3681 if (cave->lineshift)
3682 cave->getp = getp_shift;
3684 cave->getp = getp_perfect;
3686 /* check whether to scan the first and last line */
3687 if (cave->border_scan_first_and_last)
3698 for (y = ymin; y <= ymax; y++)
3700 for (x = 0; x < cave->w; x++)
3702 /* add the ckdelay correction value for every element seen. */
3703 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3707 /* update timing calculated by iterating and counting elements */
3708 update_cave_speed(cave);