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. */
139 gd_sound_play(cave, GD_S_NUT, element, x, y);
145 case O_FLYING_STONE_F:
148 case O_WAITING_STONE:
149 case O_CHASING_STONE:
150 gd_sound_play(cave, GD_S_STONE, element, x, y);
155 gd_sound_play(cave, GD_S_NITRO_PACK, element, x, y);
159 case O_FALLING_WALL_F:
160 gd_sound_play(cave, GD_S_FALLING_WALL, element, x, y);
163 case O_H_EXPANDING_WALL:
164 case O_V_EXPANDING_WALL:
165 case O_EXPANDING_WALL:
166 case O_H_EXPANDING_STEEL_WALL:
167 case O_V_EXPANDING_STEEL_WALL:
168 case O_EXPANDING_STEEL_WALL:
169 gd_sound_play(cave, GD_S_EXPANDING_WALL, element, x, y);
174 case O_FLYING_DIAMOND:
175 case O_FLYING_DIAMOND_F:
176 gd_sound_play(cave, GD_S_DIAMOND_RANDOM, element, x, y);
179 case O_BLADDER_SPENDER:
180 gd_sound_play(cave, GD_S_BLADDER_SPENDER, element, x, y);
184 gd_sound_play(cave, GD_S_BLADDER_CONVERTING, element, x, y);
188 gd_sound_play(cave, GD_S_SLIME, element, x, y);
192 gd_sound_play(cave, GD_S_LAVA, element, x, y);
196 gd_sound_play(cave, GD_S_ACID_SPREADING, element, x, y);
200 gd_sound_play(cave, GD_S_BLADDER_MOVING, element, x, y);
207 gd_sound_play(cave, GD_S_BITER_EATING, element, x, y);
214 gd_sound_play(cave, GD_S_DIRT_BALL, element, x, y);
223 static inline GdElement *getp(const GdCave *cave, const int x, const int y)
225 return cave->getp(cave, x, y);
229 perfect (non-lineshifting) GET function.
230 returns a pointer to a selected cave element by its coordinates.
232 static inline GdElement *getp_perfect(const GdCave *cave, const int x, const int y)
234 /* (x + n) mod n: this works also for x >= n and -n + 1 < x < 0 */
235 return &(cave->map[(y + cave->h) % cave->h][(x + cave->w) % cave->w]);
239 line shifting GET function; returns a pointer to the selected cave element.
240 this is used to emulate the line-shifting behaviour of original games, so that
241 the player entering one side will appear one row above or below on the other.
243 static inline GdElement *getp_shift(const GdCave *cave, int x, int y)
256 y = (y + cave->h) % cave->h;
258 return &(cave->map[y][x]);
261 static inline GdElement get(const GdCave *cave, const int x, const int y)
263 return *getp(cave, x, y);
266 /* returns an element which is somewhere near x,y */
267 static inline GdElement get_dir(const GdCave *cave, const int x, const int y,
268 const GdDirection dir)
270 return get(cave, x + gd_dx[dir], y + gd_dy[dir]);
273 static inline boolean explodes_by_hit_dir(const GdCave *cave, const int x,
274 const int y, GdDirection dir)
276 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_EXPLODES_BY_HIT) != 0;
279 /* returns true if the element is not explodable, for example the steel wall */
280 static inline boolean non_explodable(const GdCave *cave, const int x, const int y)
282 return (gd_elements[get(cave, x,y) & O_MASK].properties & P_NON_EXPLODABLE) != 0;
285 /* returns true if the element can be eaten by the amoeba, eg. space and dirt. */
286 static inline boolean amoeba_eats_dir(const GdCave *cave, const int x, const int y,
287 const GdDirection dir)
289 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_AMOEBA_CONSUMES) != 0;
292 /* returns true if the element is sloped, so stones and diamonds roll down on it.
293 for example a stone or brick wall */
294 static inline boolean sloped_dir(const GdCave *cave, const int x, const int y,
295 const GdDirection dir, const GdDirection slop)
300 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_LEFT) != 0;
303 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_RIGHT) != 0;
306 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_UP) != 0;
309 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_DOWN) != 0;
318 /* returns true if the element is sloped for bladder movement
319 (brick = yes, diamond = no, for example) */
320 static inline boolean sloped_for_bladder_dir (const GdCave *cave, const int x, const int y,
321 const GdDirection dir)
323 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLADDER_SLOPED) != 0;
326 static inline boolean blows_up_flies_dir(const GdCave *cave, const int x, const int y,
327 const GdDirection dir)
329 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLOWS_UP_FLIES) != 0;
332 /* returns true if the element is a counter-clockwise creature */
333 static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
335 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_CCW) != 0;
338 /* returns true if the element is a player */
339 static inline boolean is_player(const GdCave *cave, const int x, const int y)
341 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
344 /* returns true if the element is a player */
345 static inline boolean is_player_dir(const GdCave *cave, const int x, const int y,
346 const GdDirection dir)
348 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PLAYER) != 0;
351 static inline boolean can_be_hammered_dir(const GdCave *cave, const int x, const int y,
352 const GdDirection dir)
354 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
357 /* returns true if the element is explodable and explodes to space, for example the player */
358 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
360 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
363 /* returns true if the element is moved by the conveyor belt */
364 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
365 const GdDirection dir)
367 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
370 /* returns true if the element is moved by the conveyor belt */
371 static inline boolean moved_by_conveyor_bottom_dir(const GdCave *cave, const int x, const int y,
372 const GdDirection dir)
374 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_BOTTOM) != 0;
377 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
378 const GdDirection dir)
380 return (get_dir(cave, x, y, dir) & SCANNED) != 0;
383 /* returns true if neighbouring element is "e" */
384 /* treats dirt specially */
385 /* treats lava specially */
386 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
387 const GdDirection dir, GdElement e)
389 GdElement examined = get_dir(cave, x, y, dir);
391 /* if it is a dirt-like, change to dirt, so equality will evaluate to true */
392 if (gd_elements[examined & O_MASK].properties & P_DIRT)
395 if (gd_elements[e & O_MASK].properties & P_DIRT)
398 /* if the element on the map is a lava, it should be like space */
399 if (examined == O_LAVA)
402 return (e == examined);
405 /* returns true if neighbouring element is space */
406 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
407 const GdDirection dir)
409 GdElement e = get_dir(cave, x, y, dir)&O_MASK;
411 return (e == O_SPACE || e == O_LAVA);
414 /* store an element at the given position */
415 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
417 GdElement *e = getp(cave, x, y);
421 play_sound_of_element(cave, O_LAVA, x, y);
429 /* store an element with SCANNED flag turned on */
430 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
432 store(cave, x, y, element | SCANNED);
435 /* store an element to a neighbouring cell */
436 static inline void store_dir(GdCave *cave, const int x, const int y,
437 const GdDirection dir, const GdElement element)
439 store(cave, x + gd_dx[dir], y + gd_dy[dir], element|SCANNED);
442 /* store an element to a neighbouring cell */
443 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
444 const GdDirection dir, const GdElement element)
446 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
449 /* move element to direction; then place space at x, y */
450 static inline void move(GdCave *cave, const int x, const int y,
451 const GdDirection dir, const GdElement e)
453 store_dir(cave, x, y, dir, e);
454 store(cave, x, y, O_SPACE);
457 /* increment a cave element; can be used for elements which are one after
458 the other, for example bladder1, bladder2, bladder3... */
459 static inline void next(GdCave *cave, const int x, const int y)
461 (*getp(cave, x, y))++;
464 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
466 if (non_explodable (cave, x, y))
469 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
470 cave->voodoo_touched = TRUE;
472 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
473 /* voodoo turns into a time penalty */
474 store_sc(cave, x, y, O_TIME_PENALTY);
475 else if (get(cave, x, y) == O_NITRO_PACK ||
476 get(cave, x, y) == O_NITRO_PACK_F)
477 /* nitro pack inside an explosion - it is now triggered */
478 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
480 /* for everything else */
481 store_sc(cave, x, y, explode_to);
484 /* a creature explodes to a 3x3 something. */
485 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
489 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
490 cave->ckdelay += 1200;
491 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
493 for (yy = y - 1; yy <= y + 1; yy++)
494 for (xx = x - 1; xx <= x + 1; xx++)
495 cell_explode(cave, xx, yy, explode_to);
498 static void nitro_explode(GdCave *cave, int x, int y)
502 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
503 cave->ckdelay += 1200;
504 gd_sound_play(cave, GD_S_NITRO_PACK_EXPLODING, get(cave, x, y), x, y);
506 for (yy = y - 1; yy <= y + 1; yy++)
507 for (xx = x - 1; xx <= x + 1; xx++)
508 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
510 /* the current cell is explicitly changed into a nitro expl,
511 as cell_explode changes it to a triggered nitro pack */
512 store_sc(cave, x, y, O_NITRO_EXPL_1);
515 /* a voodoo explodes, leaving a 3x3 steel and a time penalty behind. */
516 static void voodoo_explode(GdCave *cave, int x, int y)
520 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
521 cave->ckdelay += 1000;
523 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
524 if (cave->voodoo_any_hurt_kills_player)
525 cave->voodoo_touched = TRUE;
527 /* voodoo explodes to 3x3 steel */
528 for (yy = y - 1; yy <= y + 1; yy++)
529 for (xx = x - 1; xx <= x + 1; xx++)
530 store_sc(cave, xx, yy, O_PRE_STEEL_1);
532 /* middle is a time penalty (which will be turned into a gravestone) */
533 store_sc(cave, x, y, O_TIME_PENALTY);
536 /* a bomb does not explode the voodoo, neither does the ghost.
537 this function check this, and stores the new element or not.
538 destroying the voodoo is also controlled by the
539 voodoo_disappear_in_explosion flag. */
540 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
542 if (non_explodable (cave, x, y))
545 /* bomb does not explode voodoo */
546 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
549 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
550 cave->voodoo_touched = TRUE;
552 store_sc (cave, x, y, expl);
555 /* X shaped ghost explosion; does not touch voodoo! */
556 static void ghost_explode(GdCave *cave, const int x, const int y)
558 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
560 /* the processing of an explosion took pretty much time: processing 5 elements */
561 cave->ckdelay += 650;
563 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
564 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
565 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
566 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
567 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
570 /* +shaped bomb explosion; does not touch voodoo! */
571 static void bomb_explode(GdCave *cave, const int x, const int y)
573 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
575 /* the processing of an explosion took pretty much time: processing 5 elements */
576 cave->ckdelay += 650;
578 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
579 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
580 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
581 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
582 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
586 explode an element with the appropriate type of exlposion.
588 static void explode(GdCave *cave, int x, int y)
590 GdElement e = get(cave, x, y) & O_MASK;
595 ghost_explode(cave, x, y);
599 bomb_explode(cave, x, y);
603 voodoo_explode(cave, x, y);
608 case O_NITRO_PACK_EXPLODE:
609 nitro_explode(cave, x, y);
613 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
616 case O_FALLING_WALL_F:
617 creature_explode(cave, x, y, O_EXPLODE_1);
624 creature_explode(cave, x, y, cave->butterfly_explode_to);
631 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
638 creature_explode(cave, x, y, cave->firefly_explode_to);
641 case O_ALT_FIREFLY_1:
642 case O_ALT_FIREFLY_2:
643 case O_ALT_FIREFLY_3:
644 case O_ALT_FIREFLY_4:
645 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
651 case O_PLAYER_STIRRING:
652 case O_PLAYER_PNEUMATIC_LEFT:
653 case O_PLAYER_PNEUMATIC_RIGHT:
654 creature_explode(cave, x, y, O_EXPLODE_1);
661 creature_explode(cave, x, y, cave->stonefly_explode_to);
668 creature_explode(cave, x, y, cave->dragonfly_explode_to);
676 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
678 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
682 player eats specified object.
683 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
684 returns other element if something other appears there and he can't move.
685 cave pointer is needed to know the diamond values.
687 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
694 cave->diamond_key_collected = TRUE;
695 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
700 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
705 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
710 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
717 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
724 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
731 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
736 case O_CREATURE_SWITCH: /* creatures change direction. */
737 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
738 cave->creatures_backwards = !cave->creatures_backwards;
741 case O_EXPANDING_WALL_SWITCH: /* expanding wall change direction. */
742 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
743 cave->expanding_wall_changed = !cave->expanding_wall_changed;
746 case O_BITER_SWITCH: /* biter change delay */
747 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
748 cave->biter_delay_frame++;
749 if (cave->biter_delay_frame == 4)
750 cave->biter_delay_frame = 0;
753 case O_REPLICATOR_SWITCH: /* replicator on/off switch */
754 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
755 cave->replicators_active = !cave->replicators_active;
758 case O_CONVEYOR_SWITCH: /* conveyor belts on/off */
759 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
760 cave->conveyor_belts_active = !cave->conveyor_belts_active;
763 case O_CONVEYOR_DIR_SWITCH: /* conveyor belts switch direction */
764 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
765 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
771 case O_STEEL_EATABLE:
772 case O_BRICK_EATABLE:
773 case O_DIRT_SLOPED_UP_RIGHT:
774 case O_DIRT_SLOPED_UP_LEFT:
775 case O_DIRT_SLOPED_DOWN_LEFT:
776 case O_DIRT_SLOPED_DOWN_RIGHT:
779 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
783 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
784 cave->sweet_eaten = TRUE;
787 case O_PNEUMATIC_HAMMER:
788 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
789 cave->got_pneumatic_hammer = TRUE;
794 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
795 cave->time += cave->time_bonus * cave->timing_factor;
796 if (cave->time > cave->max_time * cave->timing_factor)
797 cave->time -= cave->max_time * cave->timing_factor;
798 /* no space, rather a dirt remains there... */
802 case O_FLYING_DIAMOND:
803 // prevent diamond sounds for O_SKELETON (see below)
804 if (x != -1 && y != -1)
805 gd_sound_play(cave, GD_S_DIAMOND_COLLECTING, object, x, y);
807 cave->score += cave->diamond_value;
808 cave->diamonds_collected++;
810 if (cave->diamonds_needed == cave->diamonds_collected)
812 cave->gate_open = TRUE;
814 /* extra is worth more points. */
815 cave->diamond_value = cave->extra_diamond_value;
817 cave->gate_open_flash = 1;
818 cave->sound3 = GD_S_CRACKING;
819 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
824 cave->skeletons_collected++;
826 /* as if player got a diamond */
827 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
828 player_get_element(cave, O_DIAMOND, -1, -1);
830 /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
831 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
836 cave->player_state = GD_PL_EXITED; /* player now exits the cave! */
840 case O_LAVA: /* player goes into lava, as if it was space */
841 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
845 /* the object will remain there. */
851 process a crazy dream-style teleporter.
852 called from gd_cave_iterate, for a player or a player_bomb.
853 player is standing at px, py, and trying to move in the direction player_move,
854 where there is a teleporter.
855 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
856 around). the first teleporter we find, and which is suitable, will be the destination.
857 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
859 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
868 /* jump to next element; wrap around columns and rows. */
880 /* if we found a teleporter... */
881 if (get(cave, tx, ty) == O_TELEPORTER &&
882 is_space_dir(cave, tx, ty, player_move))
884 /* new player appears near teleporter found */
885 store_dir(cave, tx, ty, player_move, get(cave, px, py));
887 /* current player disappears */
888 store(cave, px, py, O_SPACE);
890 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
892 return TRUE; /* return true as teleporter worked */
895 /* loop until we get back to original coordinates */
896 while (tx != px || ty != py);
898 /* return false as we did not find any usable teleporter */
903 try to push an element.
904 returns true if the push is possible; also does move the specified _element_.
905 up to the caller to move the _player_itself_.
907 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
910 GdElement what = get_dir(cave, x, y, player_move);
912 /* gravity for falling wall, bladder, ... */
913 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
919 case O_WAITING_STONE:
922 case O_CHASING_STONE:
926 /* pushing some kind of stone or nut */
927 /* directions possible: 90degrees cw or ccw to current gravity. */
928 /* only push if player dir is orthogonal to gravity,
929 ie. gravity down, pushing left & right possible */
930 if (player_move == ccw_fourth[cave->gravity] ||
931 player_move == cw_fourth[cave->gravity])
937 /* different probabilities for different elements. */
940 case O_WAITING_STONE:
941 /* waiting stones are light, can always push */
945 case O_CHASING_STONE:
946 /* chasing can be pushed if player is turbo */
947 if (cave->sweet_eaten)
952 /* mega may(!) be pushed if player is turbo */
953 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
961 if (cave->sweet_eaten)
962 prob = cave->pushing_stone_prob_sweet; /* probability with sweet */
964 prob = cave->pushing_stone_prob; /* probability without sweet. */
971 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
972 g_rand_int_range(cave->random, 0, 1000000) < prob)
974 /* if decided that he will be able to push, */
975 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
976 play_sound_of_element(cave, what, x, y);
991 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
992 * not an O_BLADDER_x. */
993 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
995 /* first check: we cannot push a bladder "up" */
996 if (player_move != opposite[grav_compat])
998 /* pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check. */
999 /* player moving in the direction of gravity. */
1003 if (player_move == grav_compat)
1005 /* pushing bladder down */
1006 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1007 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1008 /* if no space to push down, maybe left (down-left to player) */
1009 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1011 /* left is "down, turned right (cw)" */
1012 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1013 /* if not, maybe right (down-right to player) */
1014 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1015 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1018 /* pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check. */
1022 else if (player_move == cw_fourth[grav_compat])
1024 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) /* pushing it left */
1025 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1026 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) /* maybe down, and player will move left */
1027 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1028 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) /* maybe up, and player will move left */
1029 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1032 /* pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check. */
1036 else if (player_move == ccw_fourth[grav_compat])
1038 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) /* pushing it right */
1039 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1040 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) /* maybe down, and player will move right */
1041 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1042 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) /* maybe up, and player will move right */
1043 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1047 play_sound_of_element(cave, O_BLADDER, x, y);
1052 /* a box is only pushed with the fire pressed */
1055 /* but always with 100% probability */
1056 switch (player_move)
1062 /* pushing in some dir, two steps in that dir - is there space? */
1063 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1066 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1068 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1073 /* push in no other directions possible */
1079 /* pushing of other elements not possible */
1087 /* from the key press booleans, create a direction */
1088 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1090 GdDirection player_move;
1092 /* from the key press booleans, create a direction */
1094 player_move = GD_MV_UP_RIGHT;
1095 else if (down && right)
1096 player_move = GD_MV_DOWN_RIGHT;
1097 else if (down && left)
1098 player_move = GD_MV_DOWN_LEFT;
1099 else if (up && left)
1100 player_move = GD_MV_UP_LEFT;
1102 player_move = GD_MV_UP;
1104 player_move = GD_MV_DOWN;
1106 player_move = GD_MV_LEFT;
1108 player_move = GD_MV_RIGHT;
1110 player_move = GD_MV_STILL;
1115 /* clear these to no sound; and they will be set during iteration. */
1116 void gd_cave_clear_sounds(GdCave *cave)
1118 cave->sound1 = GD_S_NONE;
1119 cave->sound2 = GD_S_NONE;
1120 cave->sound3 = GD_S_NONE;
1123 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1124 GdElement falling_element)
1126 if (cave->gravity_disabled)
1129 if (is_space_dir(cave, x, y, falling_direction))
1131 /* beginning to fall */
1132 play_sound_of_element(cave, get(cave, x, y), x, y);
1133 move(cave, x, y, falling_direction, falling_element);
1136 /* check if it is on a sloped element, and it can roll. */
1137 /* for example, sloped wall looks like: */
1140 /* this is tagged as sloped up&left. */
1141 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1142 /* then check the direction to roll (left or right) */
1143 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1144 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1146 /* rolling down, if sitting on a sloped object */
1147 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1148 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1149 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1151 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1152 so here we use cw_fourth */
1153 play_sound_of_element(cave, get(cave, x, y), x, y);
1154 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1156 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1157 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1158 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1160 /* rolling right? */
1161 play_sound_of_element(cave, get(cave, x, y), x, y);
1162 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1167 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1169 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1170 cave->voodoo_dies_by_stone)
1172 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1173 explode_dir(cave, x, y, fall_dir);
1180 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1182 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1183 cave->voodoo_collects_diamonds)
1185 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1186 player_get_element(cave, O_DIAMOND, x, y); /* as if player got diamond */
1187 store(cave, x, y, O_SPACE); /* diamond disappears */
1194 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1195 GdDirection fall_dir, GdElement bouncing)
1197 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1198 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1201 store(cave, x, y, bouncing);
1202 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1204 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1212 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1213 GdDirection fall_dir, GdElement magic)
1215 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1217 play_sound_of_element(cave, O_DIAMOND, x, y); /* always play diamond sound */
1219 if (cave->magic_wall_state==GD_MW_DORMANT)
1220 cave->magic_wall_state = GD_MW_ACTIVE;
1222 if (cave->magic_wall_state==GD_MW_ACTIVE &&
1223 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1225 /* if magic wall active and place underneath, it turns element
1226 into anything the effect says to do. */
1227 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1230 /* active or non-active or anything, element falling in will always disappear */
1231 store(cave, x, y, O_SPACE);
1239 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1241 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1243 explode_dir(cave, x, y, fall_dir);
1250 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1251 GdDirection fall_dir, GdElement bouncing)
1253 if (is_space_dir(cave, x, y, fall_dir))
1255 /* falling further */
1256 move(cave, x, y, fall_dir, get(cave, x, y));
1261 /* check if it is on a sloped element, and it can roll. */
1262 /* for example, sloped wall looks like: */
1265 /* this is tagged as sloped up&left. */
1266 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1267 /* then check the direction to roll (left or right) */
1268 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1270 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1272 /* sloped element, falling to left or right */
1273 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1274 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1275 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1277 play_sound_of_element(cave, get(cave, x, y), x, y);
1279 /* try to roll left first - see O_STONE to understand why cw_fourth */
1280 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1282 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1283 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1284 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1286 play_sound_of_element(cave, get(cave, x, y), x, y);
1288 /* if not, try to roll right */
1289 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1293 /* cannot roll in any direction, so it stops */
1294 play_sound_of_element(cave, get(cave, x, y), x, y);
1295 store(cave, x, y, bouncing);
1301 /* any other element, stops */
1302 play_sound_of_element(cave, get(cave, x, y), x, y);
1303 store(cave, x, y, bouncing);
1307 static void update_cave_speed(GdCave *cave)
1309 /* update timing calculated by iterating and counting elements which were slow to process on c64 */
1310 switch (cave->scheduling)
1312 case GD_SCHEDULING_MILLISECONDS:
1313 /* cave->speed already contains the milliseconds value, do not touch it */
1316 case GD_SCHEDULING_BD1:
1317 if (!cave->intermission)
1318 /* non-intermissions */
1319 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1321 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
1322 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1325 case GD_SCHEDULING_BD1_ATARI:
1326 /* about 20ms/frame faster than c64 version */
1327 if (!cave->intermission)
1328 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); /* non-intermissions */
1330 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); /* for intermissions */
1333 case GD_SCHEDULING_BD2:
1334 /* 60 is a guess. */
1335 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1338 case GD_SCHEDULING_PLCK:
1339 /* 65 is totally empty cave in construction kit, with delay = 0) */
1340 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1343 case GD_SCHEDULING_BD2_PLCK_ATARI:
1344 /* a really fast engine; timing works like c64 plck. */
1345 /* 40 ms was measured in the construction kit, with delay = 0 */
1346 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1349 case GD_SCHEDULING_CRDR:
1350 if (cave->hammered_walls_reappear) /* this made the engine very slow. */
1351 cave->ckdelay += 60000;
1352 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1355 case GD_SCHEDULING_MAX:
1360 /* process a cave. */
1361 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1365 /* for border scan */
1368 /* amoeba found to be enclosed. if not, this is cleared */
1369 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1371 /* counting the number of amoebas. after scan, check if too much */
1372 int amoeba_count, amoeba_2_count;
1374 /* cave scan found water - for sound */
1375 boolean found_water;
1377 boolean inbox_toggle;
1378 boolean start_signal;
1380 /* gravity for falling wall, bladder, ... */
1381 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1383 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1384 static const GdDirection creature_dir[] =
1391 static const GdDirection creature_chdir[] =
1398 int time_decrement_sec;
1400 /* biters eating elements preference, they try to go in this order */
1401 GdElement biter_try[] =
1408 boolean amoeba_sound, magic_sound;
1410 gd_cave_clear_sounds(cave);
1412 /* if diagonal movements not allowed, */
1413 /* horizontal movements have precedence. [BROADRIBB] */
1414 if (!cave->diagonal_movements)
1416 switch (player_move)
1418 case GD_MV_UP_RIGHT:
1419 case GD_MV_DOWN_RIGHT:
1420 player_move = GD_MV_RIGHT;
1424 case GD_MV_DOWN_LEFT:
1425 player_move = GD_MV_LEFT;
1429 /* no correction needed */
1434 /* set cave get function; to implement perfect or lineshifting borders */
1435 if (cave->lineshift)
1436 cave->getp = getp_shift;
1438 cave->getp = getp_perfect;
1440 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1441 if (cave->player_seen_ago < 100)
1442 cave->player_seen_ago++;
1444 if (cave->pneumatic_hammer_active_delay > 0)
1445 cave->pneumatic_hammer_active_delay--;
1447 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1448 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1449 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1450 inbox_toggle = cave->inbox_flash_toggle;
1452 if (cave->gate_open_flash > 0)
1453 cave->gate_open_flash--;
1455 /* score collected this frame */
1458 /* suicide only kills the active player */
1459 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1460 /* we must check if there is a player or not - he may have exploded or something like that */
1461 if (suicide && cave->player_state == GD_PL_LIVING &&
1462 is_player(cave, cave->player_x, cave->player_y))
1463 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1465 /* check for walls reappearing */
1466 if (cave->hammered_reappear)
1468 for (y = 0; y < cave->h; y++)
1470 for (x = 0; x < cave->w; x++)
1472 /* timer for the cell > 0? */
1473 if (cave->hammered_reappear[y][x]>0)
1475 /* decrease timer */
1476 cave->hammered_reappear[y][x]--;
1478 /* check if it became zero */
1479 if (cave->hammered_reappear[y][x] == 0)
1481 store(cave, x, y, O_BRICK);
1482 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1489 /* variables to check during the scan */
1491 /* will be set to false if any of the amoeba is found free. */
1492 amoeba_found_enclosed = TRUE;
1493 amoeba_2_found_enclosed = TRUE;
1496 found_water = FALSE;
1498 time_decrement_sec = 0;
1500 /* check whether to scan the first and last line */
1501 if (cave->border_scan_first_and_last)
1512 /* the cave scan routine */
1513 for (y = ymin; y <= ymax; y++)
1515 for (x = 0; x < cave->w; x++)
1517 /* if we find a scanned element, change it to the normal one, and that's all. */
1518 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1519 if (get(cave, x, y)&SCANNED)
1521 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1526 /* add the ckdelay correction value for every element seen. */
1527 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1529 switch (get(cave, x, y))
1535 if (cave->kill_player)
1537 explode (cave, x, y);
1541 cave->player_seen_ago = 0;
1542 /* bd4 intermission caves have many players. so if one of them has exited,
1543 * do not change the flag anymore. so this if () is needed */
1544 if (cave->player_state!=GD_PL_EXITED)
1545 cave->player_state = GD_PL_LIVING;
1547 /* check for pneumatic hammer things */
1548 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right
1549 for hammer 5) stand on something */
1550 if (player_fire && cave->got_pneumatic_hammer &&
1551 is_space_dir(cave, x, y, player_move) &&
1552 !is_space_dir(cave, x, y, GD_MV_DOWN))
1554 if (player_move == GD_MV_LEFT &&
1555 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1557 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1558 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1559 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1560 break; /* finished. */
1563 if (player_move == GD_MV_RIGHT &&
1564 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1566 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1567 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1568 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1569 break; /* finished. */
1573 if (player_move != GD_MV_STILL)
1575 /* only do every check if he is not moving */
1576 GdElement what = get_dir(cave, x, y, player_move);
1577 GdElement remains = what;
1580 /* if we are 'eating' a teleporter, and the function returns true
1581 (teleporting worked), break here */
1582 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1585 /* try to push element; if successful, break */
1586 push = do_push(cave, x, y, player_move, player_fire);
1593 /* if its a bomb, remember he now has one. */
1594 /* we do not change the "remains" and "what" variables,
1595 so that part of the code will be ineffective */
1596 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1597 store_dir(cave, x, y, player_move, O_SPACE);
1600 store(cave, x, y, O_PLAYER_BOMB);
1602 move(cave, x, y, player_move, O_PLAYER_BOMB);
1606 /* we do not change the "remains" and "what" variables,
1607 so that part of the code will be ineffective */
1608 if (!player_fire && !cave->gravity_switch_active &&
1609 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1611 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1612 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1613 cave->gravity_disabled = TRUE;
1617 case O_GRAVITY_SWITCH:
1618 /* (we cannot use player_get for this as it does not have player_move parameter) */
1619 /* only allow changing direction if the new dir is not diagonal */
1620 if (cave->gravity_switch_active &&
1621 (player_move == GD_MV_LEFT ||
1622 player_move == GD_MV_RIGHT ||
1623 player_move == GD_MV_UP ||
1624 player_move == GD_MV_DOWN))
1626 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1627 cave->gravity_will_change =
1628 cave->gravity_change_time * cave->timing_factor;
1629 cave->gravity_next_direction = player_move;
1630 cave->gravity_switch_active = FALSE;
1635 /* get element - process others.
1636 if cannot get, player_get_element will return the same */
1637 remains = player_get_element (cave, what, x, y);
1641 if (remains != what || remains == O_SPACE)
1643 /* if anything changed, apply the change. */
1645 /* if snapping anything and we have snapping explosions set.
1646 but these is not true for pushing. */
1647 if (remains == O_SPACE && player_fire && !push)
1648 remains = cave->snap_element;
1650 if (remains != O_SPACE || player_fire)
1651 /* if any other element than space, player cannot move.
1652 also if pressing fire, will not move. */
1653 store_dir(cave, x, y, player_move, remains);
1655 /* if space remains there, the player moves. */
1656 move(cave, x, y, player_move, O_PLAYER);
1662 /* much simpler; cannot steal stones */
1663 if (cave->kill_player)
1665 explode(cave, x, y);
1669 cave->player_seen_ago = 0;
1670 /* bd4 intermission caves have many players. so if one of them has exited,
1671 * do not change the flag anymore. so this if () is needed */
1672 if (cave->player_state != GD_PL_EXITED)
1673 cave->player_state = GD_PL_LIVING;
1675 if (player_move != GD_MV_STILL)
1677 /* if the player does not move, nothing to do */
1678 GdElement what = get_dir(cave, x, y, player_move);
1679 GdElement remains = what;
1683 /* placing a bomb into empty space or dirt */
1684 if (is_space_dir(cave, x, y, player_move) ||
1685 is_element_dir(cave, x, y, player_move, O_DIRT))
1687 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1689 /* placed bomb, he is normal player again */
1690 store(cave, x, y, O_PLAYER);
1691 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1696 /* pushing and collecting */
1697 /* if we are 'eating' a teleporter, and the function returns true
1698 (teleporting worked), break here */
1699 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1702 /* player fire is false... */
1703 if (do_push(cave, x, y, player_move, FALSE))
1709 case O_GRAVITY_SWITCH:
1710 /* (we cannot use player_get for this as it does not have
1711 player_move parameter) */
1712 /* only allow changing direction if the new dir is not diagonal */
1713 if (cave->gravity_switch_active &&
1714 (player_move==GD_MV_LEFT ||
1715 player_move==GD_MV_RIGHT ||
1716 player_move==GD_MV_UP ||
1717 player_move==GD_MV_DOWN))
1719 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1720 cave->gravity_will_change =
1721 cave->gravity_change_time * cave->timing_factor;
1722 cave->gravity_next_direction = player_move;
1723 cave->gravity_switch_active = FALSE;
1728 /* get element. if cannot get, player_get_element will return the same */
1729 remains = player_get_element (cave, what, x, y);
1734 /* if element changed, OR there is space, move. */
1735 if (remains != what || remains == O_SPACE)
1737 /* if anything changed, apply the change. */
1738 move(cave, x, y, player_move, O_PLAYER_BOMB);
1743 case O_PLAYER_STIRRING:
1744 if (cave->kill_player)
1746 explode(cave, x, y);
1750 /* stirring sound, if no other walking sound or explosion */
1751 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, 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;
1761 /* player "exits" stirring the pot by pressing fire */
1762 cave->gravity_disabled = FALSE;
1763 store(cave, x, y, O_PLAYER);
1764 cave->gravity_switch_active = TRUE;
1768 /* player holding pneumatic hammer */
1769 case O_PLAYER_PNEUMATIC_LEFT:
1770 case O_PLAYER_PNEUMATIC_RIGHT:
1771 /* usual player stuff */
1772 if (cave->kill_player)
1774 explode(cave, x, y);
1778 cave->player_seen_ago = 0;
1779 if (cave->player_state!=GD_PL_EXITED)
1780 cave->player_state = GD_PL_LIVING;
1782 /* if hammering time is up, becomes a normal player again. */
1783 if (cave->pneumatic_hammer_active_delay == 0)
1784 store(cave, x, y, O_PLAYER);
1787 /* the active pneumatic hammer itself */
1788 case O_PNEUMATIC_ACTIVE_RIGHT:
1789 case O_PNEUMATIC_ACTIVE_LEFT:
1790 if (cave->pneumatic_hammer_active_delay == 0)
1794 /* pneumatic hammer element disappears */
1795 store(cave, x, y, O_SPACE);
1797 /* which is the new element which appears after that one is hammered? */
1798 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1800 /* if there is a new element, display it */
1801 /* O_NONE might be returned, for example if the element being
1802 hammered explodes during hammering (by a nearby explosion) */
1803 if (new_elem != O_NONE)
1805 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1807 /* and if walls reappear, remember it in array */
1808 if (cave->hammered_walls_reappear)
1812 wall_y = (y + 1) % cave->h;
1813 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1820 * S T O N E S, D I A M O N D S
1822 case O_STONE: /* standing stone */
1823 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1826 case O_MEGA_STONE: /* standing mega_stone */
1827 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1830 case O_DIAMOND: /* standing diamond */
1831 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1834 case O_NUT: /* standing nut */
1835 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1838 case O_DIRT_BALL: /* standing dirt ball */
1839 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1842 case O_DIRT_LOOSE: /* standing loose dirt */
1843 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1846 case O_FLYING_STONE: /* standing stone */
1847 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1850 case O_FLYING_DIAMOND: /* standing diamond */
1851 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1855 * 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
1857 case O_DIRT_BALL_F: /* falling dirt ball */
1858 if (!cave->gravity_disabled)
1859 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
1862 case O_DIRT_LOOSE_F: /* falling loose dirt */
1863 if (!cave->gravity_disabled)
1864 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
1867 case O_STONE_F: /* falling stone */
1868 if (!cave->gravity_disabled)
1870 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1873 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
1876 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
1879 if (do_fall_try_crush(cave, x, y, cave->gravity))
1882 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
1886 case O_MEGA_STONE_F: /* falling mega */
1887 if (!cave->gravity_disabled)
1889 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1892 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
1895 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
1898 if (do_fall_try_crush(cave, x, y, cave->gravity))
1901 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
1905 case O_DIAMOND_F: /* falling diamond */
1906 if (!cave->gravity_disabled)
1908 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
1911 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
1914 if (do_fall_try_crush(cave, x, y, cave->gravity))
1917 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
1921 case O_NUT_F: /* falling nut */
1922 if (!cave->gravity_disabled)
1924 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
1927 if (do_fall_try_crush(cave, x, y, cave->gravity))
1930 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
1934 case O_FLYING_STONE_F: /* falling stone */
1935 if (!cave->gravity_disabled)
1937 GdDirection fall_dir = opposite[cave->gravity];
1939 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
1942 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
1945 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
1948 if (do_fall_try_crush(cave, x, y, fall_dir))
1951 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
1955 case O_FLYING_DIAMOND_F: /* falling diamond */
1956 if (!cave->gravity_disabled)
1958 GdDirection fall_dir = opposite[cave->gravity];
1960 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
1963 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
1966 if (do_fall_try_crush(cave, x, y, fall_dir))
1969 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
1976 case O_NITRO_PACK: /* standing nitro pack */
1977 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
1980 case O_NITRO_PACK_F: /* falling nitro pack */
1981 if (!cave->gravity_disabled)
1983 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
1984 move(cave, x, y, cave->gravity, get(cave, x, y));
1985 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
1987 /* try magic wall; if true, function did the work */
1989 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
1991 /* falling on a dirt, it does NOT explode - just stops at its place. */
1992 play_sound_of_element(cave, O_NITRO_PACK, x, y);
1993 store(cave, x, y, O_NITRO_PACK);
1996 /* falling on any other element it explodes */
1997 explode(cave, x, y);
2001 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
2002 explode(cave, x, y);
2013 /* if cannot move in any direction, becomes an enclosed cow */
2014 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2015 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2016 store(cave, x, y, O_COW_ENCLOSED_1);
2019 /* THIS IS THE CREATURE MOVE thing copied. */
2020 const GdDirection *creature_move;
2021 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2022 GdElement base; /* base element number (which is like O_***_1) */
2023 int dir, dirn, dirp; /* direction */
2027 dir = get(cave, x, y)-base; /* facing where */
2028 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2030 /* now change direction if backwards */
2031 if (cave->creatures_backwards)
2036 dirn = (dir + 3) & 3; /* fast turn */
2037 dirp = (dir + 1) & 3; /* slow turn */
2041 dirn = (dir + 1) & 3; /* fast turn */
2042 dirp = (dir + 3) & 3; /* slow turn */
2045 if (is_space_dir(cave, x, y, creature_move[dirn]))
2046 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2047 else if (is_space_dir(cave, x, y, creature_move[dir]))
2048 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2050 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2054 /* enclosed cows wait some time before turning to a skeleton */
2055 case O_COW_ENCLOSED_1:
2056 case O_COW_ENCLOSED_2:
2057 case O_COW_ENCLOSED_3:
2058 case O_COW_ENCLOSED_4:
2059 case O_COW_ENCLOSED_5:
2060 case O_COW_ENCLOSED_6:
2061 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2062 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2063 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2064 is_space_dir(cave, x, y, GD_MV_DOWN))
2065 store(cave, x, y, O_COW_1);
2070 case O_COW_ENCLOSED_7:
2071 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2072 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2073 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2074 is_space_dir(cave, x, y, GD_MV_DOWN))
2075 store(cave, x, y, O_COW_1);
2077 store(cave, x, y, O_SKELETON);
2084 case O_ALT_FIREFLY_1:
2085 case O_ALT_FIREFLY_2:
2086 case O_ALT_FIREFLY_3:
2087 case O_ALT_FIREFLY_4:
2092 case O_ALT_BUTTER_1:
2093 case O_ALT_BUTTER_2:
2094 case O_ALT_BUTTER_3:
2095 case O_ALT_BUTTER_4:
2100 /* check if touches a voodoo */
2101 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2102 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2103 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2104 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2105 cave->voodoo_touched = TRUE;
2107 /* check if touches something bad and should explode (includes voodoo by the flags) */
2108 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2109 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2110 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2111 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2112 explode (cave, x, y);
2113 /* otherwise move */
2116 const GdDirection *creature_move;
2117 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2118 GdElement base; /* base element number (which is like O_***_1) */
2119 int dir, dirn, dirp; /* direction */
2121 if (get(cave, x, y) >= O_FIREFLY_1 &&
2122 get(cave, x, y) <= O_FIREFLY_4)
2124 else if (get(cave, x, y) >= O_BUTTER_1 &&
2125 get(cave, x, y) <= O_BUTTER_4)
2127 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2128 get(cave, x, y) <= O_STONEFLY_4)
2129 base = O_STONEFLY_1;
2130 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2131 get(cave, x, y) <= O_ALT_FIREFLY_4)
2132 base = O_ALT_FIREFLY_1;
2133 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2134 get(cave, x, y) <= O_ALT_BUTTER_4)
2135 base = O_ALT_BUTTER_1;
2137 dir = get(cave, x, y)-base; /* facing where */
2138 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2140 /* now change direction if backwards */
2141 if (cave->creatures_backwards)
2146 dirn = (dir + 3) & 3; /* fast turn */
2147 dirp = (dir + 1) & 3; /* slow turn */
2151 dirn = (dir + 1) & 3; /* fast turn */
2152 dirp = (dir + 3) & 3; /* slow turn */
2155 if (is_space_dir(cave, x, y, creature_move[dirn]))
2156 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2157 else if (is_space_dir(cave, x, y, creature_move[dir]))
2158 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2160 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2164 case O_WAITING_STONE:
2165 if (is_space_dir(cave, x, y, grav_compat))
2167 /* beginning to fall */
2169 move(cave, x, y, grav_compat, O_CHASING_STONE);
2171 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2173 /* rolling down a brick wall or a stone */
2174 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2175 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2176 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2178 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2179 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2181 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2182 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2183 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2185 /* or maybe right */
2186 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2191 case O_CHASING_STONE:
2193 int px = cave->px[0];
2194 int py = cave->py[0];
2195 boolean horizontal = g_rand_boolean(cave->random);
2196 boolean dont_move = FALSE;
2199 /* try to move... */
2204 /*********************************/
2205 /* check for a horizontal movement */
2208 /* if coordinates are the same */
2210 horizontal = !horizontal;
2217 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2219 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2223 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2225 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2234 horizontal = !horizontal;
2242 /********************************/
2243 /* check for a vertical movement */
2246 /* if coordinates are the same */
2248 horizontal = !horizontal;
2254 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2256 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2260 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2262 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2271 horizontal = !horizontal;
2284 /* if we should move in both directions, but can not move in any, stop. */
2289 /* check for horizontal */
2292 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2293 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2294 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2295 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2296 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2297 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2301 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2302 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2303 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2304 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2305 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2306 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2311 /* check for vertical */
2314 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2315 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2316 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2317 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2318 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2319 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2323 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2324 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2325 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2326 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2327 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2328 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2336 if (cave->replicators_wait_frame == 0 &&
2337 cave->replicators_active &&
2338 !cave->gravity_disabled)
2340 /* only replicate, if space is under it. */
2341 /* do not replicate players! */
2342 /* also obeys gravity settings. */
2343 /* only replicate element if it is not a scanned one */
2344 /* do not replicate space... that condition looks like it
2345 makes no sense, but otherwise it generates SCANNED spaces,
2346 which cannot be "collected" by the player, so he cannot run
2347 under a replicator */
2348 if (is_space_dir(cave, x, y, cave->gravity) &&
2349 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2350 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2352 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2353 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2362 if (cave->biters_wait_frame == 0)
2364 static GdDirection biter_move[] =
2372 /* direction, last two bits 0..3 */
2373 int dir = get(cave, x, y) - O_BITER_1;
2374 int dirn = (dir + 3) & 3;
2375 int dirp = (dir + 1) & 3;
2377 GdElement made_sound_of = O_NONE;
2379 for (i = 0; i < G_N_ELEMENTS (biter_try); i++)
2381 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2383 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2384 if (biter_try[i] != O_SPACE)
2385 made_sound_of = O_BITER_1; /* sound of a biter eating */
2388 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2390 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2391 if (biter_try[i] != O_SPACE)
2392 made_sound_of = O_BITER_1; /* sound of a biter eating */
2395 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2397 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2398 if (biter_try[i] != O_SPACE)
2399 made_sound_of = O_BITER_1; /* sound of a biter eating */
2404 if (i == G_N_ELEMENTS(biter_try))
2405 /* i = number of elements in array: could not move, so just turn */
2406 store(cave, x, y, O_BITER_1 + dirp);
2407 else if (biter_try[i] == O_STONE)
2409 /* if there was a stone there, where we moved...
2410 do not eat stones, just throw them back */
2411 store(cave, x, y, O_STONE);
2412 made_sound_of = O_STONE;
2415 /* if biter did move, we had sound. play it. */
2416 if (made_sound_of != O_NONE)
2417 play_sound_of_element(cave, made_sound_of, x, y);
2425 /* check if touches a voodoo */
2426 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2427 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2428 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2429 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2430 cave->voodoo_touched = TRUE;
2432 /* check if touches something bad and should explode (includes voodoo by the flags) */
2433 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2434 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2435 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2436 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2437 explode (cave, x, y);
2438 /* otherwise move */
2441 const GdDirection *creature_move;
2442 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2443 GdElement base = O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2444 int dir, dirn; /* direction */
2446 dir = get(cave, x, y)-base; /* facing where */
2447 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2449 /* now change direction if backwards */
2450 if (cave->creatures_backwards)
2454 dirn = (dir + 3) & 3; /* fast turn */
2456 dirn = (dir + 1) & 3; /* fast turn */
2458 /* if can move forward, does so. */
2459 if (is_space_dir(cave, x, y, creature_move[dir]))
2460 move(cave, x, y, creature_move[dir], base + dir);
2462 /* otherwise turns 90 degrees in place. */
2463 store(cave, x, y, base + dirn);
2468 store(cave, x, y, O_BLADDER_1);
2479 /* bladder with any delay state: try to convert to clock. */
2480 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2481 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))
2483 /* if touches the specified element, let it be a clock */
2484 store(cave, x, y, O_PRE_CLOCK_1);
2486 /* plays the bladder convert sound */
2487 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2491 /* is space over the bladder? */
2492 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2494 if (get(cave, x, y)==O_BLADDER_8)
2496 /* if it is a bladder 8, really move up */
2497 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2498 play_sound_of_element(cave, O_BLADDER, x, y);
2501 /* if smaller delay, just increase delay. */
2505 /* if not space, is something sloped over the bladder? */
2506 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2507 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2509 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2510 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2511 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2513 /* rolling up, to left */
2514 if (get(cave, x, y) == O_BLADDER_8)
2516 /* if it is a bladder 8, really roll */
2517 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2518 play_sound_of_element(cave, O_BLADDER, x, y);
2521 /* if smaller delay, just increase delay. */
2524 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2525 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2526 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2528 /* rolling up, to left */
2529 if (get(cave, x, y) == O_BLADDER_8)
2531 /* if it is a bladder 8, really roll */
2532 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2533 play_sound_of_element(cave, O_BLADDER, x, y);
2536 /* if smaller delay, just increase delay. */
2541 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2543 store(cave, x, y, O_BLADDER_1);
2548 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2549 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2550 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2551 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2552 explode (cave, x, y);
2557 /* the ghost is given four possibilities to move. */
2558 for (i = 0; i < 4; i++)
2560 static GdDirection dirs[] =
2567 GdDirection random_dir;
2569 random_dir = dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2570 if (is_space_dir(cave, x, y, random_dir))
2572 move(cave, x, y, random_dir, O_GHOST);
2573 break; /* ghost did move -> exit loop */
2580 * A C T I V E E L E M E N T S
2585 switch (cave->amoeba_state)
2588 store(cave, x, y, cave->amoeba_too_big_effect);
2591 case GD_AM_ENCLOSED:
2592 store(cave, x, y, cave->amoeba_enclosed_effect);
2595 case GD_AM_SLEEPING:
2597 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2598 if (amoeba_found_enclosed)
2599 /* if still found enclosed, check all four directions,
2600 if this one is able to grow. */
2601 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2602 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2603 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2604 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2606 /* not enclosed. this is a local (per scan) flag! */
2607 amoeba_found_enclosed = FALSE;
2608 cave->amoeba_state = GD_AM_AWAKE;
2611 /* if alive, check in which dir to grow (or not) */
2612 if (cave->amoeba_state==GD_AM_AWAKE)
2614 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2616 switch (g_rand_int_range(cave->random, 0, 4))
2618 /* decided to grow, choose a random direction. */
2619 case 0: /* let this be up. numbers indifferent. */
2620 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2621 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2625 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2626 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2630 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2631 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2635 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2636 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2648 /* check if it is touching an amoeba, and explosion is enabled */
2649 if (cave->amoeba_2_explodes_by_amoeba &&
2650 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2651 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2652 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2653 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2654 explode (cave, x, y);
2656 switch (cave->amoeba_2_state)
2659 store(cave, x, y, cave->amoeba_2_too_big_effect);
2662 case GD_AM_ENCLOSED:
2663 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2666 case GD_AM_SLEEPING:
2668 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2669 if (amoeba_2_found_enclosed)
2670 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2671 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2672 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2673 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2675 /* not enclosed. this is a local (per scan) flag! */
2676 amoeba_2_found_enclosed = FALSE;
2677 cave->amoeba_2_state = GD_AM_AWAKE;
2680 /* if it is alive, decide if it attempts to grow */
2681 if (cave->amoeba_2_state == GD_AM_AWAKE)
2682 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2684 switch (g_rand_int_range(cave->random, 0, 4))
2686 /* decided to grow, choose a random direction. */
2687 case 0: /* let this be up. numbers indifferent. */
2688 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2689 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2693 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2694 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2698 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2699 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2703 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2704 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2714 /* choose randomly, if it spreads */
2715 if (g_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2717 /* the current one explodes */
2718 store(cave, x, y, cave->acid_turns_to);
2720 /* and if neighbours are eaten, put acid there. */
2721 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2723 play_sound_of_element(cave, O_ACID, x, y);
2724 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2727 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2729 play_sound_of_element(cave, O_ACID, x, y);
2730 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2733 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2735 play_sound_of_element(cave, O_ACID, x, y);
2736 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2739 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2741 play_sound_of_element(cave, O_ACID, x, y);
2742 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2749 if (!cave->water_does_not_flow_down &&
2750 is_space_dir(cave, x, y, GD_MV_DOWN))
2751 /* emulating the odd behaviour in crdr */
2752 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2754 if (is_space_dir(cave, x, y, GD_MV_UP))
2755 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2757 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2758 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2760 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2761 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2765 store(cave, x, y, O_WATER);
2768 case O_H_EXPANDING_WALL:
2769 case O_V_EXPANDING_WALL:
2770 case O_H_EXPANDING_STEEL_WALL:
2771 case O_V_EXPANDING_STEEL_WALL:
2772 /* checks first if direction is changed. */
2773 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2774 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2775 !cave->expanding_wall_changed) ||
2776 ((get(cave, x, y)==O_V_EXPANDING_WALL ||
2777 get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) &&
2778 cave->expanding_wall_changed))
2780 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2782 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2783 play_sound_of_element(cave, get(cave, x, y), x, y);
2786 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2787 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2788 play_sound_of_element(cave, get(cave, x, y), x, y);
2793 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2794 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2795 play_sound_of_element(cave, get(cave, x, y), x, y);
2798 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2799 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2800 play_sound_of_element(cave, get(cave, x, y), x, y);
2805 case O_EXPANDING_WALL:
2806 case O_EXPANDING_STEEL_WALL:
2807 /* the wall which grows in all four directions. */
2808 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2810 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2811 play_sound_of_element(cave, get(cave, x, y), x, y);
2814 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2815 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2816 play_sound_of_element(cave, get(cave, x, y), x, y);
2819 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2820 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2821 play_sound_of_element(cave, get(cave, x, y), x, y);
2824 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2825 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2826 play_sound_of_element(cave, get(cave, x, y), x, y);
2832 ; // to make compilers happy ...
2834 g_print("Step[%03d]", cave->frame); /* XXX */
2836 int rrr = gd_cave_c64_random(cave);
2839 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2840 (rrr & cave->slime_permeability_c64) == 0);
2843 * unpredictable: g_rand_int
2844 * predictable: c64 predictable random generator.
2845 * for predictable, a random number is generated,
2846 * whether or not it is even possible that the stone will be able to pass.
2848 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : g_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2850 GdDirection grav = cave->gravity;
2851 GdDirection oppos = opposite[cave->gravity];
2853 /* space under the slime? elements may pass from top to bottom then. */
2854 if (is_space_dir(cave, x, y, grav))
2856 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
2858 /* output a falling xy under */
2859 store_dir(cave, x, y, grav, cave->slime_converts_1);
2861 store_dir(cave, x, y, oppos, O_SPACE);
2862 play_sound_of_element(cave, O_SLIME, x, y);
2864 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
2866 store_dir(cave, x, y, grav, cave->slime_converts_2);
2867 store_dir(cave, x, y, oppos, O_SPACE);
2868 play_sound_of_element(cave, O_SLIME, x, y);
2870 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
2872 store_dir(cave, x, y, grav, cave->slime_converts_3);
2873 store_dir(cave, x, y, oppos, O_SPACE);
2874 play_sound_of_element(cave, O_SLIME, x, y);
2876 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
2878 /* waiting stones pass without awakening */
2879 store_dir(cave, x, y, grav, O_WAITING_STONE);
2880 store_dir(cave, x, y, oppos, O_SPACE);
2881 play_sound_of_element(cave, O_SLIME, x, y);
2883 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
2885 /* chasing stones pass */
2886 store_dir(cave, x, y, grav, O_CHASING_STONE);
2887 store_dir(cave, x, y, oppos, O_SPACE);
2888 play_sound_of_element(cave, O_SLIME, x, y);
2892 /* or space over the slime? elements may pass from bottom to up then. */
2893 if (is_space_dir(cave, x, y, oppos))
2895 if (get_dir(cave, x, y, grav) == O_BLADDER)
2897 /* bladders move UP the slime */
2898 store_dir(cave, x, y, grav, O_SPACE);
2899 store_dir(cave, x, y, oppos, O_BLADDER_1);
2900 play_sound_of_element(cave, O_SLIME, x, y);
2902 else if (get_dir(cave, x, y, grav)==O_FLYING_STONE)
2904 store_dir(cave, x, y, grav, O_SPACE);
2905 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
2906 play_sound_of_element(cave, O_SLIME, x, y);
2908 else if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND)
2910 store_dir(cave, x, y, grav, O_SPACE);
2911 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
2912 play_sound_of_element(cave, O_SLIME, x, y);
2918 case O_FALLING_WALL:
2919 if (is_space_dir(cave, x, y, grav_compat))
2921 /* try falling if space under. */
2924 for (yy = y + 1; yy < y + cave->h; yy++)
2925 /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
2926 if (get(cave, x, yy) != O_SPACE)
2927 /* stop cycle when other than space */
2930 /* if scanning stopped by a player... start falling! */
2931 if (get(cave, x, yy) == O_PLAYER ||
2932 get(cave, x, yy) == O_PLAYER_GLUED ||
2933 get(cave, x, yy) == O_PLAYER_BOMB)
2935 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2936 /* no sound when the falling wall starts falling! */
2941 case O_FALLING_WALL_F:
2942 switch (get_dir(cave, x, y, grav_compat))
2945 case O_PLAYER_GLUED:
2947 /* if player under, it explodes - the falling wall, not the player! */
2948 explode(cave, x, y);
2952 /* continue falling */
2953 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2958 play_sound_of_element(cave, get(cave, x, y), x, y);
2959 store(cave, x, y, O_FALLING_WALL);
2965 * C O N V E Y O R B E L T S
2968 case O_CONVEYOR_RIGHT:
2969 case O_CONVEYOR_LEFT:
2970 /* only works if gravity is up or down!!! */
2971 /* first, check for gravity and running belts. */
2972 if (!cave->gravity_disabled && cave->conveyor_belts_active)
2974 const GdDirection *dir;
2977 /* decide direction */
2978 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
2979 if (cave->conveyor_belts_direction_changed)
2981 dir = left ? ccw_eighth : cw_eighth;
2983 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
2984 /* if gravity is normal, and the conveyor belt has something
2985 ABOVE which can be moved
2987 the gravity is up, so anything that should float now goes
2988 DOWN and touches the conveyor */
2989 if ((cave->gravity == GD_MV_DOWN &&
2990 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
2991 (cave->gravity == GD_MV_UP &&
2992 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
2994 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
2995 is_space_dir(cave, x, y, dir[GD_MV_UP]))
2997 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); /* move */
2998 store_dir(cave, x, y, GD_MV_UP, O_SPACE); /* and place a space. */
3002 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3003 if ((cave->gravity == GD_MV_UP &&
3004 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3005 (cave->gravity == GD_MV_DOWN &&
3006 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3008 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3009 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3011 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); /* move */
3012 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); /* and clear. */
3019 * S I M P L E C H A N G I N G; E X P L O S I O N S
3023 store(cave, x, y, cave->explosion_effect);
3027 store(cave, x, y, O_DIAMOND);
3031 store(cave, x, y, cave->diamond_birth_effect);
3035 store(cave, x, y, O_STONE);
3038 case O_NITRO_EXPL_4:
3039 store(cave, x, y, cave->nitro_explosion_effect);
3043 store(cave, x, y, cave->bomb_explosion_effect);
3046 case O_AMOEBA_2_EXPL_4:
3047 store(cave, x, y, cave->amoeba_2_explosion_effect);
3050 case O_GHOST_EXPL_4:
3052 static GdElement ghost_explode[] =
3054 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3055 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3056 O_WAITING_STONE, O_BITER_1
3059 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
3064 store(cave, x, y, O_STEEL);
3068 store(cave, x, y, O_CLOCK);
3072 explode(cave, x, y);
3075 case O_TRAPPED_DIAMOND:
3076 if (cave->diamond_key_collected)
3077 store(cave, x, y, O_DIAMOND);
3081 if (cave->gate_open) /* if no more diamonds needed */
3082 store(cave, x, y, O_OUTBOX); /* open outbox */
3085 case O_PRE_INVIS_OUTBOX:
3086 if (cave->gate_open) /* if no more diamonds needed */
3087 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
3091 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
3092 store(cave, x, y, O_PRE_PL_1);
3093 inbox_toggle = !inbox_toggle;
3097 store(cave, x, y, O_PLAYER);
3122 case O_GHOST_EXPL_1:
3123 case O_GHOST_EXPL_2:
3124 case O_GHOST_EXPL_3:
3134 case O_NITRO_EXPL_1:
3135 case O_NITRO_EXPL_2:
3136 case O_NITRO_EXPL_3:
3137 case O_AMOEBA_2_EXPL_1:
3138 case O_AMOEBA_2_EXPL_2:
3139 case O_AMOEBA_2_EXPL_3:
3140 /* simply the next identifier */
3159 found_water = TRUE; /* for sound */
3160 /* simply the next identifier */
3164 case O_BLADDER_SPENDER:
3165 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3167 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3168 store(cave, x, y, O_PRE_STEEL_1);
3169 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3174 /* other inanimate elements that do nothing */
3180 /* POSTPROCESSING */
3182 /* another scan-like routine: */
3183 /* short explosions (for example, in bd1) started with explode_2. */
3184 /* internally we use explode_1; and change it to explode_2 if needed. */
3185 if (cave->short_explosions)
3187 for (y = 0; y < cave->h; y++)
3189 for (x = 0; x < cave->w; x++)
3191 if (is_first_stage_of_explosion(cave, x, y))
3193 next(cave, x, y); /* select next frame of explosion */
3195 /* forget scanned flag immediately */
3196 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3202 /* finally: forget "scanned" flags for objects. */
3203 /* also, check for time penalties. */
3204 /* these is something like an effect table, but we do not really use one. */
3205 for (y = 0; y < cave->h; y++)
3207 for (x = 0; x < cave->w; x++)
3209 if (get(cave, x, y) & SCANNED)
3210 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3212 if (get(cave, x, y) == O_TIME_PENALTY)
3214 store(cave, x, y, O_GRAVESTONE);
3216 /* there is time penalty for destroying the voodoo */
3217 time_decrement_sec += cave->time_penalty;
3222 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3223 /* but we only do this, if a living player was found. if not yet, the setup
3224 routine coordinates are used */
3225 if (cave->player_state==GD_PL_LIVING)
3227 if (cave->active_is_first_found)
3229 /* to be 1stb compatible, we do everything backwards. */
3230 for (y = cave->h - 1; y >= 0; y--)
3232 for (x = cave->w - 1; x >= 0; x--)
3234 if (is_player(cave, x, y))
3236 /* here we remember the coordinates. */
3245 /* as in the original: look for the last one */
3246 for (y = 0; y < cave->h; y++)
3248 for (x = 0; x < cave->w; x++)
3250 if (is_player(cave, x, y))
3252 /* here we remember the coordinates. */
3261 /* record coordinates of player for chasing stone */
3262 for (i = 0; i < G_N_ELEMENTS(cave->px) - 1; i++)
3264 cave->px[i] = cave->px[i + 1];
3265 cave->py[i] = cave->py[i + 1];
3268 cave->px[G_N_ELEMENTS(cave->px) - 1] = cave->player_x;
3269 cave->py[G_N_ELEMENTS(cave->py) - 1] = cave->player_y;
3273 /* update timing calculated by iterating and counting elements */
3274 update_cave_speed(cave);
3276 /* cave 3 sounds. precedence is controlled by the sound_play function. */
3277 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3279 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3281 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3282 cave->magic_wall_sound);
3284 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3285 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3286 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3288 if (amoeba_sound && magic_sound)
3290 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3295 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3296 else if (magic_sound)
3297 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3302 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3303 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3304 play_sound_of_element(cave, O_AMOEBA, x, y);
3307 /* pneumatic hammer sound - overrides everything. */
3308 if (cave->pneumatic_hammer_active_delay > 0)
3309 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3311 /* CAVE VARIABLES */
3315 /* check if player is alive. */
3316 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3317 cave->player_state = GD_PL_DIED;
3319 /* check if any voodoo exploded, and kill players the next scan if that happended. */
3320 if (cave->voodoo_touched)
3321 cave->kill_player = TRUE;
3325 /* check flags after evaluating. */
3326 if (cave->amoeba_state == GD_AM_AWAKE)
3328 if (amoeba_count >= cave->amoeba_max_count)
3329 cave->amoeba_state = GD_AM_TOO_BIG;
3330 if (amoeba_found_enclosed)
3331 cave->amoeba_state = GD_AM_ENCLOSED;
3334 /* amoeba can also be turned into diamond by magic wall */
3335 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3336 cave->amoeba_state = GD_AM_ENCLOSED;
3339 if (cave->amoeba_2_state == GD_AM_AWAKE)
3341 /* check flags after evaluating. */
3342 if (amoeba_2_count >= cave->amoeba_2_max_count)
3343 cave->amoeba_2_state = GD_AM_TOO_BIG;
3345 if (amoeba_2_found_enclosed)
3346 cave->amoeba_2_state = GD_AM_ENCLOSED;
3349 /* amoeba 2 can also be turned into diamond by magic wall */
3350 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3351 cave->amoeba_2_state = GD_AM_ENCLOSED;
3353 /* now check times. --------------------------- */
3354 /* decrement time if a voodoo was killed. */
3355 cave->time -= time_decrement_sec * cave->timing_factor;
3359 /* only decrement time when player is already born. */
3362 int secondsbefore, secondsafter;
3364 secondsbefore = cave->time / cave->timing_factor;
3365 cave->time -= cave->speed;
3366 if (cave->time <= 0)
3369 secondsafter = cave->time / cave->timing_factor;
3370 if (cave->time / cave->timing_factor < 10)
3371 /* if less than 10 seconds, no walking sound, but play explosion sound */
3372 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3374 if (secondsbefore != secondsafter)
3375 gd_cave_set_seconds_sound(cave);
3378 /* a gravity switch was activated; seconds counting down */
3379 if (cave->gravity_will_change > 0)
3381 cave->gravity_will_change -= cave->speed;
3382 if (cave->gravity_will_change < 0)
3383 cave->gravity_will_change = 0;
3385 if (cave->gravity_will_change == 0)
3387 cave->gravity = cave->gravity_next_direction;
3388 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); /* takes precedence over amoeba and magic wall sound */
3392 /* creatures direction automatically change */
3393 if (cave->creatures_direction_will_change > 0)
3395 cave->creatures_direction_will_change -= cave->speed;
3396 if (cave->creatures_direction_will_change < 0)
3397 cave->creatures_direction_will_change = 0;
3399 if (cave->creatures_direction_will_change == 0)
3401 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3403 cave->creatures_backwards = !cave->creatures_backwards;
3404 cave->creatures_direction_will_change =
3405 cave->creatures_direction_auto_change_time * cave->timing_factor;
3409 /* magic wall; if active&wait or not wait for hatching */
3410 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3411 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3413 cave->magic_wall_time -= cave->speed;
3414 if (cave->magic_wall_time < 0)
3415 cave->magic_wall_time = 0;
3416 if (cave->magic_wall_time == 0)
3417 cave->magic_wall_state = GD_MW_EXPIRED;
3420 /* we may wait for hatching, when starting amoeba */
3421 if (cave->amoeba_timer_started_immediately ||
3422 (cave->amoeba_state == GD_AM_AWAKE &&
3423 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3425 cave->amoeba_time -= cave->speed;
3426 if (cave->amoeba_time < 0)
3427 cave->amoeba_time = 0;
3428 if (cave->amoeba_time == 0)
3429 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3432 /* we may wait for hatching, when starting amoeba */
3433 if (cave->amoeba_timer_started_immediately ||
3434 (cave->amoeba_2_state == GD_AM_AWAKE &&
3435 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3437 cave->amoeba_2_time -= cave->speed;
3438 if (cave->amoeba_2_time < 0)
3439 cave->amoeba_2_time = 0;
3440 if (cave->amoeba_2_time == 0)
3441 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3444 /* check for player hatching. */
3445 start_signal = FALSE;
3447 /* if not the c64 scheduling, but the correct frametime is used,
3448 hatching delay should always be decremented. */
3449 /* otherwise, the if (millisecs...) condition below will set this. */
3450 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3452 /* NON-C64 scheduling */
3453 if (cave->hatching_delay_frame > 0)
3455 /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3456 cave->hatching_delay_frame--;
3457 if (cave->hatching_delay_frame == 0)
3458 start_signal = TRUE;
3463 /* C64 scheduling */
3464 if (cave->hatching_delay_time > 0)
3466 /* for c64 schedulings, hatching delay means milliseconds. */
3467 cave->hatching_delay_time -= cave->speed;
3468 if (cave->hatching_delay_time <= 0)
3470 cave->hatching_delay_time = 0;
3471 start_signal = TRUE;
3476 /* if decremented hatching, and it became zero: */
3479 /* THIS IS THE CAVE START SIGNAL */
3481 /* record that now the cave is in its normal state */
3482 cave->hatched = TRUE;
3484 /* if diamonds needed is below zero, we count the available diamonds now. */
3485 gd_cave_count_diamonds(cave);
3487 /* setup direction auto change */
3488 if (cave->creatures_direction_auto_change_time)
3490 cave->creatures_direction_will_change =
3491 cave->creatures_direction_auto_change_time * cave->timing_factor;
3493 if (cave->creatures_direction_auto_change_on_start)
3494 cave->creatures_backwards = !cave->creatures_backwards;
3497 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3501 if (cave->biters_wait_frame == 0)
3502 cave->biters_wait_frame = cave->biter_delay_frame;
3504 cave->biters_wait_frame--;
3506 /* replicators delay */
3507 if (cave->replicators_wait_frame == 0)
3508 cave->replicators_wait_frame = cave->replicator_delay_frame;
3510 cave->replicators_wait_frame--;
3515 /* check if cave failed by timeout is done in main game engine */
3517 /* check if cave failed by timeout */
3518 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3520 gd_cave_clear_sounds(cave);
3521 cave->player_state = GD_PL_TIMEOUT;
3522 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3526 /* set these for drawing. */
3527 cave->last_direction = player_move;
3528 /* here we remember last movements for animation. this is needed here,
3529 as animation is in sync with the game, not the keyboard directly.
3530 (for example, after exiting the cave, the player was "running" in the
3531 original, till bonus points were counted for remaining time and so on. */
3532 if (player_move == GD_MV_LEFT ||
3533 player_move == GD_MV_UP_LEFT ||
3534 player_move == GD_MV_DOWN_LEFT)
3535 cave->last_horizontal_direction = GD_MV_LEFT;
3537 if (player_move == GD_MV_RIGHT ||
3538 player_move == GD_MV_UP_RIGHT ||
3539 player_move == GD_MV_DOWN_RIGHT)
3540 cave->last_horizontal_direction = GD_MV_RIGHT;
3542 cave->frame++; /* XXX */
3545 void set_initial_cave_speed(GdCave *cave)
3550 /* set cave get function; to implement perfect or lineshifting borders */
3551 if (cave->lineshift)
3552 cave->getp = getp_shift;
3554 cave->getp = getp_perfect;
3556 /* check whether to scan the first and last line */
3557 if (cave->border_scan_first_and_last)
3568 for (y = ymin; y <= ymax; y++)
3570 for (x = 0; x < cave->w; x++)
3572 /* add the ckdelay correction value for every element seen. */
3573 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3577 /* update timing calculated by iterating and counting elements */
3578 update_cave_speed(cave);