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);
1717 /* if its a bomb, remember he now has one. */
1718 /* we do not change the "remains" and "what" variables,
1719 so that part of the code will be ineffective */
1720 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1721 store_dir(cave, x, y, player_move, O_SPACE);
1724 store(cave, x, y, O_PLAYER_BOMB);
1726 move(cave, x, y, player_move, O_PLAYER_BOMB);
1730 /* we do not change the "remains" and "what" variables,
1731 so that part of the code will be ineffective */
1732 if (!player_fire && !cave->gravity_switch_active &&
1733 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1735 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1736 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1737 cave->gravity_disabled = TRUE;
1741 case O_GRAVITY_SWITCH:
1742 /* (we cannot use player_get for this as it does not have player_move parameter) */
1743 /* only allow changing direction if the new dir is not diagonal */
1744 if (cave->gravity_switch_active &&
1745 (player_move == GD_MV_LEFT ||
1746 player_move == GD_MV_RIGHT ||
1747 player_move == GD_MV_UP ||
1748 player_move == GD_MV_DOWN))
1750 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1751 cave->gravity_will_change =
1752 cave->gravity_change_time * cave->timing_factor;
1753 cave->gravity_next_direction = player_move;
1754 cave->gravity_switch_active = FALSE;
1759 /* get element - process others.
1760 if cannot get, player_get_element will return the same */
1761 remains = player_get_element (cave, what, x, y);
1765 if (remains != what || remains == O_SPACE)
1767 /* if anything changed, apply the change. */
1769 /* if snapping anything and we have snapping explosions set.
1770 but these is not true for pushing. */
1771 if (remains == O_SPACE && player_fire && !push)
1772 remains = cave->snap_element;
1774 if (remains != O_SPACE || player_fire)
1775 /* if any other element than space, player cannot move.
1776 also if pressing fire, will not move. */
1777 store_dir(cave, x, y, player_move, remains);
1779 /* if space remains there, the player moves. */
1780 move(cave, x, y, player_move, O_PLAYER);
1786 /* much simpler; cannot steal stones */
1787 if (cave->kill_player)
1789 explode(cave, x, y);
1793 cave->player_seen_ago = 0;
1794 /* bd4 intermission caves have many players. so if one of them has exited,
1795 * do not change the flag anymore. so this if () is needed */
1796 if (cave->player_state != GD_PL_EXITED)
1797 cave->player_state = GD_PL_LIVING;
1799 if (player_move != GD_MV_STILL)
1801 /* if the player does not move, nothing to do */
1802 GdElement what = get_dir(cave, x, y, player_move);
1803 GdElement remains = what;
1807 /* placing a bomb into empty space or dirt */
1808 if (is_space_dir(cave, x, y, player_move) ||
1809 is_element_dir(cave, x, y, player_move, O_DIRT))
1811 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1813 /* placed bomb, he is normal player again */
1814 store(cave, x, y, O_PLAYER);
1815 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1820 /* pushing and collecting */
1821 /* if we are 'eating' a teleporter, and the function returns true
1822 (teleporting worked), break here */
1823 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1826 /* player fire is false... */
1827 if (do_push(cave, x, y, player_move, FALSE))
1833 case O_GRAVITY_SWITCH:
1834 /* (we cannot use player_get for this as it does not have
1835 player_move parameter) */
1836 /* only allow changing direction if the new dir is not diagonal */
1837 if (cave->gravity_switch_active &&
1838 (player_move==GD_MV_LEFT ||
1839 player_move==GD_MV_RIGHT ||
1840 player_move==GD_MV_UP ||
1841 player_move==GD_MV_DOWN))
1843 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1844 cave->gravity_will_change =
1845 cave->gravity_change_time * cave->timing_factor;
1846 cave->gravity_next_direction = player_move;
1847 cave->gravity_switch_active = FALSE;
1852 /* get element. if cannot get, player_get_element will return the same */
1853 remains = player_get_element (cave, what, x, y);
1858 /* if element changed, OR there is space, move. */
1859 if (remains != what || remains == O_SPACE)
1861 /* if anything changed, apply the change. */
1862 move(cave, x, y, player_move, O_PLAYER_BOMB);
1867 case O_PLAYER_STIRRING:
1868 if (cave->kill_player)
1870 explode(cave, x, y);
1874 /* stirring sound, if no other walking sound or explosion */
1875 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1877 cave->player_seen_ago = 0;
1878 /* bd4 intermission caves have many players. so if one of them has exited,
1879 * do not change the flag anymore. so this if () is needed */
1880 if (cave->player_state!=GD_PL_EXITED)
1881 cave->player_state = GD_PL_LIVING;
1885 /* player "exits" stirring the pot by pressing fire */
1886 cave->gravity_disabled = FALSE;
1887 store(cave, x, y, O_PLAYER);
1888 cave->gravity_switch_active = TRUE;
1892 /* player holding pneumatic hammer */
1893 case O_PLAYER_PNEUMATIC_LEFT:
1894 case O_PLAYER_PNEUMATIC_RIGHT:
1895 /* usual player stuff */
1896 if (cave->kill_player)
1898 explode(cave, x, y);
1902 cave->player_seen_ago = 0;
1903 if (cave->player_state!=GD_PL_EXITED)
1904 cave->player_state = GD_PL_LIVING;
1906 /* if hammering time is up, becomes a normal player again. */
1907 if (cave->pneumatic_hammer_active_delay == 0)
1908 store(cave, x, y, O_PLAYER);
1911 /* the active pneumatic hammer itself */
1912 case O_PNEUMATIC_ACTIVE_RIGHT:
1913 case O_PNEUMATIC_ACTIVE_LEFT:
1914 if (cave->pneumatic_hammer_active_delay == 0)
1918 /* pneumatic hammer element disappears */
1919 store(cave, x, y, O_SPACE);
1921 /* which is the new element which appears after that one is hammered? */
1922 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1924 /* if there is a new element, display it */
1925 /* O_NONE might be returned, for example if the element being
1926 hammered explodes during hammering (by a nearby explosion) */
1927 if (new_elem != O_NONE)
1929 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1931 /* and if walls reappear, remember it in array */
1932 if (cave->hammered_walls_reappear)
1936 wall_y = (y + 1) % cave->h;
1937 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1944 * S T O N E S, D I A M O N D S
1946 case O_STONE: /* standing stone */
1947 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1950 case O_MEGA_STONE: /* standing mega_stone */
1951 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1954 case O_DIAMOND: /* standing diamond */
1955 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1958 case O_NUT: /* standing nut */
1959 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1962 case O_DIRT_BALL: /* standing dirt ball */
1963 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1966 case O_DIRT_LOOSE: /* standing loose dirt */
1967 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1970 case O_FLYING_STONE: /* standing stone */
1971 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1974 case O_FLYING_DIAMOND: /* standing diamond */
1975 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1979 * 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
1981 case O_DIRT_BALL_F: /* falling dirt ball */
1982 if (!cave->gravity_disabled)
1983 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
1986 case O_DIRT_LOOSE_F: /* falling loose dirt */
1987 if (!cave->gravity_disabled)
1988 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
1991 case O_STONE_F: /* falling stone */
1992 if (!cave->gravity_disabled)
1994 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1997 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2000 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2003 if (do_fall_try_crush(cave, x, y, cave->gravity))
2006 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2010 case O_MEGA_STONE_F: /* falling mega */
2011 if (!cave->gravity_disabled)
2013 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2016 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2019 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2022 if (do_fall_try_crush(cave, x, y, cave->gravity))
2025 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2029 case O_DIAMOND_F: /* falling diamond */
2030 if (!cave->gravity_disabled)
2032 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2035 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2038 if (do_fall_try_crush(cave, x, y, cave->gravity))
2041 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2045 case O_NUT_F: /* falling nut */
2046 if (!cave->gravity_disabled)
2048 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2051 if (do_fall_try_crush(cave, x, y, cave->gravity))
2054 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2058 case O_FLYING_STONE_F: /* falling stone */
2059 if (!cave->gravity_disabled)
2061 GdDirection fall_dir = opposite[cave->gravity];
2063 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2066 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2069 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2072 if (do_fall_try_crush(cave, x, y, fall_dir))
2075 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2079 case O_FLYING_DIAMOND_F: /* falling diamond */
2080 if (!cave->gravity_disabled)
2082 GdDirection fall_dir = opposite[cave->gravity];
2084 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2087 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2090 if (do_fall_try_crush(cave, x, y, fall_dir))
2093 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2100 case O_NITRO_PACK: /* standing nitro pack */
2101 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2104 case O_NITRO_PACK_F: /* falling nitro pack */
2105 if (!cave->gravity_disabled)
2107 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
2108 move(cave, x, y, cave->gravity, get(cave, x, y));
2109 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2111 /* try magic wall; if true, function did the work */
2113 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2115 /* falling on a dirt, it does NOT explode - just stops at its place. */
2116 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2117 store(cave, x, y, O_NITRO_PACK);
2120 /* falling on any other element it explodes */
2121 explode(cave, x, y);
2125 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
2126 explode(cave, x, y);
2137 /* if cannot move in any direction, becomes an enclosed cow */
2138 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2139 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2140 store(cave, x, y, O_COW_ENCLOSED_1);
2143 /* THIS IS THE CREATURE MOVE thing copied. */
2144 const GdDirection *creature_move;
2145 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2146 GdElement base; /* base element number (which is like O_***_1) */
2147 int dir, dirn, dirp; /* direction */
2151 dir = get(cave, x, y)-base; /* facing where */
2152 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2154 /* now change direction if backwards */
2155 if (cave->creatures_backwards)
2160 dirn = (dir + 3) & 3; /* fast turn */
2161 dirp = (dir + 1) & 3; /* slow turn */
2165 dirn = (dir + 1) & 3; /* fast turn */
2166 dirp = (dir + 3) & 3; /* slow turn */
2169 if (is_space_dir(cave, x, y, creature_move[dirn]))
2170 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2171 else if (is_space_dir(cave, x, y, creature_move[dir]))
2172 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2174 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2178 /* enclosed cows wait some time before turning to a skeleton */
2179 case O_COW_ENCLOSED_1:
2180 case O_COW_ENCLOSED_2:
2181 case O_COW_ENCLOSED_3:
2182 case O_COW_ENCLOSED_4:
2183 case O_COW_ENCLOSED_5:
2184 case O_COW_ENCLOSED_6:
2185 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2186 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2187 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2188 is_space_dir(cave, x, y, GD_MV_DOWN))
2189 store(cave, x, y, O_COW_1);
2194 case O_COW_ENCLOSED_7:
2195 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2196 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2197 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2198 is_space_dir(cave, x, y, GD_MV_DOWN))
2199 store(cave, x, y, O_COW_1);
2201 store(cave, x, y, O_SKELETON);
2208 case O_ALT_FIREFLY_1:
2209 case O_ALT_FIREFLY_2:
2210 case O_ALT_FIREFLY_3:
2211 case O_ALT_FIREFLY_4:
2216 case O_ALT_BUTTER_1:
2217 case O_ALT_BUTTER_2:
2218 case O_ALT_BUTTER_3:
2219 case O_ALT_BUTTER_4:
2224 /* check if touches a voodoo */
2225 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2226 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2227 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2228 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2229 cave->voodoo_touched = TRUE;
2231 /* check if touches something bad and should explode (includes voodoo by the flags) */
2232 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2233 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2234 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2235 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2236 explode (cave, x, y);
2237 /* otherwise move */
2240 const GdDirection *creature_move;
2241 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2242 GdElement base; /* base element number (which is like O_***_1) */
2243 int dir, dirn, dirp; /* direction */
2245 if (get(cave, x, y) >= O_FIREFLY_1 &&
2246 get(cave, x, y) <= O_FIREFLY_4)
2248 else if (get(cave, x, y) >= O_BUTTER_1 &&
2249 get(cave, x, y) <= O_BUTTER_4)
2251 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2252 get(cave, x, y) <= O_STONEFLY_4)
2253 base = O_STONEFLY_1;
2254 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2255 get(cave, x, y) <= O_ALT_FIREFLY_4)
2256 base = O_ALT_FIREFLY_1;
2257 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2258 get(cave, x, y) <= O_ALT_BUTTER_4)
2259 base = O_ALT_BUTTER_1;
2261 dir = get(cave, x, y)-base; /* facing where */
2262 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2264 /* now change direction if backwards */
2265 if (cave->creatures_backwards)
2270 dirn = (dir + 3) & 3; /* fast turn */
2271 dirp = (dir + 1) & 3; /* slow turn */
2275 dirn = (dir + 1) & 3; /* fast turn */
2276 dirp = (dir + 3) & 3; /* slow turn */
2279 if (is_space_dir(cave, x, y, creature_move[dirn]))
2280 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2281 else if (is_space_dir(cave, x, y, creature_move[dir]))
2282 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2284 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2288 case O_WAITING_STONE:
2289 if (is_space_dir(cave, x, y, grav_compat))
2291 /* beginning to fall */
2293 move(cave, x, y, grav_compat, O_CHASING_STONE);
2295 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2297 /* rolling down a brick wall or a stone */
2298 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2299 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2300 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2302 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2303 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2305 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2306 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2307 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2309 /* or maybe right */
2310 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2315 case O_CHASING_STONE:
2317 int px = cave->px[0];
2318 int py = cave->py[0];
2319 boolean horizontal = g_rand_boolean(cave->random);
2320 boolean dont_move = FALSE;
2323 /* try to move... */
2328 /*********************************/
2329 /* check for a horizontal movement */
2332 /* if coordinates are the same */
2334 horizontal = !horizontal;
2341 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2343 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2347 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2349 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2358 horizontal = !horizontal;
2366 /********************************/
2367 /* check for a vertical movement */
2370 /* if coordinates are the same */
2372 horizontal = !horizontal;
2378 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2380 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2384 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2386 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2395 horizontal = !horizontal;
2408 /* if we should move in both directions, but can not move in any, stop. */
2413 /* check for horizontal */
2416 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2417 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2418 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2419 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2420 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2421 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2425 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2426 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2427 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2428 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2429 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2430 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2435 /* check for vertical */
2438 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2439 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2440 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2441 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2442 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2443 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2447 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2448 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2449 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2450 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2451 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2452 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2460 if (cave->replicators_wait_frame == 0 &&
2461 cave->replicators_active &&
2462 !cave->gravity_disabled)
2464 /* only replicate, if space is under it. */
2465 /* do not replicate players! */
2466 /* also obeys gravity settings. */
2467 /* only replicate element if it is not a scanned one */
2468 /* do not replicate space... that condition looks like it
2469 makes no sense, but otherwise it generates SCANNED spaces,
2470 which cannot be "collected" by the player, so he cannot run
2471 under a replicator */
2472 if (is_space_dir(cave, x, y, cave->gravity) &&
2473 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2474 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2476 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2477 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2486 if (cave->biters_wait_frame == 0)
2488 static GdDirection biter_move[] =
2496 /* direction, last two bits 0..3 */
2497 int dir = get(cave, x, y) - O_BITER_1;
2498 int dirn = (dir + 3) & 3;
2499 int dirp = (dir + 1) & 3;
2501 GdElement made_sound_of = O_NONE;
2503 for (i = 0; i < G_N_ELEMENTS (biter_try); i++)
2505 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2507 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2508 if (biter_try[i] != O_SPACE)
2509 made_sound_of = O_BITER_1; /* sound of a biter eating */
2512 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2514 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2515 if (biter_try[i] != O_SPACE)
2516 made_sound_of = O_BITER_1; /* sound of a biter eating */
2519 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2521 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2522 if (biter_try[i] != O_SPACE)
2523 made_sound_of = O_BITER_1; /* sound of a biter eating */
2528 if (i == G_N_ELEMENTS(biter_try))
2529 /* i = number of elements in array: could not move, so just turn */
2530 store(cave, x, y, O_BITER_1 + dirp);
2531 else if (biter_try[i] == O_STONE)
2533 /* if there was a stone there, where we moved...
2534 do not eat stones, just throw them back */
2535 store(cave, x, y, O_STONE);
2536 made_sound_of = O_STONE;
2539 /* if biter did move, we had sound. play it. */
2540 if (made_sound_of != O_NONE)
2541 play_sound_of_element(cave, made_sound_of, x, y);
2549 /* check if touches a voodoo */
2550 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2551 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2552 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2553 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2554 cave->voodoo_touched = TRUE;
2556 /* check if touches something bad and should explode (includes voodoo by the flags) */
2557 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2558 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2559 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2560 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2561 explode (cave, x, y);
2562 /* otherwise move */
2565 const GdDirection *creature_move;
2566 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2567 GdElement base = O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2568 int dir, dirn; /* direction */
2570 dir = get(cave, x, y)-base; /* facing where */
2571 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2573 /* now change direction if backwards */
2574 if (cave->creatures_backwards)
2578 dirn = (dir + 3) & 3; /* fast turn */
2580 dirn = (dir + 1) & 3; /* fast turn */
2582 /* if can move forward, does so. */
2583 if (is_space_dir(cave, x, y, creature_move[dir]))
2584 move(cave, x, y, creature_move[dir], base + dir);
2586 /* otherwise turns 90 degrees in place. */
2587 store(cave, x, y, base + dirn);
2592 store(cave, x, y, O_BLADDER_1);
2603 /* bladder with any delay state: try to convert to clock. */
2604 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2605 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))
2607 /* if touches the specified element, let it be a clock */
2608 store(cave, x, y, O_PRE_CLOCK_1);
2610 /* plays the bladder convert sound */
2611 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2615 /* is space over the bladder? */
2616 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2618 if (get(cave, x, y)==O_BLADDER_8)
2620 /* if it is a bladder 8, really move up */
2621 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2622 play_sound_of_element(cave, O_BLADDER, x, y);
2625 /* if smaller delay, just increase delay. */
2629 /* if not space, is something sloped over the bladder? */
2630 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2631 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2633 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2634 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2635 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2637 /* rolling up, to left */
2638 if (get(cave, x, y) == O_BLADDER_8)
2640 /* if it is a bladder 8, really roll */
2641 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2642 play_sound_of_element(cave, O_BLADDER, x, y);
2645 /* if smaller delay, just increase delay. */
2648 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2649 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2650 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2652 /* rolling up, to left */
2653 if (get(cave, x, y) == O_BLADDER_8)
2655 /* if it is a bladder 8, really roll */
2656 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2657 play_sound_of_element(cave, O_BLADDER, x, y);
2660 /* if smaller delay, just increase delay. */
2665 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2667 store(cave, x, y, O_BLADDER_1);
2672 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2673 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2674 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2675 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2676 explode (cave, x, y);
2681 /* the ghost is given four possibilities to move. */
2682 for (i = 0; i < 4; i++)
2684 static GdDirection dirs[] =
2691 GdDirection random_dir;
2693 random_dir = dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2694 if (is_space_dir(cave, x, y, random_dir))
2696 move(cave, x, y, random_dir, O_GHOST);
2697 break; /* ghost did move -> exit loop */
2704 * A C T I V E E L E M E N T S
2709 switch (cave->amoeba_state)
2712 store(cave, x, y, cave->amoeba_too_big_effect);
2715 case GD_AM_ENCLOSED:
2716 store(cave, x, y, cave->amoeba_enclosed_effect);
2719 case GD_AM_SLEEPING:
2721 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2722 if (amoeba_found_enclosed)
2723 /* if still found enclosed, check all four directions,
2724 if this one is able to grow. */
2725 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2726 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2727 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2728 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2730 /* not enclosed. this is a local (per scan) flag! */
2731 amoeba_found_enclosed = FALSE;
2732 cave->amoeba_state = GD_AM_AWAKE;
2735 /* if alive, check in which dir to grow (or not) */
2736 if (cave->amoeba_state==GD_AM_AWAKE)
2738 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2740 switch (g_rand_int_range(cave->random, 0, 4))
2742 /* decided to grow, choose a random direction. */
2743 case 0: /* let this be up. numbers indifferent. */
2744 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2745 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2749 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2750 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2754 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2755 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2759 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2760 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2772 /* check if it is touching an amoeba, and explosion is enabled */
2773 if (cave->amoeba_2_explodes_by_amoeba &&
2774 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2775 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2776 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2777 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2778 explode (cave, x, y);
2780 switch (cave->amoeba_2_state)
2783 store(cave, x, y, cave->amoeba_2_too_big_effect);
2786 case GD_AM_ENCLOSED:
2787 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2790 case GD_AM_SLEEPING:
2792 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2793 if (amoeba_2_found_enclosed)
2794 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2795 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2796 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2797 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2799 /* not enclosed. this is a local (per scan) flag! */
2800 amoeba_2_found_enclosed = FALSE;
2801 cave->amoeba_2_state = GD_AM_AWAKE;
2804 /* if it is alive, decide if it attempts to grow */
2805 if (cave->amoeba_2_state == GD_AM_AWAKE)
2806 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2808 switch (g_rand_int_range(cave->random, 0, 4))
2810 /* decided to grow, choose a random direction. */
2811 case 0: /* let this be up. numbers indifferent. */
2812 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2813 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2817 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2818 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2822 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2823 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2827 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2828 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2838 /* choose randomly, if it spreads */
2839 if (g_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2841 /* the current one explodes */
2842 store(cave, x, y, cave->acid_turns_to);
2844 /* and if neighbours are eaten, put acid there. */
2845 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2847 play_sound_of_element(cave, O_ACID, x, y);
2848 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2851 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2853 play_sound_of_element(cave, O_ACID, x, y);
2854 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2857 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2859 play_sound_of_element(cave, O_ACID, x, y);
2860 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2863 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2865 play_sound_of_element(cave, O_ACID, x, y);
2866 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2873 if (!cave->water_does_not_flow_down &&
2874 is_space_dir(cave, x, y, GD_MV_DOWN))
2875 /* emulating the odd behaviour in crdr */
2876 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2878 if (is_space_dir(cave, x, y, GD_MV_UP))
2879 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2881 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2882 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2884 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2885 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2889 store(cave, x, y, O_WATER);
2892 case O_H_EXPANDING_WALL:
2893 case O_V_EXPANDING_WALL:
2894 case O_H_EXPANDING_STEEL_WALL:
2895 case O_V_EXPANDING_STEEL_WALL:
2896 /* checks first if direction is changed. */
2897 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2898 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2899 !cave->expanding_wall_changed) ||
2900 ((get(cave, x, y)==O_V_EXPANDING_WALL ||
2901 get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) &&
2902 cave->expanding_wall_changed))
2904 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2906 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2907 play_sound_of_element(cave, get(cave, x, y), x, y);
2910 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2911 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2912 play_sound_of_element(cave, get(cave, x, y), x, y);
2917 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2918 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2919 play_sound_of_element(cave, get(cave, x, y), x, y);
2922 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2923 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2924 play_sound_of_element(cave, get(cave, x, y), x, y);
2929 case O_EXPANDING_WALL:
2930 case O_EXPANDING_STEEL_WALL:
2931 /* the wall which grows in all four directions. */
2932 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2934 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2935 play_sound_of_element(cave, get(cave, x, y), x, y);
2938 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2939 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2940 play_sound_of_element(cave, get(cave, x, y), x, y);
2943 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2944 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2945 play_sound_of_element(cave, get(cave, x, y), x, y);
2948 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2949 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2950 play_sound_of_element(cave, get(cave, x, y), x, y);
2956 ; // to make compilers happy ...
2958 g_print("Step[%03d]", cave->frame); /* XXX */
2960 int rrr = gd_cave_c64_random(cave);
2963 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2964 (rrr & cave->slime_permeability_c64) == 0);
2967 * unpredictable: g_rand_int
2968 * predictable: c64 predictable random generator.
2969 * for predictable, a random number is generated,
2970 * whether or not it is even possible that the stone will be able to pass.
2972 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : g_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2974 GdDirection grav = cave->gravity;
2975 GdDirection oppos = opposite[cave->gravity];
2977 /* space under the slime? elements may pass from top to bottom then. */
2978 if (is_space_dir(cave, x, y, grav))
2980 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
2982 /* output a falling xy under */
2983 store_dir(cave, x, y, grav, cave->slime_converts_1);
2985 store_dir(cave, x, y, oppos, O_SPACE);
2986 play_sound_of_element(cave, O_SLIME, x, y);
2988 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
2990 store_dir(cave, x, y, grav, cave->slime_converts_2);
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_3)
2996 store_dir(cave, x, y, grav, cave->slime_converts_3);
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) == O_WAITING_STONE)
3002 /* waiting stones pass without awakening */
3003 store_dir(cave, x, y, grav, O_WAITING_STONE);
3004 store_dir(cave, x, y, oppos, O_SPACE);
3005 play_sound_of_element(cave, O_SLIME, x, y);
3007 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3009 /* chasing stones pass */
3010 store_dir(cave, x, y, grav, O_CHASING_STONE);
3011 store_dir(cave, x, y, oppos, O_SPACE);
3012 play_sound_of_element(cave, O_SLIME, x, y);
3016 /* or space over the slime? elements may pass from bottom to up then. */
3017 if (is_space_dir(cave, x, y, oppos))
3019 if (get_dir(cave, x, y, grav) == O_BLADDER)
3021 /* bladders move UP the slime */
3022 store_dir(cave, x, y, grav, O_SPACE);
3023 store_dir(cave, x, y, oppos, O_BLADDER_1);
3024 play_sound_of_element(cave, O_SLIME, x, y);
3026 else if (get_dir(cave, x, y, grav)==O_FLYING_STONE)
3028 store_dir(cave, x, y, grav, O_SPACE);
3029 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3030 play_sound_of_element(cave, O_SLIME, x, y);
3032 else if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND)
3034 store_dir(cave, x, y, grav, O_SPACE);
3035 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3036 play_sound_of_element(cave, O_SLIME, x, y);
3042 case O_FALLING_WALL:
3043 if (is_space_dir(cave, x, y, grav_compat))
3045 /* try falling if space under. */
3048 for (yy = y + 1; yy < y + cave->h; yy++)
3049 /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
3050 if (get(cave, x, yy) != O_SPACE)
3051 /* stop cycle when other than space */
3054 /* if scanning stopped by a player... start falling! */
3055 if (get(cave, x, yy) == O_PLAYER ||
3056 get(cave, x, yy) == O_PLAYER_GLUED ||
3057 get(cave, x, yy) == O_PLAYER_BOMB)
3059 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3060 /* no sound when the falling wall starts falling! */
3065 case O_FALLING_WALL_F:
3066 switch (get_dir(cave, x, y, grav_compat))
3069 case O_PLAYER_GLUED:
3071 /* if player under, it explodes - the falling wall, not the player! */
3072 explode(cave, x, y);
3076 /* continue falling */
3077 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3082 play_sound_of_element(cave, get(cave, x, y), x, y);
3083 store(cave, x, y, O_FALLING_WALL);
3089 * C O N V E Y O R B E L T S
3092 case O_CONVEYOR_RIGHT:
3093 case O_CONVEYOR_LEFT:
3094 /* only works if gravity is up or down!!! */
3095 /* first, check for gravity and running belts. */
3096 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3098 const GdDirection *dir;
3101 /* decide direction */
3102 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3103 if (cave->conveyor_belts_direction_changed)
3105 dir = left ? ccw_eighth : cw_eighth;
3107 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
3108 /* if gravity is normal, and the conveyor belt has something
3109 ABOVE which can be moved
3111 the gravity is up, so anything that should float now goes
3112 DOWN and touches the conveyor */
3113 if ((cave->gravity == GD_MV_DOWN &&
3114 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3115 (cave->gravity == GD_MV_UP &&
3116 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3118 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3119 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3121 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); /* move */
3122 store_dir(cave, x, y, GD_MV_UP, O_SPACE); /* and place a space. */
3126 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3127 if ((cave->gravity == GD_MV_UP &&
3128 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3129 (cave->gravity == GD_MV_DOWN &&
3130 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3132 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3133 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3135 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); /* move */
3136 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); /* and clear. */
3143 * S I M P L E C H A N G I N G; E X P L O S I O N S
3147 store(cave, x, y, cave->explosion_effect);
3151 store(cave, x, y, O_DIAMOND);
3155 store(cave, x, y, cave->diamond_birth_effect);
3159 store(cave, x, y, O_STONE);
3162 case O_NITRO_EXPL_4:
3163 store(cave, x, y, cave->nitro_explosion_effect);
3167 store(cave, x, y, cave->bomb_explosion_effect);
3170 case O_AMOEBA_2_EXPL_4:
3171 store(cave, x, y, cave->amoeba_2_explosion_effect);
3174 case O_GHOST_EXPL_4:
3176 static GdElement ghost_explode[] =
3178 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3179 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3180 O_WAITING_STONE, O_BITER_1
3183 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
3188 store(cave, x, y, O_STEEL);
3192 store(cave, x, y, O_CLOCK);
3196 explode(cave, x, y);
3199 case O_TRAPPED_DIAMOND:
3200 if (cave->diamond_key_collected)
3201 store(cave, x, y, O_DIAMOND);
3205 if (cave->gate_open) /* if no more diamonds needed */
3206 store(cave, x, y, O_OUTBOX); /* open outbox */
3209 case O_PRE_INVIS_OUTBOX:
3210 if (cave->gate_open) /* if no more diamonds needed */
3211 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
3215 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
3216 store(cave, x, y, O_PRE_PL_1);
3217 inbox_toggle = !inbox_toggle;
3221 store(cave, x, y, O_PLAYER);
3246 case O_GHOST_EXPL_1:
3247 case O_GHOST_EXPL_2:
3248 case O_GHOST_EXPL_3:
3258 case O_NITRO_EXPL_1:
3259 case O_NITRO_EXPL_2:
3260 case O_NITRO_EXPL_3:
3261 case O_AMOEBA_2_EXPL_1:
3262 case O_AMOEBA_2_EXPL_2:
3263 case O_AMOEBA_2_EXPL_3:
3264 /* simply the next identifier */
3283 found_water = TRUE; /* for sound */
3284 /* simply the next identifier */
3288 case O_BLADDER_SPENDER:
3289 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3291 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3292 store(cave, x, y, O_PRE_STEEL_1);
3293 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3298 /* other inanimate elements that do nothing */
3304 /* POSTPROCESSING */
3306 /* another scan-like routine: */
3307 /* short explosions (for example, in bd1) started with explode_2. */
3308 /* internally we use explode_1; and change it to explode_2 if needed. */
3309 if (cave->short_explosions)
3311 for (y = 0; y < cave->h; y++)
3313 for (x = 0; x < cave->w; x++)
3315 if (is_first_stage_of_explosion(cave, x, y))
3317 next(cave, x, y); /* select next frame of explosion */
3319 /* forget scanned flag immediately */
3320 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3326 /* finally: forget "scanned" flags for objects. */
3327 /* also, check for time penalties. */
3328 /* these is something like an effect table, but we do not really use one. */
3329 for (y = 0; y < cave->h; y++)
3331 for (x = 0; x < cave->w; x++)
3333 if (get(cave, x, y) & SCANNED)
3334 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3336 if (get(cave, x, y) == O_TIME_PENALTY)
3338 store(cave, x, y, O_GRAVESTONE);
3340 /* there is time penalty for destroying the voodoo */
3341 time_decrement_sec += cave->time_penalty;
3346 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3347 /* but we only do this, if a living player was found. if not yet, the setup
3348 routine coordinates are used */
3349 if (cave->player_state==GD_PL_LIVING)
3351 if (cave->active_is_first_found)
3353 /* to be 1stb compatible, we do everything backwards. */
3354 for (y = cave->h - 1; y >= 0; y--)
3356 for (x = cave->w - 1; x >= 0; x--)
3358 if (is_player(cave, x, y))
3360 /* here we remember the coordinates. */
3369 /* as in the original: look for the last one */
3370 for (y = 0; y < cave->h; y++)
3372 for (x = 0; x < cave->w; x++)
3374 if (is_player(cave, x, y))
3376 /* here we remember the coordinates. */
3385 /* record coordinates of player for chasing stone */
3386 for (i = 0; i < G_N_ELEMENTS(cave->px) - 1; i++)
3388 cave->px[i] = cave->px[i + 1];
3389 cave->py[i] = cave->py[i + 1];
3392 cave->px[G_N_ELEMENTS(cave->px) - 1] = cave->player_x;
3393 cave->py[G_N_ELEMENTS(cave->py) - 1] = cave->player_y;
3397 /* update timing calculated by iterating and counting elements */
3398 update_cave_speed(cave);
3400 /* cave 3 sounds. precedence is controlled by the sound_play function. */
3401 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3403 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3405 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3406 cave->magic_wall_sound);
3408 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3409 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3410 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3412 if (amoeba_sound && magic_sound)
3414 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3419 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3420 else if (magic_sound)
3421 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3426 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3427 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3428 play_sound_of_element(cave, O_AMOEBA, x, y);
3431 /* pneumatic hammer sound - overrides everything. */
3432 if (cave->pneumatic_hammer_active_delay > 0)
3433 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3435 /* CAVE VARIABLES */
3439 /* check if player is alive. */
3440 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3441 cave->player_state = GD_PL_DIED;
3443 /* check if any voodoo exploded, and kill players the next scan if that happended. */
3444 if (cave->voodoo_touched)
3445 cave->kill_player = TRUE;
3449 /* check flags after evaluating. */
3450 if (cave->amoeba_state == GD_AM_AWAKE)
3452 if (amoeba_count >= cave->amoeba_max_count)
3453 cave->amoeba_state = GD_AM_TOO_BIG;
3454 if (amoeba_found_enclosed)
3455 cave->amoeba_state = GD_AM_ENCLOSED;
3458 /* amoeba can also be turned into diamond by magic wall */
3459 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3460 cave->amoeba_state = GD_AM_ENCLOSED;
3463 if (cave->amoeba_2_state == GD_AM_AWAKE)
3465 /* check flags after evaluating. */
3466 if (amoeba_2_count >= cave->amoeba_2_max_count)
3467 cave->amoeba_2_state = GD_AM_TOO_BIG;
3469 if (amoeba_2_found_enclosed)
3470 cave->amoeba_2_state = GD_AM_ENCLOSED;
3473 /* amoeba 2 can also be turned into diamond by magic wall */
3474 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3475 cave->amoeba_2_state = GD_AM_ENCLOSED;
3477 /* now check times. --------------------------- */
3478 /* decrement time if a voodoo was killed. */
3479 cave->time -= time_decrement_sec * cave->timing_factor;
3483 /* only decrement time when player is already born. */
3486 int secondsbefore, secondsafter;
3488 secondsbefore = cave->time / cave->timing_factor;
3489 cave->time -= cave->speed;
3490 if (cave->time <= 0)
3493 secondsafter = cave->time / cave->timing_factor;
3494 if (cave->time / cave->timing_factor < 10)
3495 /* if less than 10 seconds, no walking sound, but play explosion sound */
3496 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3498 if (secondsbefore != secondsafter)
3499 gd_cave_set_seconds_sound(cave);
3502 /* a gravity switch was activated; seconds counting down */
3503 if (cave->gravity_will_change > 0)
3505 cave->gravity_will_change -= cave->speed;
3506 if (cave->gravity_will_change < 0)
3507 cave->gravity_will_change = 0;
3509 if (cave->gravity_will_change == 0)
3511 cave->gravity = cave->gravity_next_direction;
3512 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); /* takes precedence over amoeba and magic wall sound */
3516 /* creatures direction automatically change */
3517 if (cave->creatures_direction_will_change > 0)
3519 cave->creatures_direction_will_change -= cave->speed;
3520 if (cave->creatures_direction_will_change < 0)
3521 cave->creatures_direction_will_change = 0;
3523 if (cave->creatures_direction_will_change == 0)
3525 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3527 cave->creatures_backwards = !cave->creatures_backwards;
3528 cave->creatures_direction_will_change =
3529 cave->creatures_direction_auto_change_time * cave->timing_factor;
3533 /* magic wall; if active&wait or not wait for hatching */
3534 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3535 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3537 cave->magic_wall_time -= cave->speed;
3538 if (cave->magic_wall_time < 0)
3539 cave->magic_wall_time = 0;
3540 if (cave->magic_wall_time == 0)
3541 cave->magic_wall_state = GD_MW_EXPIRED;
3544 /* we may wait for hatching, when starting amoeba */
3545 if (cave->amoeba_timer_started_immediately ||
3546 (cave->amoeba_state == GD_AM_AWAKE &&
3547 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3549 cave->amoeba_time -= cave->speed;
3550 if (cave->amoeba_time < 0)
3551 cave->amoeba_time = 0;
3552 if (cave->amoeba_time == 0)
3553 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3556 /* we may wait for hatching, when starting amoeba */
3557 if (cave->amoeba_timer_started_immediately ||
3558 (cave->amoeba_2_state == GD_AM_AWAKE &&
3559 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3561 cave->amoeba_2_time -= cave->speed;
3562 if (cave->amoeba_2_time < 0)
3563 cave->amoeba_2_time = 0;
3564 if (cave->amoeba_2_time == 0)
3565 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3568 /* check for player hatching. */
3569 start_signal = FALSE;
3571 /* if not the c64 scheduling, but the correct frametime is used,
3572 hatching delay should always be decremented. */
3573 /* otherwise, the if (millisecs...) condition below will set this. */
3574 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3576 /* NON-C64 scheduling */
3577 if (cave->hatching_delay_frame > 0)
3579 /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3580 cave->hatching_delay_frame--;
3581 if (cave->hatching_delay_frame == 0)
3582 start_signal = TRUE;
3587 /* C64 scheduling */
3588 if (cave->hatching_delay_time > 0)
3590 /* for c64 schedulings, hatching delay means milliseconds. */
3591 cave->hatching_delay_time -= cave->speed;
3592 if (cave->hatching_delay_time <= 0)
3594 cave->hatching_delay_time = 0;
3595 start_signal = TRUE;
3600 /* if decremented hatching, and it became zero: */
3603 /* THIS IS THE CAVE START SIGNAL */
3605 /* record that now the cave is in its normal state */
3606 cave->hatched = TRUE;
3608 /* if diamonds needed is below zero, we count the available diamonds now. */
3609 gd_cave_count_diamonds(cave);
3611 /* setup direction auto change */
3612 if (cave->creatures_direction_auto_change_time)
3614 cave->creatures_direction_will_change =
3615 cave->creatures_direction_auto_change_time * cave->timing_factor;
3617 if (cave->creatures_direction_auto_change_on_start)
3618 cave->creatures_backwards = !cave->creatures_backwards;
3621 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3625 if (cave->biters_wait_frame == 0)
3626 cave->biters_wait_frame = cave->biter_delay_frame;
3628 cave->biters_wait_frame--;
3630 /* replicators delay */
3631 if (cave->replicators_wait_frame == 0)
3632 cave->replicators_wait_frame = cave->replicator_delay_frame;
3634 cave->replicators_wait_frame--;
3639 /* check if cave failed by timeout is done in main game engine */
3641 /* check if cave failed by timeout */
3642 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3644 gd_cave_clear_sounds(cave);
3645 cave->player_state = GD_PL_TIMEOUT;
3646 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3650 /* set these for drawing. */
3651 cave->last_direction = player_move;
3652 /* here we remember last movements for animation. this is needed here,
3653 as animation is in sync with the game, not the keyboard directly.
3654 (for example, after exiting the cave, the player was "running" in the
3655 original, till bonus points were counted for remaining time and so on. */
3656 if (player_move == GD_MV_LEFT ||
3657 player_move == GD_MV_UP_LEFT ||
3658 player_move == GD_MV_DOWN_LEFT)
3659 cave->last_horizontal_direction = GD_MV_LEFT;
3661 if (player_move == GD_MV_RIGHT ||
3662 player_move == GD_MV_UP_RIGHT ||
3663 player_move == GD_MV_DOWN_RIGHT)
3664 cave->last_horizontal_direction = GD_MV_RIGHT;
3666 cave->frame++; /* XXX */
3669 void set_initial_cave_speed(GdCave *cave)
3674 /* set cave get function; to implement perfect or lineshifting borders */
3675 if (cave->lineshift)
3676 cave->getp = getp_shift;
3678 cave->getp = getp_perfect;
3680 /* check whether to scan the first and last line */
3681 if (cave->border_scan_first_and_last)
3692 for (y = ymin; y <= ymax; y++)
3694 for (x = 0; x < cave->w; x++)
3696 /* add the ckdelay correction value for every element seen. */
3697 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3701 /* update timing calculated by iterating and counting elements */
3702 update_cave_speed(cave);