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 GdElement *getp(const GdCave *cave, const int x, const int y)
308 return cave->getp(cave, x, y);
312 perfect (non-lineshifting) GET function.
313 returns a pointer to a selected cave element by its coordinates.
315 static inline GdElement *getp_perfect(const GdCave *cave, const int x, const int y)
317 /* (x + n) mod n: this works also for x >= n and -n + 1 < x < 0 */
318 return &(cave->map[(y + cave->h) % cave->h][(x + cave->w) % cave->w]);
322 line shifting GET function; returns a pointer to the selected cave element.
323 this is used to emulate the line-shifting behaviour of original games, so that
324 the player entering one side will appear one row above or below on the other.
326 static inline GdElement *getp_shift(const GdCave *cave, int x, int y)
339 y = (y + cave->h) % cave->h;
341 return &(cave->map[y][x]);
344 static inline GdElement get(const GdCave *cave, const int x, const int y)
346 return *getp(cave, x, y);
349 /* returns an element which is somewhere near x,y */
350 static inline GdElement get_dir(const GdCave *cave, const int x, const int y,
351 const GdDirection dir)
353 return get(cave, x + gd_dx[dir], y + gd_dy[dir]);
356 static inline boolean explodes_by_hit_dir(const GdCave *cave, const int x,
357 const int y, GdDirection dir)
359 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_EXPLODES_BY_HIT) != 0;
362 /* returns true if the element is not explodable, for example the steel wall */
363 static inline boolean non_explodable(const GdCave *cave, const int x, const int y)
365 return (gd_elements[get(cave, x,y) & O_MASK].properties & P_NON_EXPLODABLE) != 0;
368 /* returns true if the element can be eaten by the amoeba, eg. space and dirt. */
369 static inline boolean amoeba_eats_dir(const GdCave *cave, const int x, const int y,
370 const GdDirection dir)
372 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_AMOEBA_CONSUMES) != 0;
375 /* returns true if the element is sloped, so stones and diamonds roll down on it.
376 for example a stone or brick wall */
377 static inline boolean sloped_dir(const GdCave *cave, const int x, const int y,
378 const GdDirection dir, const GdDirection slop)
383 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_LEFT) != 0;
386 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_RIGHT) != 0;
389 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_UP) != 0;
392 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_DOWN) != 0;
401 /* returns true if the element is sloped for bladder movement
402 (brick = yes, diamond = no, for example) */
403 static inline boolean sloped_for_bladder_dir (const GdCave *cave, const int x, const int y,
404 const GdDirection dir)
406 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLADDER_SLOPED) != 0;
409 static inline boolean blows_up_flies_dir(const GdCave *cave, const int x, const int y,
410 const GdDirection dir)
412 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLOWS_UP_FLIES) != 0;
415 /* returns true if the element is a counter-clockwise creature */
416 static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
418 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_CCW) != 0;
421 /* returns true if the element is a player */
422 static inline boolean is_player(const GdCave *cave, const int x, const int y)
424 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
427 /* returns true if the element is a player */
428 static inline boolean is_player_dir(const GdCave *cave, const int x, const int y,
429 const GdDirection dir)
431 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PLAYER) != 0;
434 static inline boolean can_be_hammered_dir(const GdCave *cave, const int x, const int y,
435 const GdDirection dir)
437 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
440 /* returns true if the element is explodable and explodes to space, for example the player */
441 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
443 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
446 /* returns true if the element is moved by the conveyor belt */
447 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
448 const GdDirection dir)
450 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
453 /* returns true if the element is moved by the conveyor belt */
454 static inline boolean moved_by_conveyor_bottom_dir(const GdCave *cave, const int x, const int y,
455 const GdDirection dir)
457 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_BOTTOM) != 0;
460 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
461 const GdDirection dir)
463 return (get_dir(cave, x, y, dir) & SCANNED) != 0;
466 /* returns true if neighbouring element is "e" */
467 /* treats dirt specially */
468 /* treats lava specially */
469 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
470 const GdDirection dir, GdElement e)
472 GdElement examined = get_dir(cave, x, y, dir);
474 /* if it is a dirt-like, change to dirt, so equality will evaluate to true */
475 if (gd_elements[examined & O_MASK].properties & P_DIRT)
478 if (gd_elements[e & O_MASK].properties & P_DIRT)
481 /* if the element on the map is a lava, it should be like space */
482 if (examined == O_LAVA)
485 return (e == examined);
488 /* returns true if neighbouring element is space */
489 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
490 const GdDirection dir)
492 GdElement e = get_dir(cave, x, y, dir)&O_MASK;
494 return (e == O_SPACE || e == O_LAVA);
497 /* store an element at the given position */
498 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
500 GdElement *e = getp(cave, x, y);
504 play_sound_of_element(cave, O_LAVA, x, y);
512 /* store an element with SCANNED flag turned on */
513 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
515 store(cave, x, y, element | SCANNED);
518 /* store an element to a neighbouring cell */
519 static inline void store_dir(GdCave *cave, const int x, const int y,
520 const GdDirection dir, const GdElement element)
522 store(cave, x + gd_dx[dir], y + gd_dy[dir], element|SCANNED);
525 /* store an element to a neighbouring cell */
526 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
527 const GdDirection dir, const GdElement element)
529 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
532 /* move element to direction; then place space at x, y */
533 static inline void move(GdCave *cave, const int x, const int y,
534 const GdDirection dir, const GdElement e)
536 store_dir(cave, x, y, dir, e);
537 store(cave, x, y, O_SPACE);
540 /* increment a cave element; can be used for elements which are one after
541 the other, for example bladder1, bladder2, bladder3... */
542 static inline void next(GdCave *cave, const int x, const int y)
544 (*getp(cave, x, y))++;
547 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
549 if (non_explodable (cave, x, y))
552 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
553 cave->voodoo_touched = TRUE;
555 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
556 /* voodoo turns into a time penalty */
557 store_sc(cave, x, y, O_TIME_PENALTY);
558 else if (get(cave, x, y) == O_NITRO_PACK ||
559 get(cave, x, y) == O_NITRO_PACK_F)
560 /* nitro pack inside an explosion - it is now triggered */
561 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
563 /* for everything else */
564 store_sc(cave, x, y, explode_to);
567 /* a creature explodes to a 3x3 something. */
568 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
572 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
573 cave->ckdelay += 1200;
574 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
576 for (yy = y - 1; yy <= y + 1; yy++)
577 for (xx = x - 1; xx <= x + 1; xx++)
578 cell_explode(cave, xx, yy, explode_to);
581 static void nitro_explode(GdCave *cave, int x, int y)
585 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
586 cave->ckdelay += 1200;
587 gd_sound_play(cave, GD_S_NITRO_PACK_EXPLODING, get(cave, x, y), x, y);
589 for (yy = y - 1; yy <= y + 1; yy++)
590 for (xx = x - 1; xx <= x + 1; xx++)
591 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
593 /* the current cell is explicitly changed into a nitro expl,
594 as cell_explode changes it to a triggered nitro pack */
595 store_sc(cave, x, y, O_NITRO_EXPL_1);
598 /* a voodoo explodes, leaving a 3x3 steel and a time penalty behind. */
599 static void voodoo_explode(GdCave *cave, int x, int y)
603 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
604 cave->ckdelay += 1000;
606 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
607 if (cave->voodoo_any_hurt_kills_player)
608 cave->voodoo_touched = TRUE;
610 /* voodoo explodes to 3x3 steel */
611 for (yy = y - 1; yy <= y + 1; yy++)
612 for (xx = x - 1; xx <= x + 1; xx++)
613 store_sc(cave, xx, yy, O_PRE_STEEL_1);
615 /* middle is a time penalty (which will be turned into a gravestone) */
616 store_sc(cave, x, y, O_TIME_PENALTY);
619 /* a bomb does not explode the voodoo, neither does the ghost.
620 this function check this, and stores the new element or not.
621 destroying the voodoo is also controlled by the
622 voodoo_disappear_in_explosion flag. */
623 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
625 if (non_explodable (cave, x, y))
628 /* bomb does not explode voodoo */
629 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
632 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
633 cave->voodoo_touched = TRUE;
635 store_sc (cave, x, y, expl);
638 /* X shaped ghost explosion; does not touch voodoo! */
639 static void ghost_explode(GdCave *cave, const int x, const int y)
641 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
643 /* the processing of an explosion took pretty much time: processing 5 elements */
644 cave->ckdelay += 650;
646 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
647 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
648 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
649 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
650 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
653 /* +shaped bomb explosion; does not touch voodoo! */
654 static void bomb_explode(GdCave *cave, const int x, const int y)
656 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
658 /* the processing of an explosion took pretty much time: processing 5 elements */
659 cave->ckdelay += 650;
661 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
662 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
663 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
664 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
665 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
669 explode an element with the appropriate type of exlposion.
671 static void explode(GdCave *cave, int x, int y)
673 GdElement e = get(cave, x, y) & O_MASK;
678 ghost_explode(cave, x, y);
682 bomb_explode(cave, x, y);
686 voodoo_explode(cave, x, y);
691 case O_NITRO_PACK_EXPLODE:
692 nitro_explode(cave, x, y);
696 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
699 case O_FALLING_WALL_F:
700 creature_explode(cave, x, y, O_EXPLODE_1);
707 creature_explode(cave, x, y, cave->butterfly_explode_to);
714 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
721 creature_explode(cave, x, y, cave->firefly_explode_to);
724 case O_ALT_FIREFLY_1:
725 case O_ALT_FIREFLY_2:
726 case O_ALT_FIREFLY_3:
727 case O_ALT_FIREFLY_4:
728 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
734 case O_PLAYER_STIRRING:
735 case O_PLAYER_PNEUMATIC_LEFT:
736 case O_PLAYER_PNEUMATIC_RIGHT:
737 creature_explode(cave, x, y, O_EXPLODE_1);
744 creature_explode(cave, x, y, cave->stonefly_explode_to);
751 creature_explode(cave, x, y, cave->dragonfly_explode_to);
759 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
761 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
765 player eats specified object.
766 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
767 returns other element if something other appears there and he can't move.
768 cave pointer is needed to know the diamond values.
770 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
777 cave->diamond_key_collected = TRUE;
778 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
783 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
788 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
793 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
800 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
807 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
814 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
819 case O_CREATURE_SWITCH: /* creatures change direction. */
820 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
821 cave->creatures_backwards = !cave->creatures_backwards;
824 case O_EXPANDING_WALL_SWITCH: /* expanding wall change direction. */
825 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
826 cave->expanding_wall_changed = !cave->expanding_wall_changed;
829 case O_BITER_SWITCH: /* biter change delay */
830 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
831 cave->biter_delay_frame++;
832 if (cave->biter_delay_frame == 4)
833 cave->biter_delay_frame = 0;
836 case O_REPLICATOR_SWITCH: /* replicator on/off switch */
837 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
838 cave->replicators_active = !cave->replicators_active;
841 case O_CONVEYOR_SWITCH: /* conveyor belts on/off */
842 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
843 cave->conveyor_belts_active = !cave->conveyor_belts_active;
846 case O_CONVEYOR_DIR_SWITCH: /* conveyor belts switch direction */
847 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
848 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
854 case O_STEEL_EATABLE:
855 case O_BRICK_EATABLE:
856 case O_DIRT_SLOPED_UP_RIGHT:
857 case O_DIRT_SLOPED_UP_LEFT:
858 case O_DIRT_SLOPED_DOWN_LEFT:
859 case O_DIRT_SLOPED_DOWN_RIGHT:
862 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
866 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
867 cave->sweet_eaten = TRUE;
870 case O_PNEUMATIC_HAMMER:
871 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
872 cave->got_pneumatic_hammer = TRUE;
877 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
878 cave->time += cave->time_bonus * cave->timing_factor;
879 if (cave->time > cave->max_time * cave->timing_factor)
880 cave->time -= cave->max_time * cave->timing_factor;
881 /* no space, rather a dirt remains there... */
885 case O_FLYING_DIAMOND:
886 // prevent diamond sounds for O_SKELETON (see below)
887 if (x != -1 && y != -1)
888 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
889 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
891 cave->score += cave->diamond_value;
892 cave->diamonds_collected++;
894 if (cave->diamonds_needed == cave->diamonds_collected)
896 cave->gate_open = TRUE;
898 /* extra is worth more points. */
899 cave->diamond_value = cave->extra_diamond_value;
901 cave->gate_open_flash = 1;
902 cave->sound3 = GD_S_CRACKING;
903 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
908 cave->skeletons_collected++;
910 /* as if player got a diamond */
911 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
912 player_get_element(cave, O_DIAMOND, -1, -1);
914 /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
915 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
920 cave->player_state = GD_PL_EXITED; /* player now exits the cave! */
924 case O_LAVA: /* player goes into lava, as if it was space */
925 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
929 /* the object will remain there. */
935 process a crazy dream-style teleporter.
936 called from gd_cave_iterate, for a player or a player_bomb.
937 player is standing at px, py, and trying to move in the direction player_move,
938 where there is a teleporter.
939 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
940 around). the first teleporter we find, and which is suitable, will be the destination.
941 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
943 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
952 /* jump to next element; wrap around columns and rows. */
964 /* if we found a teleporter... */
965 if (get(cave, tx, ty) == O_TELEPORTER &&
966 is_space_dir(cave, tx, ty, player_move))
968 /* new player appears near teleporter found */
969 store_dir(cave, tx, ty, player_move, get(cave, px, py));
971 /* current player disappears */
972 store(cave, px, py, O_SPACE);
974 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
976 return TRUE; /* return true as teleporter worked */
979 /* loop until we get back to original coordinates */
980 while (tx != px || ty != py);
982 /* return false as we did not find any usable teleporter */
987 try to push an element.
988 returns true if the push is possible; also does move the specified _element_.
989 up to the caller to move the _player_itself_.
991 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
994 GdElement what = get_dir(cave, x, y, player_move);
996 /* gravity for falling wall, bladder, ... */
997 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1003 case O_WAITING_STONE:
1006 case O_CHASING_STONE:
1008 case O_FLYING_STONE:
1010 /* pushing some kind of stone or nut */
1011 /* directions possible: 90degrees cw or ccw to current gravity. */
1012 /* only push if player dir is orthogonal to gravity,
1013 ie. gravity down, pushing left & right possible */
1014 if (player_move == ccw_fourth[cave->gravity] ||
1015 player_move == cw_fourth[cave->gravity])
1021 /* different probabilities for different elements. */
1024 case O_WAITING_STONE:
1025 /* waiting stones are light, can always push */
1029 case O_CHASING_STONE:
1030 /* chasing can be pushed if player is turbo */
1031 if (cave->sweet_eaten)
1036 /* mega may(!) be pushed if player is turbo */
1037 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1043 case O_FLYING_STONE:
1045 if (cave->sweet_eaten)
1046 prob = cave->pushing_stone_prob_sweet; /* probability with sweet */
1048 prob = cave->pushing_stone_prob; /* probability without sweet. */
1055 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1056 g_rand_int_range(cave->random, 0, 1000000) < prob)
1058 /* if decided that he will be able to push, */
1059 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1060 play_sound_of_element_pushing(cave, what, x, y);
1075 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1076 * not an O_BLADDER_x. */
1077 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
1079 /* first check: we cannot push a bladder "up" */
1080 if (player_move != opposite[grav_compat])
1082 /* pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check. */
1083 /* player moving in the direction of gravity. */
1087 if (player_move == grav_compat)
1089 /* pushing bladder down */
1090 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1091 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1092 /* if no space to push down, maybe left (down-left to player) */
1093 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1095 /* left is "down, turned right (cw)" */
1096 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1097 /* if not, maybe right (down-right to player) */
1098 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1099 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1102 /* pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check. */
1106 else if (player_move == cw_fourth[grav_compat])
1108 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) /* pushing it left */
1109 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1110 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) /* maybe down, and player will move left */
1111 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1112 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) /* maybe up, and player will move left */
1113 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1116 /* pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check. */
1120 else if (player_move == ccw_fourth[grav_compat])
1122 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) /* pushing it right */
1123 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1124 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) /* maybe down, and player will move right */
1125 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1126 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) /* maybe up, and player will move right */
1127 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1131 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1136 /* a box is only pushed with the fire pressed */
1139 /* but always with 100% probability */
1140 switch (player_move)
1146 /* pushing in some dir, two steps in that dir - is there space? */
1147 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1150 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1152 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1157 /* push in no other directions possible */
1163 /* pushing of other elements not possible */
1171 /* from the key press booleans, create a direction */
1172 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1174 GdDirection player_move;
1176 /* from the key press booleans, create a direction */
1178 player_move = GD_MV_UP_RIGHT;
1179 else if (down && right)
1180 player_move = GD_MV_DOWN_RIGHT;
1181 else if (down && left)
1182 player_move = GD_MV_DOWN_LEFT;
1183 else if (up && left)
1184 player_move = GD_MV_UP_LEFT;
1186 player_move = GD_MV_UP;
1188 player_move = GD_MV_DOWN;
1190 player_move = GD_MV_LEFT;
1192 player_move = GD_MV_RIGHT;
1194 player_move = GD_MV_STILL;
1199 /* clear these to no sound; and they will be set during iteration. */
1200 void gd_cave_clear_sounds(GdCave *cave)
1202 cave->sound1 = GD_S_NONE;
1203 cave->sound2 = GD_S_NONE;
1204 cave->sound3 = GD_S_NONE;
1207 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1208 GdElement falling_element)
1210 if (cave->gravity_disabled)
1213 if (is_space_dir(cave, x, y, falling_direction))
1215 /* beginning to fall */
1216 play_sound_of_element(cave, get(cave, x, y), x, y);
1217 move(cave, x, y, falling_direction, falling_element);
1220 /* check if it is on a sloped element, and it can roll. */
1221 /* for example, sloped wall looks like: */
1224 /* this is tagged as sloped up&left. */
1225 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1226 /* then check the direction to roll (left or right) */
1227 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1228 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1230 /* rolling down, if sitting on a sloped object */
1231 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1232 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1233 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1235 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1236 so here we use cw_fourth */
1237 play_sound_of_element(cave, get(cave, x, y), x, y);
1238 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1240 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1241 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1242 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1244 /* rolling right? */
1245 play_sound_of_element(cave, get(cave, x, y), x, y);
1246 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1251 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1253 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1254 cave->voodoo_dies_by_stone)
1256 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1257 explode_dir(cave, x, y, fall_dir);
1264 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1266 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1267 cave->voodoo_collects_diamonds)
1269 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1270 player_get_element(cave, O_DIAMOND, x, y); /* as if player got diamond */
1271 store(cave, x, y, O_SPACE); /* diamond disappears */
1278 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1279 GdDirection fall_dir, GdElement bouncing)
1281 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1282 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1285 store(cave, x, y, bouncing);
1286 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1288 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1296 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1297 GdDirection fall_dir, GdElement magic)
1299 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1301 play_sound_of_element(cave, O_DIAMOND, x, y); /* always play diamond sound */
1303 if (cave->magic_wall_state==GD_MW_DORMANT)
1304 cave->magic_wall_state = GD_MW_ACTIVE;
1306 if (cave->magic_wall_state==GD_MW_ACTIVE &&
1307 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1309 /* if magic wall active and place underneath, it turns element
1310 into anything the effect says to do. */
1311 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1314 /* active or non-active or anything, element falling in will always disappear */
1315 store(cave, x, y, O_SPACE);
1323 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1325 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1327 explode_dir(cave, x, y, fall_dir);
1334 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1335 GdDirection fall_dir, GdElement bouncing)
1337 if (is_space_dir(cave, x, y, fall_dir))
1339 /* falling further */
1340 move(cave, x, y, fall_dir, get(cave, x, y));
1345 /* check if it is on a sloped element, and it can roll. */
1346 /* for example, sloped wall looks like: */
1349 /* this is tagged as sloped up&left. */
1350 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1351 /* then check the direction to roll (left or right) */
1352 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1354 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1356 /* sloped element, falling to left or right */
1357 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1358 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1359 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1361 play_sound_of_element(cave, get(cave, x, y), x, y);
1363 /* try to roll left first - see O_STONE to understand why cw_fourth */
1364 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1366 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1367 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1368 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1370 play_sound_of_element(cave, get(cave, x, y), x, y);
1372 /* if not, try to roll right */
1373 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1377 /* cannot roll in any direction, so it stops */
1378 play_sound_of_element(cave, get(cave, x, y), x, y);
1379 store(cave, x, y, bouncing);
1385 /* any other element, stops */
1386 play_sound_of_element(cave, get(cave, x, y), x, y);
1387 store(cave, x, y, bouncing);
1391 static void update_cave_speed(GdCave *cave)
1393 /* update timing calculated by iterating and counting elements which were slow to process on c64 */
1394 switch (cave->scheduling)
1396 case GD_SCHEDULING_MILLISECONDS:
1397 /* cave->speed already contains the milliseconds value, do not touch it */
1400 case GD_SCHEDULING_BD1:
1401 if (!cave->intermission)
1402 /* non-intermissions */
1403 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1405 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
1406 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1409 case GD_SCHEDULING_BD1_ATARI:
1410 /* about 20ms/frame faster than c64 version */
1411 if (!cave->intermission)
1412 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); /* non-intermissions */
1414 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); /* for intermissions */
1417 case GD_SCHEDULING_BD2:
1418 /* 60 is a guess. */
1419 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1422 case GD_SCHEDULING_PLCK:
1423 /* 65 is totally empty cave in construction kit, with delay = 0) */
1424 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1427 case GD_SCHEDULING_BD2_PLCK_ATARI:
1428 /* a really fast engine; timing works like c64 plck. */
1429 /* 40 ms was measured in the construction kit, with delay = 0 */
1430 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1433 case GD_SCHEDULING_CRDR:
1434 if (cave->hammered_walls_reappear) /* this made the engine very slow. */
1435 cave->ckdelay += 60000;
1436 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1439 case GD_SCHEDULING_MAX:
1444 /* process a cave. */
1445 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1449 /* for border scan */
1452 /* amoeba found to be enclosed. if not, this is cleared */
1453 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1455 /* counting the number of amoebas. after scan, check if too much */
1456 int amoeba_count, amoeba_2_count;
1458 /* cave scan found water - for sound */
1459 boolean found_water;
1461 boolean inbox_toggle;
1462 boolean start_signal;
1464 /* gravity for falling wall, bladder, ... */
1465 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1467 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1468 static const GdDirection creature_dir[] =
1475 static const GdDirection creature_chdir[] =
1482 int time_decrement_sec;
1484 /* biters eating elements preference, they try to go in this order */
1485 GdElement biter_try[] =
1492 boolean amoeba_sound, magic_sound;
1494 gd_cave_clear_sounds(cave);
1496 /* if diagonal movements not allowed, */
1497 /* horizontal movements have precedence. [BROADRIBB] */
1498 if (!cave->diagonal_movements)
1500 switch (player_move)
1502 case GD_MV_UP_RIGHT:
1503 case GD_MV_DOWN_RIGHT:
1504 player_move = GD_MV_RIGHT;
1508 case GD_MV_DOWN_LEFT:
1509 player_move = GD_MV_LEFT;
1513 /* no correction needed */
1518 /* set cave get function; to implement perfect or lineshifting borders */
1519 if (cave->lineshift)
1520 cave->getp = getp_shift;
1522 cave->getp = getp_perfect;
1524 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1525 if (cave->player_seen_ago < 100)
1526 cave->player_seen_ago++;
1528 if (cave->pneumatic_hammer_active_delay > 0)
1529 cave->pneumatic_hammer_active_delay--;
1531 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1532 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1533 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1534 inbox_toggle = cave->inbox_flash_toggle;
1536 if (cave->gate_open_flash > 0)
1537 cave->gate_open_flash--;
1539 /* score collected this frame */
1542 /* suicide only kills the active player */
1543 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1544 /* we must check if there is a player or not - he may have exploded or something like that */
1545 if (suicide && cave->player_state == GD_PL_LIVING &&
1546 is_player(cave, cave->player_x, cave->player_y))
1547 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1549 /* check for walls reappearing */
1550 if (cave->hammered_reappear)
1552 for (y = 0; y < cave->h; y++)
1554 for (x = 0; x < cave->w; x++)
1556 /* timer for the cell > 0? */
1557 if (cave->hammered_reappear[y][x]>0)
1559 /* decrease timer */
1560 cave->hammered_reappear[y][x]--;
1562 /* check if it became zero */
1563 if (cave->hammered_reappear[y][x] == 0)
1565 store(cave, x, y, O_BRICK);
1566 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1573 /* variables to check during the scan */
1575 /* will be set to false if any of the amoeba is found free. */
1576 amoeba_found_enclosed = TRUE;
1577 amoeba_2_found_enclosed = TRUE;
1580 found_water = FALSE;
1582 time_decrement_sec = 0;
1584 /* check whether to scan the first and last line */
1585 if (cave->border_scan_first_and_last)
1596 /* the cave scan routine */
1597 for (y = ymin; y <= ymax; y++)
1599 for (x = 0; x < cave->w; x++)
1601 /* if we find a scanned element, change it to the normal one, and that's all. */
1602 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1603 if (get(cave, x, y)&SCANNED)
1605 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1610 /* add the ckdelay correction value for every element seen. */
1611 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1613 switch (get(cave, x, y))
1619 if (cave->kill_player)
1621 explode (cave, x, y);
1625 cave->player_seen_ago = 0;
1626 /* bd4 intermission caves have many players. so if one of them has exited,
1627 * do not change the flag anymore. so this if () is needed */
1628 if (cave->player_state!=GD_PL_EXITED)
1629 cave->player_state = GD_PL_LIVING;
1631 /* check for pneumatic hammer things */
1632 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right
1633 for hammer 5) stand on something */
1634 if (player_fire && cave->got_pneumatic_hammer &&
1635 is_space_dir(cave, x, y, player_move) &&
1636 !is_space_dir(cave, x, y, GD_MV_DOWN))
1638 if (player_move == GD_MV_LEFT &&
1639 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1641 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1642 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1643 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1644 break; /* finished. */
1647 if (player_move == GD_MV_RIGHT &&
1648 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1650 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1651 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1652 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1653 break; /* finished. */
1657 if (player_move != GD_MV_STILL)
1659 /* only do every check if he is not moving */
1660 GdElement what = get_dir(cave, x, y, player_move);
1661 GdElement remains = what;
1664 /* if we are 'eating' a teleporter, and the function returns true
1665 (teleporting worked), break here */
1666 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1669 /* try to push element; if successful, break */
1670 push = do_push(cave, x, y, player_move, player_fire);
1677 /* if its a bomb, remember he now has one. */
1678 /* we do not change the "remains" and "what" variables,
1679 so that part of the code will be ineffective */
1680 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1681 store_dir(cave, x, y, player_move, O_SPACE);
1684 store(cave, x, y, O_PLAYER_BOMB);
1686 move(cave, x, y, player_move, O_PLAYER_BOMB);
1690 /* we do not change the "remains" and "what" variables,
1691 so that part of the code will be ineffective */
1692 if (!player_fire && !cave->gravity_switch_active &&
1693 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1695 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1696 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1697 cave->gravity_disabled = TRUE;
1701 case O_GRAVITY_SWITCH:
1702 /* (we cannot use player_get for this as it does not have player_move parameter) */
1703 /* only allow changing direction if the new dir is not diagonal */
1704 if (cave->gravity_switch_active &&
1705 (player_move == GD_MV_LEFT ||
1706 player_move == GD_MV_RIGHT ||
1707 player_move == GD_MV_UP ||
1708 player_move == GD_MV_DOWN))
1710 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1711 cave->gravity_will_change =
1712 cave->gravity_change_time * cave->timing_factor;
1713 cave->gravity_next_direction = player_move;
1714 cave->gravity_switch_active = FALSE;
1719 /* get element - process others.
1720 if cannot get, player_get_element will return the same */
1721 remains = player_get_element (cave, what, x, y);
1725 if (remains != what || remains == O_SPACE)
1727 /* if anything changed, apply the change. */
1729 /* if snapping anything and we have snapping explosions set.
1730 but these is not true for pushing. */
1731 if (remains == O_SPACE && player_fire && !push)
1732 remains = cave->snap_element;
1734 if (remains != O_SPACE || player_fire)
1735 /* if any other element than space, player cannot move.
1736 also if pressing fire, will not move. */
1737 store_dir(cave, x, y, player_move, remains);
1739 /* if space remains there, the player moves. */
1740 move(cave, x, y, player_move, O_PLAYER);
1746 /* much simpler; cannot steal stones */
1747 if (cave->kill_player)
1749 explode(cave, x, y);
1753 cave->player_seen_ago = 0;
1754 /* bd4 intermission caves have many players. so if one of them has exited,
1755 * do not change the flag anymore. so this if () is needed */
1756 if (cave->player_state != GD_PL_EXITED)
1757 cave->player_state = GD_PL_LIVING;
1759 if (player_move != GD_MV_STILL)
1761 /* if the player does not move, nothing to do */
1762 GdElement what = get_dir(cave, x, y, player_move);
1763 GdElement remains = what;
1767 /* placing a bomb into empty space or dirt */
1768 if (is_space_dir(cave, x, y, player_move) ||
1769 is_element_dir(cave, x, y, player_move, O_DIRT))
1771 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1773 /* placed bomb, he is normal player again */
1774 store(cave, x, y, O_PLAYER);
1775 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1780 /* pushing and collecting */
1781 /* if we are 'eating' a teleporter, and the function returns true
1782 (teleporting worked), break here */
1783 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1786 /* player fire is false... */
1787 if (do_push(cave, x, y, player_move, FALSE))
1793 case O_GRAVITY_SWITCH:
1794 /* (we cannot use player_get for this as it does not have
1795 player_move parameter) */
1796 /* only allow changing direction if the new dir is not diagonal */
1797 if (cave->gravity_switch_active &&
1798 (player_move==GD_MV_LEFT ||
1799 player_move==GD_MV_RIGHT ||
1800 player_move==GD_MV_UP ||
1801 player_move==GD_MV_DOWN))
1803 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1804 cave->gravity_will_change =
1805 cave->gravity_change_time * cave->timing_factor;
1806 cave->gravity_next_direction = player_move;
1807 cave->gravity_switch_active = FALSE;
1812 /* get element. if cannot get, player_get_element will return the same */
1813 remains = player_get_element (cave, what, x, y);
1818 /* if element changed, OR there is space, move. */
1819 if (remains != what || remains == O_SPACE)
1821 /* if anything changed, apply the change. */
1822 move(cave, x, y, player_move, O_PLAYER_BOMB);
1827 case O_PLAYER_STIRRING:
1828 if (cave->kill_player)
1830 explode(cave, x, y);
1834 /* stirring sound, if no other walking sound or explosion */
1835 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1837 cave->player_seen_ago = 0;
1838 /* bd4 intermission caves have many players. so if one of them has exited,
1839 * do not change the flag anymore. so this if () is needed */
1840 if (cave->player_state!=GD_PL_EXITED)
1841 cave->player_state = GD_PL_LIVING;
1845 /* player "exits" stirring the pot by pressing fire */
1846 cave->gravity_disabled = FALSE;
1847 store(cave, x, y, O_PLAYER);
1848 cave->gravity_switch_active = TRUE;
1852 /* player holding pneumatic hammer */
1853 case O_PLAYER_PNEUMATIC_LEFT:
1854 case O_PLAYER_PNEUMATIC_RIGHT:
1855 /* usual player stuff */
1856 if (cave->kill_player)
1858 explode(cave, x, y);
1862 cave->player_seen_ago = 0;
1863 if (cave->player_state!=GD_PL_EXITED)
1864 cave->player_state = GD_PL_LIVING;
1866 /* if hammering time is up, becomes a normal player again. */
1867 if (cave->pneumatic_hammer_active_delay == 0)
1868 store(cave, x, y, O_PLAYER);
1871 /* the active pneumatic hammer itself */
1872 case O_PNEUMATIC_ACTIVE_RIGHT:
1873 case O_PNEUMATIC_ACTIVE_LEFT:
1874 if (cave->pneumatic_hammer_active_delay == 0)
1878 /* pneumatic hammer element disappears */
1879 store(cave, x, y, O_SPACE);
1881 /* which is the new element which appears after that one is hammered? */
1882 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1884 /* if there is a new element, display it */
1885 /* O_NONE might be returned, for example if the element being
1886 hammered explodes during hammering (by a nearby explosion) */
1887 if (new_elem != O_NONE)
1889 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1891 /* and if walls reappear, remember it in array */
1892 if (cave->hammered_walls_reappear)
1896 wall_y = (y + 1) % cave->h;
1897 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1904 * S T O N E S, D I A M O N D S
1906 case O_STONE: /* standing stone */
1907 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1910 case O_MEGA_STONE: /* standing mega_stone */
1911 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1914 case O_DIAMOND: /* standing diamond */
1915 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1918 case O_NUT: /* standing nut */
1919 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1922 case O_DIRT_BALL: /* standing dirt ball */
1923 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1926 case O_DIRT_LOOSE: /* standing loose dirt */
1927 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1930 case O_FLYING_STONE: /* standing stone */
1931 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1934 case O_FLYING_DIAMOND: /* standing diamond */
1935 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1939 * 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
1941 case O_DIRT_BALL_F: /* falling dirt ball */
1942 if (!cave->gravity_disabled)
1943 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
1946 case O_DIRT_LOOSE_F: /* falling loose dirt */
1947 if (!cave->gravity_disabled)
1948 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
1951 case O_STONE_F: /* falling stone */
1952 if (!cave->gravity_disabled)
1954 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1957 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
1960 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
1963 if (do_fall_try_crush(cave, x, y, cave->gravity))
1966 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
1970 case O_MEGA_STONE_F: /* falling mega */
1971 if (!cave->gravity_disabled)
1973 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1976 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
1979 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
1982 if (do_fall_try_crush(cave, x, y, cave->gravity))
1985 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
1989 case O_DIAMOND_F: /* falling diamond */
1990 if (!cave->gravity_disabled)
1992 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
1995 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
1998 if (do_fall_try_crush(cave, x, y, cave->gravity))
2001 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2005 case O_NUT_F: /* falling nut */
2006 if (!cave->gravity_disabled)
2008 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2011 if (do_fall_try_crush(cave, x, y, cave->gravity))
2014 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2018 case O_FLYING_STONE_F: /* falling stone */
2019 if (!cave->gravity_disabled)
2021 GdDirection fall_dir = opposite[cave->gravity];
2023 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2026 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2029 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2032 if (do_fall_try_crush(cave, x, y, fall_dir))
2035 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2039 case O_FLYING_DIAMOND_F: /* falling diamond */
2040 if (!cave->gravity_disabled)
2042 GdDirection fall_dir = opposite[cave->gravity];
2044 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2047 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2050 if (do_fall_try_crush(cave, x, y, fall_dir))
2053 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2060 case O_NITRO_PACK: /* standing nitro pack */
2061 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2064 case O_NITRO_PACK_F: /* falling nitro pack */
2065 if (!cave->gravity_disabled)
2067 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
2068 move(cave, x, y, cave->gravity, get(cave, x, y));
2069 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2071 /* try magic wall; if true, function did the work */
2073 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2075 /* falling on a dirt, it does NOT explode - just stops at its place. */
2076 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2077 store(cave, x, y, O_NITRO_PACK);
2080 /* falling on any other element it explodes */
2081 explode(cave, x, y);
2085 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
2086 explode(cave, x, y);
2097 /* if cannot move in any direction, becomes an enclosed cow */
2098 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2099 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2100 store(cave, x, y, O_COW_ENCLOSED_1);
2103 /* THIS IS THE CREATURE MOVE thing copied. */
2104 const GdDirection *creature_move;
2105 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2106 GdElement base; /* base element number (which is like O_***_1) */
2107 int dir, dirn, dirp; /* direction */
2111 dir = get(cave, x, y)-base; /* facing where */
2112 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2114 /* now change direction if backwards */
2115 if (cave->creatures_backwards)
2120 dirn = (dir + 3) & 3; /* fast turn */
2121 dirp = (dir + 1) & 3; /* slow turn */
2125 dirn = (dir + 1) & 3; /* fast turn */
2126 dirp = (dir + 3) & 3; /* slow turn */
2129 if (is_space_dir(cave, x, y, creature_move[dirn]))
2130 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2131 else if (is_space_dir(cave, x, y, creature_move[dir]))
2132 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2134 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2138 /* enclosed cows wait some time before turning to a skeleton */
2139 case O_COW_ENCLOSED_1:
2140 case O_COW_ENCLOSED_2:
2141 case O_COW_ENCLOSED_3:
2142 case O_COW_ENCLOSED_4:
2143 case O_COW_ENCLOSED_5:
2144 case O_COW_ENCLOSED_6:
2145 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2146 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2147 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2148 is_space_dir(cave, x, y, GD_MV_DOWN))
2149 store(cave, x, y, O_COW_1);
2154 case O_COW_ENCLOSED_7:
2155 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2156 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2157 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2158 is_space_dir(cave, x, y, GD_MV_DOWN))
2159 store(cave, x, y, O_COW_1);
2161 store(cave, x, y, O_SKELETON);
2168 case O_ALT_FIREFLY_1:
2169 case O_ALT_FIREFLY_2:
2170 case O_ALT_FIREFLY_3:
2171 case O_ALT_FIREFLY_4:
2176 case O_ALT_BUTTER_1:
2177 case O_ALT_BUTTER_2:
2178 case O_ALT_BUTTER_3:
2179 case O_ALT_BUTTER_4:
2184 /* check if touches a voodoo */
2185 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2186 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2187 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2188 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2189 cave->voodoo_touched = TRUE;
2191 /* check if touches something bad and should explode (includes voodoo by the flags) */
2192 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2193 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2194 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2195 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2196 explode (cave, x, y);
2197 /* otherwise move */
2200 const GdDirection *creature_move;
2201 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2202 GdElement base; /* base element number (which is like O_***_1) */
2203 int dir, dirn, dirp; /* direction */
2205 if (get(cave, x, y) >= O_FIREFLY_1 &&
2206 get(cave, x, y) <= O_FIREFLY_4)
2208 else if (get(cave, x, y) >= O_BUTTER_1 &&
2209 get(cave, x, y) <= O_BUTTER_4)
2211 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2212 get(cave, x, y) <= O_STONEFLY_4)
2213 base = O_STONEFLY_1;
2214 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2215 get(cave, x, y) <= O_ALT_FIREFLY_4)
2216 base = O_ALT_FIREFLY_1;
2217 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2218 get(cave, x, y) <= O_ALT_BUTTER_4)
2219 base = O_ALT_BUTTER_1;
2221 dir = get(cave, x, y)-base; /* facing where */
2222 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2224 /* now change direction if backwards */
2225 if (cave->creatures_backwards)
2230 dirn = (dir + 3) & 3; /* fast turn */
2231 dirp = (dir + 1) & 3; /* slow turn */
2235 dirn = (dir + 1) & 3; /* fast turn */
2236 dirp = (dir + 3) & 3; /* slow turn */
2239 if (is_space_dir(cave, x, y, creature_move[dirn]))
2240 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2241 else if (is_space_dir(cave, x, y, creature_move[dir]))
2242 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2244 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2248 case O_WAITING_STONE:
2249 if (is_space_dir(cave, x, y, grav_compat))
2251 /* beginning to fall */
2253 move(cave, x, y, grav_compat, O_CHASING_STONE);
2255 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2257 /* rolling down a brick wall or a stone */
2258 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2259 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2260 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2262 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2263 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2265 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2266 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2267 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2269 /* or maybe right */
2270 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2275 case O_CHASING_STONE:
2277 int px = cave->px[0];
2278 int py = cave->py[0];
2279 boolean horizontal = g_rand_boolean(cave->random);
2280 boolean dont_move = FALSE;
2283 /* try to move... */
2288 /*********************************/
2289 /* check for a horizontal movement */
2292 /* if coordinates are the same */
2294 horizontal = !horizontal;
2301 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2303 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2307 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2309 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2318 horizontal = !horizontal;
2326 /********************************/
2327 /* check for a vertical movement */
2330 /* if coordinates are the same */
2332 horizontal = !horizontal;
2338 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2340 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2344 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2346 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2355 horizontal = !horizontal;
2368 /* if we should move in both directions, but can not move in any, stop. */
2373 /* check for horizontal */
2376 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2377 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2378 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2379 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2380 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2381 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2385 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2386 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2387 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2388 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2389 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2390 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2395 /* check for vertical */
2398 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2399 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2400 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2401 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2402 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2403 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2407 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2408 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2409 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2410 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2411 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2412 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2420 if (cave->replicators_wait_frame == 0 &&
2421 cave->replicators_active &&
2422 !cave->gravity_disabled)
2424 /* only replicate, if space is under it. */
2425 /* do not replicate players! */
2426 /* also obeys gravity settings. */
2427 /* only replicate element if it is not a scanned one */
2428 /* do not replicate space... that condition looks like it
2429 makes no sense, but otherwise it generates SCANNED spaces,
2430 which cannot be "collected" by the player, so he cannot run
2431 under a replicator */
2432 if (is_space_dir(cave, x, y, cave->gravity) &&
2433 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2434 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2436 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2437 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2446 if (cave->biters_wait_frame == 0)
2448 static GdDirection biter_move[] =
2456 /* direction, last two bits 0..3 */
2457 int dir = get(cave, x, y) - O_BITER_1;
2458 int dirn = (dir + 3) & 3;
2459 int dirp = (dir + 1) & 3;
2461 GdElement made_sound_of = O_NONE;
2463 for (i = 0; i < G_N_ELEMENTS (biter_try); i++)
2465 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2467 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2468 if (biter_try[i] != O_SPACE)
2469 made_sound_of = O_BITER_1; /* sound of a biter eating */
2472 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2474 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2475 if (biter_try[i] != O_SPACE)
2476 made_sound_of = O_BITER_1; /* sound of a biter eating */
2479 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2481 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2482 if (biter_try[i] != O_SPACE)
2483 made_sound_of = O_BITER_1; /* sound of a biter eating */
2488 if (i == G_N_ELEMENTS(biter_try))
2489 /* i = number of elements in array: could not move, so just turn */
2490 store(cave, x, y, O_BITER_1 + dirp);
2491 else if (biter_try[i] == O_STONE)
2493 /* if there was a stone there, where we moved...
2494 do not eat stones, just throw them back */
2495 store(cave, x, y, O_STONE);
2496 made_sound_of = O_STONE;
2499 /* if biter did move, we had sound. play it. */
2500 if (made_sound_of != O_NONE)
2501 play_sound_of_element(cave, made_sound_of, x, y);
2509 /* check if touches a voodoo */
2510 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2511 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2512 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2513 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2514 cave->voodoo_touched = TRUE;
2516 /* check if touches something bad and should explode (includes voodoo by the flags) */
2517 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2518 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2519 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2520 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2521 explode (cave, x, y);
2522 /* otherwise move */
2525 const GdDirection *creature_move;
2526 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2527 GdElement base = O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2528 int dir, dirn; /* direction */
2530 dir = get(cave, x, y)-base; /* facing where */
2531 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2533 /* now change direction if backwards */
2534 if (cave->creatures_backwards)
2538 dirn = (dir + 3) & 3; /* fast turn */
2540 dirn = (dir + 1) & 3; /* fast turn */
2542 /* if can move forward, does so. */
2543 if (is_space_dir(cave, x, y, creature_move[dir]))
2544 move(cave, x, y, creature_move[dir], base + dir);
2546 /* otherwise turns 90 degrees in place. */
2547 store(cave, x, y, base + dirn);
2552 store(cave, x, y, O_BLADDER_1);
2563 /* bladder with any delay state: try to convert to clock. */
2564 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2565 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))
2567 /* if touches the specified element, let it be a clock */
2568 store(cave, x, y, O_PRE_CLOCK_1);
2570 /* plays the bladder convert sound */
2571 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2575 /* is space over the bladder? */
2576 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2578 if (get(cave, x, y)==O_BLADDER_8)
2580 /* if it is a bladder 8, really move up */
2581 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2582 play_sound_of_element(cave, O_BLADDER, x, y);
2585 /* if smaller delay, just increase delay. */
2589 /* if not space, is something sloped over the bladder? */
2590 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2591 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2593 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2594 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2595 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2597 /* rolling up, to left */
2598 if (get(cave, x, y) == O_BLADDER_8)
2600 /* if it is a bladder 8, really roll */
2601 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2602 play_sound_of_element(cave, O_BLADDER, x, y);
2605 /* if smaller delay, just increase delay. */
2608 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2609 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2610 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2612 /* rolling up, to left */
2613 if (get(cave, x, y) == O_BLADDER_8)
2615 /* if it is a bladder 8, really roll */
2616 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2617 play_sound_of_element(cave, O_BLADDER, x, y);
2620 /* if smaller delay, just increase delay. */
2625 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2627 store(cave, x, y, O_BLADDER_1);
2632 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2633 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2634 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2635 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2636 explode (cave, x, y);
2641 /* the ghost is given four possibilities to move. */
2642 for (i = 0; i < 4; i++)
2644 static GdDirection dirs[] =
2651 GdDirection random_dir;
2653 random_dir = dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2654 if (is_space_dir(cave, x, y, random_dir))
2656 move(cave, x, y, random_dir, O_GHOST);
2657 break; /* ghost did move -> exit loop */
2664 * A C T I V E E L E M E N T S
2669 switch (cave->amoeba_state)
2672 store(cave, x, y, cave->amoeba_too_big_effect);
2675 case GD_AM_ENCLOSED:
2676 store(cave, x, y, cave->amoeba_enclosed_effect);
2679 case GD_AM_SLEEPING:
2681 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2682 if (amoeba_found_enclosed)
2683 /* if still found enclosed, check all four directions,
2684 if this one is able to grow. */
2685 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2686 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2687 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2688 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2690 /* not enclosed. this is a local (per scan) flag! */
2691 amoeba_found_enclosed = FALSE;
2692 cave->amoeba_state = GD_AM_AWAKE;
2695 /* if alive, check in which dir to grow (or not) */
2696 if (cave->amoeba_state==GD_AM_AWAKE)
2698 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2700 switch (g_rand_int_range(cave->random, 0, 4))
2702 /* decided to grow, choose a random direction. */
2703 case 0: /* let this be up. numbers indifferent. */
2704 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2705 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2709 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2710 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2714 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2715 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2719 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2720 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2732 /* check if it is touching an amoeba, and explosion is enabled */
2733 if (cave->amoeba_2_explodes_by_amoeba &&
2734 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2735 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2736 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2737 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2738 explode (cave, x, y);
2740 switch (cave->amoeba_2_state)
2743 store(cave, x, y, cave->amoeba_2_too_big_effect);
2746 case GD_AM_ENCLOSED:
2747 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2750 case GD_AM_SLEEPING:
2752 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2753 if (amoeba_2_found_enclosed)
2754 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2755 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2756 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2757 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2759 /* not enclosed. this is a local (per scan) flag! */
2760 amoeba_2_found_enclosed = FALSE;
2761 cave->amoeba_2_state = GD_AM_AWAKE;
2764 /* if it is alive, decide if it attempts to grow */
2765 if (cave->amoeba_2_state == GD_AM_AWAKE)
2766 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2768 switch (g_rand_int_range(cave->random, 0, 4))
2770 /* decided to grow, choose a random direction. */
2771 case 0: /* let this be up. numbers indifferent. */
2772 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2773 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2777 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2778 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2782 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2783 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2787 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2788 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2798 /* choose randomly, if it spreads */
2799 if (g_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2801 /* the current one explodes */
2802 store(cave, x, y, cave->acid_turns_to);
2804 /* and if neighbours are eaten, put acid there. */
2805 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2807 play_sound_of_element(cave, O_ACID, x, y);
2808 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2811 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2813 play_sound_of_element(cave, O_ACID, x, y);
2814 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2817 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2819 play_sound_of_element(cave, O_ACID, x, y);
2820 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2823 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2825 play_sound_of_element(cave, O_ACID, x, y);
2826 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2833 if (!cave->water_does_not_flow_down &&
2834 is_space_dir(cave, x, y, GD_MV_DOWN))
2835 /* emulating the odd behaviour in crdr */
2836 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2838 if (is_space_dir(cave, x, y, GD_MV_UP))
2839 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2841 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2842 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2844 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2845 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2849 store(cave, x, y, O_WATER);
2852 case O_H_EXPANDING_WALL:
2853 case O_V_EXPANDING_WALL:
2854 case O_H_EXPANDING_STEEL_WALL:
2855 case O_V_EXPANDING_STEEL_WALL:
2856 /* checks first if direction is changed. */
2857 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2858 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2859 !cave->expanding_wall_changed) ||
2860 ((get(cave, x, y)==O_V_EXPANDING_WALL ||
2861 get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) &&
2862 cave->expanding_wall_changed))
2864 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2866 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2867 play_sound_of_element(cave, get(cave, x, y), x, y);
2870 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2871 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2872 play_sound_of_element(cave, get(cave, x, y), x, y);
2877 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2878 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2879 play_sound_of_element(cave, get(cave, x, y), x, y);
2882 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2883 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2884 play_sound_of_element(cave, get(cave, x, y), x, y);
2889 case O_EXPANDING_WALL:
2890 case O_EXPANDING_STEEL_WALL:
2891 /* the wall which grows in all four directions. */
2892 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2894 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2895 play_sound_of_element(cave, get(cave, x, y), x, y);
2898 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2899 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2900 play_sound_of_element(cave, get(cave, x, y), x, y);
2903 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2904 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2905 play_sound_of_element(cave, get(cave, x, y), x, y);
2908 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2909 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2910 play_sound_of_element(cave, get(cave, x, y), x, y);
2916 ; // to make compilers happy ...
2918 g_print("Step[%03d]", cave->frame); /* XXX */
2920 int rrr = gd_cave_c64_random(cave);
2923 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2924 (rrr & cave->slime_permeability_c64) == 0);
2927 * unpredictable: g_rand_int
2928 * predictable: c64 predictable random generator.
2929 * for predictable, a random number is generated,
2930 * whether or not it is even possible that the stone will be able to pass.
2932 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : g_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2934 GdDirection grav = cave->gravity;
2935 GdDirection oppos = opposite[cave->gravity];
2937 /* space under the slime? elements may pass from top to bottom then. */
2938 if (is_space_dir(cave, x, y, grav))
2940 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
2942 /* output a falling xy under */
2943 store_dir(cave, x, y, grav, cave->slime_converts_1);
2945 store_dir(cave, x, y, oppos, O_SPACE);
2946 play_sound_of_element(cave, O_SLIME, x, y);
2948 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
2950 store_dir(cave, x, y, grav, cave->slime_converts_2);
2951 store_dir(cave, x, y, oppos, O_SPACE);
2952 play_sound_of_element(cave, O_SLIME, x, y);
2954 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
2956 store_dir(cave, x, y, grav, cave->slime_converts_3);
2957 store_dir(cave, x, y, oppos, O_SPACE);
2958 play_sound_of_element(cave, O_SLIME, x, y);
2960 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
2962 /* waiting stones pass without awakening */
2963 store_dir(cave, x, y, grav, O_WAITING_STONE);
2964 store_dir(cave, x, y, oppos, O_SPACE);
2965 play_sound_of_element(cave, O_SLIME, x, y);
2967 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
2969 /* chasing stones pass */
2970 store_dir(cave, x, y, grav, O_CHASING_STONE);
2971 store_dir(cave, x, y, oppos, O_SPACE);
2972 play_sound_of_element(cave, O_SLIME, x, y);
2976 /* or space over the slime? elements may pass from bottom to up then. */
2977 if (is_space_dir(cave, x, y, oppos))
2979 if (get_dir(cave, x, y, grav) == O_BLADDER)
2981 /* bladders move UP the slime */
2982 store_dir(cave, x, y, grav, O_SPACE);
2983 store_dir(cave, x, y, oppos, O_BLADDER_1);
2984 play_sound_of_element(cave, O_SLIME, x, y);
2986 else if (get_dir(cave, x, y, grav)==O_FLYING_STONE)
2988 store_dir(cave, x, y, grav, O_SPACE);
2989 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
2990 play_sound_of_element(cave, O_SLIME, x, y);
2992 else if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND)
2994 store_dir(cave, x, y, grav, O_SPACE);
2995 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
2996 play_sound_of_element(cave, O_SLIME, x, y);
3002 case O_FALLING_WALL:
3003 if (is_space_dir(cave, x, y, grav_compat))
3005 /* try falling if space under. */
3008 for (yy = y + 1; yy < y + cave->h; yy++)
3009 /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
3010 if (get(cave, x, yy) != O_SPACE)
3011 /* stop cycle when other than space */
3014 /* if scanning stopped by a player... start falling! */
3015 if (get(cave, x, yy) == O_PLAYER ||
3016 get(cave, x, yy) == O_PLAYER_GLUED ||
3017 get(cave, x, yy) == O_PLAYER_BOMB)
3019 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3020 /* no sound when the falling wall starts falling! */
3025 case O_FALLING_WALL_F:
3026 switch (get_dir(cave, x, y, grav_compat))
3029 case O_PLAYER_GLUED:
3031 /* if player under, it explodes - the falling wall, not the player! */
3032 explode(cave, x, y);
3036 /* continue falling */
3037 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3042 play_sound_of_element(cave, get(cave, x, y), x, y);
3043 store(cave, x, y, O_FALLING_WALL);
3049 * C O N V E Y O R B E L T S
3052 case O_CONVEYOR_RIGHT:
3053 case O_CONVEYOR_LEFT:
3054 /* only works if gravity is up or down!!! */
3055 /* first, check for gravity and running belts. */
3056 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3058 const GdDirection *dir;
3061 /* decide direction */
3062 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3063 if (cave->conveyor_belts_direction_changed)
3065 dir = left ? ccw_eighth : cw_eighth;
3067 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
3068 /* if gravity is normal, and the conveyor belt has something
3069 ABOVE which can be moved
3071 the gravity is up, so anything that should float now goes
3072 DOWN and touches the conveyor */
3073 if ((cave->gravity == GD_MV_DOWN &&
3074 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3075 (cave->gravity == GD_MV_UP &&
3076 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3078 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3079 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3081 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); /* move */
3082 store_dir(cave, x, y, GD_MV_UP, O_SPACE); /* and place a space. */
3086 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3087 if ((cave->gravity == GD_MV_UP &&
3088 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3089 (cave->gravity == GD_MV_DOWN &&
3090 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3092 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3093 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3095 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); /* move */
3096 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); /* and clear. */
3103 * S I M P L E C H A N G I N G; E X P L O S I O N S
3107 store(cave, x, y, cave->explosion_effect);
3111 store(cave, x, y, O_DIAMOND);
3115 store(cave, x, y, cave->diamond_birth_effect);
3119 store(cave, x, y, O_STONE);
3122 case O_NITRO_EXPL_4:
3123 store(cave, x, y, cave->nitro_explosion_effect);
3127 store(cave, x, y, cave->bomb_explosion_effect);
3130 case O_AMOEBA_2_EXPL_4:
3131 store(cave, x, y, cave->amoeba_2_explosion_effect);
3134 case O_GHOST_EXPL_4:
3136 static GdElement ghost_explode[] =
3138 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3139 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3140 O_WAITING_STONE, O_BITER_1
3143 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
3148 store(cave, x, y, O_STEEL);
3152 store(cave, x, y, O_CLOCK);
3156 explode(cave, x, y);
3159 case O_TRAPPED_DIAMOND:
3160 if (cave->diamond_key_collected)
3161 store(cave, x, y, O_DIAMOND);
3165 if (cave->gate_open) /* if no more diamonds needed */
3166 store(cave, x, y, O_OUTBOX); /* open outbox */
3169 case O_PRE_INVIS_OUTBOX:
3170 if (cave->gate_open) /* if no more diamonds needed */
3171 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
3175 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
3176 store(cave, x, y, O_PRE_PL_1);
3177 inbox_toggle = !inbox_toggle;
3181 store(cave, x, y, O_PLAYER);
3206 case O_GHOST_EXPL_1:
3207 case O_GHOST_EXPL_2:
3208 case O_GHOST_EXPL_3:
3218 case O_NITRO_EXPL_1:
3219 case O_NITRO_EXPL_2:
3220 case O_NITRO_EXPL_3:
3221 case O_AMOEBA_2_EXPL_1:
3222 case O_AMOEBA_2_EXPL_2:
3223 case O_AMOEBA_2_EXPL_3:
3224 /* simply the next identifier */
3243 found_water = TRUE; /* for sound */
3244 /* simply the next identifier */
3248 case O_BLADDER_SPENDER:
3249 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3251 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3252 store(cave, x, y, O_PRE_STEEL_1);
3253 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3258 /* other inanimate elements that do nothing */
3264 /* POSTPROCESSING */
3266 /* another scan-like routine: */
3267 /* short explosions (for example, in bd1) started with explode_2. */
3268 /* internally we use explode_1; and change it to explode_2 if needed. */
3269 if (cave->short_explosions)
3271 for (y = 0; y < cave->h; y++)
3273 for (x = 0; x < cave->w; x++)
3275 if (is_first_stage_of_explosion(cave, x, y))
3277 next(cave, x, y); /* select next frame of explosion */
3279 /* forget scanned flag immediately */
3280 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3286 /* finally: forget "scanned" flags for objects. */
3287 /* also, check for time penalties. */
3288 /* these is something like an effect table, but we do not really use one. */
3289 for (y = 0; y < cave->h; y++)
3291 for (x = 0; x < cave->w; x++)
3293 if (get(cave, x, y) & SCANNED)
3294 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3296 if (get(cave, x, y) == O_TIME_PENALTY)
3298 store(cave, x, y, O_GRAVESTONE);
3300 /* there is time penalty for destroying the voodoo */
3301 time_decrement_sec += cave->time_penalty;
3306 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3307 /* but we only do this, if a living player was found. if not yet, the setup
3308 routine coordinates are used */
3309 if (cave->player_state==GD_PL_LIVING)
3311 if (cave->active_is_first_found)
3313 /* to be 1stb compatible, we do everything backwards. */
3314 for (y = cave->h - 1; y >= 0; y--)
3316 for (x = cave->w - 1; x >= 0; x--)
3318 if (is_player(cave, x, y))
3320 /* here we remember the coordinates. */
3329 /* as in the original: look for the last one */
3330 for (y = 0; y < cave->h; y++)
3332 for (x = 0; x < cave->w; x++)
3334 if (is_player(cave, x, y))
3336 /* here we remember the coordinates. */
3345 /* record coordinates of player for chasing stone */
3346 for (i = 0; i < G_N_ELEMENTS(cave->px) - 1; i++)
3348 cave->px[i] = cave->px[i + 1];
3349 cave->py[i] = cave->py[i + 1];
3352 cave->px[G_N_ELEMENTS(cave->px) - 1] = cave->player_x;
3353 cave->py[G_N_ELEMENTS(cave->py) - 1] = cave->player_y;
3357 /* update timing calculated by iterating and counting elements */
3358 update_cave_speed(cave);
3360 /* cave 3 sounds. precedence is controlled by the sound_play function. */
3361 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3363 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3365 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3366 cave->magic_wall_sound);
3368 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3369 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3370 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3372 if (amoeba_sound && magic_sound)
3374 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3379 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3380 else if (magic_sound)
3381 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3386 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3387 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3388 play_sound_of_element(cave, O_AMOEBA, x, y);
3391 /* pneumatic hammer sound - overrides everything. */
3392 if (cave->pneumatic_hammer_active_delay > 0)
3393 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3395 /* CAVE VARIABLES */
3399 /* check if player is alive. */
3400 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3401 cave->player_state = GD_PL_DIED;
3403 /* check if any voodoo exploded, and kill players the next scan if that happended. */
3404 if (cave->voodoo_touched)
3405 cave->kill_player = TRUE;
3409 /* check flags after evaluating. */
3410 if (cave->amoeba_state == GD_AM_AWAKE)
3412 if (amoeba_count >= cave->amoeba_max_count)
3413 cave->amoeba_state = GD_AM_TOO_BIG;
3414 if (amoeba_found_enclosed)
3415 cave->amoeba_state = GD_AM_ENCLOSED;
3418 /* amoeba can also be turned into diamond by magic wall */
3419 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3420 cave->amoeba_state = GD_AM_ENCLOSED;
3423 if (cave->amoeba_2_state == GD_AM_AWAKE)
3425 /* check flags after evaluating. */
3426 if (amoeba_2_count >= cave->amoeba_2_max_count)
3427 cave->amoeba_2_state = GD_AM_TOO_BIG;
3429 if (amoeba_2_found_enclosed)
3430 cave->amoeba_2_state = GD_AM_ENCLOSED;
3433 /* amoeba 2 can also be turned into diamond by magic wall */
3434 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3435 cave->amoeba_2_state = GD_AM_ENCLOSED;
3437 /* now check times. --------------------------- */
3438 /* decrement time if a voodoo was killed. */
3439 cave->time -= time_decrement_sec * cave->timing_factor;
3443 /* only decrement time when player is already born. */
3446 int secondsbefore, secondsafter;
3448 secondsbefore = cave->time / cave->timing_factor;
3449 cave->time -= cave->speed;
3450 if (cave->time <= 0)
3453 secondsafter = cave->time / cave->timing_factor;
3454 if (cave->time / cave->timing_factor < 10)
3455 /* if less than 10 seconds, no walking sound, but play explosion sound */
3456 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3458 if (secondsbefore != secondsafter)
3459 gd_cave_set_seconds_sound(cave);
3462 /* a gravity switch was activated; seconds counting down */
3463 if (cave->gravity_will_change > 0)
3465 cave->gravity_will_change -= cave->speed;
3466 if (cave->gravity_will_change < 0)
3467 cave->gravity_will_change = 0;
3469 if (cave->gravity_will_change == 0)
3471 cave->gravity = cave->gravity_next_direction;
3472 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); /* takes precedence over amoeba and magic wall sound */
3476 /* creatures direction automatically change */
3477 if (cave->creatures_direction_will_change > 0)
3479 cave->creatures_direction_will_change -= cave->speed;
3480 if (cave->creatures_direction_will_change < 0)
3481 cave->creatures_direction_will_change = 0;
3483 if (cave->creatures_direction_will_change == 0)
3485 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3487 cave->creatures_backwards = !cave->creatures_backwards;
3488 cave->creatures_direction_will_change =
3489 cave->creatures_direction_auto_change_time * cave->timing_factor;
3493 /* magic wall; if active&wait or not wait for hatching */
3494 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3495 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3497 cave->magic_wall_time -= cave->speed;
3498 if (cave->magic_wall_time < 0)
3499 cave->magic_wall_time = 0;
3500 if (cave->magic_wall_time == 0)
3501 cave->magic_wall_state = GD_MW_EXPIRED;
3504 /* we may wait for hatching, when starting amoeba */
3505 if (cave->amoeba_timer_started_immediately ||
3506 (cave->amoeba_state == GD_AM_AWAKE &&
3507 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3509 cave->amoeba_time -= cave->speed;
3510 if (cave->amoeba_time < 0)
3511 cave->amoeba_time = 0;
3512 if (cave->amoeba_time == 0)
3513 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3516 /* we may wait for hatching, when starting amoeba */
3517 if (cave->amoeba_timer_started_immediately ||
3518 (cave->amoeba_2_state == GD_AM_AWAKE &&
3519 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3521 cave->amoeba_2_time -= cave->speed;
3522 if (cave->amoeba_2_time < 0)
3523 cave->amoeba_2_time = 0;
3524 if (cave->amoeba_2_time == 0)
3525 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3528 /* check for player hatching. */
3529 start_signal = FALSE;
3531 /* if not the c64 scheduling, but the correct frametime is used,
3532 hatching delay should always be decremented. */
3533 /* otherwise, the if (millisecs...) condition below will set this. */
3534 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3536 /* NON-C64 scheduling */
3537 if (cave->hatching_delay_frame > 0)
3539 /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3540 cave->hatching_delay_frame--;
3541 if (cave->hatching_delay_frame == 0)
3542 start_signal = TRUE;
3547 /* C64 scheduling */
3548 if (cave->hatching_delay_time > 0)
3550 /* for c64 schedulings, hatching delay means milliseconds. */
3551 cave->hatching_delay_time -= cave->speed;
3552 if (cave->hatching_delay_time <= 0)
3554 cave->hatching_delay_time = 0;
3555 start_signal = TRUE;
3560 /* if decremented hatching, and it became zero: */
3563 /* THIS IS THE CAVE START SIGNAL */
3565 /* record that now the cave is in its normal state */
3566 cave->hatched = TRUE;
3568 /* if diamonds needed is below zero, we count the available diamonds now. */
3569 gd_cave_count_diamonds(cave);
3571 /* setup direction auto change */
3572 if (cave->creatures_direction_auto_change_time)
3574 cave->creatures_direction_will_change =
3575 cave->creatures_direction_auto_change_time * cave->timing_factor;
3577 if (cave->creatures_direction_auto_change_on_start)
3578 cave->creatures_backwards = !cave->creatures_backwards;
3581 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3585 if (cave->biters_wait_frame == 0)
3586 cave->biters_wait_frame = cave->biter_delay_frame;
3588 cave->biters_wait_frame--;
3590 /* replicators delay */
3591 if (cave->replicators_wait_frame == 0)
3592 cave->replicators_wait_frame = cave->replicator_delay_frame;
3594 cave->replicators_wait_frame--;
3599 /* check if cave failed by timeout is done in main game engine */
3601 /* check if cave failed by timeout */
3602 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3604 gd_cave_clear_sounds(cave);
3605 cave->player_state = GD_PL_TIMEOUT;
3606 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3610 /* set these for drawing. */
3611 cave->last_direction = player_move;
3612 /* here we remember last movements for animation. this is needed here,
3613 as animation is in sync with the game, not the keyboard directly.
3614 (for example, after exiting the cave, the player was "running" in the
3615 original, till bonus points were counted for remaining time and so on. */
3616 if (player_move == GD_MV_LEFT ||
3617 player_move == GD_MV_UP_LEFT ||
3618 player_move == GD_MV_DOWN_LEFT)
3619 cave->last_horizontal_direction = GD_MV_LEFT;
3621 if (player_move == GD_MV_RIGHT ||
3622 player_move == GD_MV_UP_RIGHT ||
3623 player_move == GD_MV_DOWN_RIGHT)
3624 cave->last_horizontal_direction = GD_MV_RIGHT;
3626 cave->frame++; /* XXX */
3629 void set_initial_cave_speed(GdCave *cave)
3634 /* set cave get function; to implement perfect or lineshifting borders */
3635 if (cave->lineshift)
3636 cave->getp = getp_shift;
3638 cave->getp = getp_perfect;
3640 /* check whether to scan the first and last line */
3641 if (cave->border_scan_first_and_last)
3652 for (y = ymin; y <= ymax; y++)
3654 for (x = 0; x < cave->w; x++)
3656 /* add the ckdelay correction value for every element seen. */
3657 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3661 /* update timing calculated by iterating and counting elements */
3662 update_cave_speed(cave);