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. */
140 gd_sound_play(cave, GD_S_NUT, element, x, y);
146 case O_FLYING_STONE_F:
149 case O_WAITING_STONE:
150 case O_CHASING_STONE:
151 if (cave->stone_sound)
152 gd_sound_play(cave, GD_S_STONE, element, x, y);
157 if (cave->nitro_sound)
158 gd_sound_play(cave, GD_S_NITRO, element, x, y);
162 case O_FALLING_WALL_F:
163 if (cave->falling_wall_sound)
164 gd_sound_play(cave, GD_S_FALLING_WALL, element, x, y);
167 case O_H_EXPANDING_WALL:
168 case O_V_EXPANDING_WALL:
169 case O_EXPANDING_WALL:
170 case O_H_EXPANDING_STEEL_WALL:
171 case O_V_EXPANDING_STEEL_WALL:
172 case O_EXPANDING_STEEL_WALL:
173 if (cave->expanding_wall_sound)
174 gd_sound_play(cave, GD_S_EXPANDING_WALL, element, x, y);
179 case O_FLYING_DIAMOND:
180 case O_FLYING_DIAMOND_F:
181 if (cave->diamond_sound)
182 gd_sound_play(cave, GD_S_DIAMOND_RANDOM, element, x, y);
185 case O_BLADDER_SPENDER:
186 if (cave->bladder_spender_sound)
187 gd_sound_play(cave, GD_S_BLADDER_SPENDER, element, x, y);
191 if (cave->bladder_convert_sound)
192 gd_sound_play(cave, GD_S_BLADDER_CONVERT, element, x, y);
196 if (cave->slime_sound)
197 gd_sound_play(cave, GD_S_SLIME, element, x, y);
201 if (cave->lava_sound)
202 gd_sound_play(cave, GD_S_LAVA, element, x, y);
206 if (cave->acid_spread_sound)
207 gd_sound_play(cave, GD_S_ACID_SPREAD, element, x, y);
211 if (cave->bladder_sound)
212 gd_sound_play(cave, GD_S_BLADDER_MOVE, element, x, y);
219 if (cave->biter_sound)
220 gd_sound_play(cave, GD_S_BITER_EAT, element, x, y);
227 gd_sound_play(cave, GD_S_DIRT_BALL, element, x, y);
236 static inline GdElement *getp(const GdCave *cave, const int x, const int y)
238 return cave->getp(cave, x, y);
242 perfect (non-lineshifting) GET function.
243 returns a pointer to a selected cave element by its coordinates.
245 static inline GdElement *getp_perfect(const GdCave *cave, const int x, const int y)
247 /* (x + n) mod n: this works also for x >= n and -n + 1 < x < 0 */
248 return &(cave->map[(y + cave->h) % cave->h][(x + cave->w) % cave->w]);
252 line shifting GET function; returns a pointer to the selected cave element.
253 this is used to emulate the line-shifting behaviour of original games, so that
254 the player entering one side will appear one row above or below on the other.
256 static inline GdElement *getp_shift(const GdCave *cave, int x, int y)
269 y = (y + cave->h) % cave->h;
271 return &(cave->map[y][x]);
274 static inline GdElement get(const GdCave *cave, const int x, const int y)
276 return *getp(cave, x, y);
279 /* returns an element which is somewhere near x,y */
280 static inline GdElement get_dir(const GdCave *cave, const int x, const int y,
281 const GdDirection dir)
283 return get(cave, x + gd_dx[dir], y + gd_dy[dir]);
286 static inline boolean explodes_by_hit_dir(const GdCave *cave, const int x,
287 const int y, GdDirection dir)
289 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_EXPLODES_BY_HIT) != 0;
292 /* returns true if the element is not explodable, for example the steel wall */
293 static inline boolean non_explodable(const GdCave *cave, const int x, const int y)
295 return (gd_elements[get(cave, x,y) & O_MASK].properties & P_NON_EXPLODABLE) != 0;
298 /* returns true if the element can be eaten by the amoeba, eg. space and dirt. */
299 static inline boolean amoeba_eats_dir(const GdCave *cave, const int x, const int y,
300 const GdDirection dir)
302 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_AMOEBA_CONSUMES) != 0;
305 /* returns true if the element is sloped, so stones and diamonds roll down on it.
306 for example a stone or brick wall */
307 static inline boolean sloped_dir(const GdCave *cave, const int x, const int y,
308 const GdDirection dir, const GdDirection slop)
313 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_LEFT) != 0;
316 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_RIGHT) != 0;
319 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_UP) != 0;
322 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_DOWN) != 0;
331 /* returns true if the element is sloped for bladder movement
332 (brick = yes, diamond = no, for example) */
333 static inline boolean sloped_for_bladder_dir (const GdCave *cave, const int x, const int y,
334 const GdDirection dir)
336 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLADDER_SLOPED) != 0;
339 static inline boolean blows_up_flies_dir(const GdCave *cave, const int x, const int y,
340 const GdDirection dir)
342 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLOWS_UP_FLIES) != 0;
345 /* returns true if the element is a counter-clockwise creature */
346 static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
348 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_CCW) != 0;
351 /* returns true if the element is a player */
352 static inline boolean is_player(const GdCave *cave, const int x, const int y)
354 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
357 /* returns true if the element is a player */
358 static inline boolean is_player_dir(const GdCave *cave, const int x, const int y,
359 const GdDirection dir)
361 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PLAYER) != 0;
364 static inline boolean can_be_hammered_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_CAN_BE_HAMMERED) != 0;
370 /* returns true if the element is explodable and explodes to space, for example the player */
371 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
373 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
376 /* returns true if the element is moved by the conveyor belt */
377 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
378 const GdDirection dir)
380 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
383 /* returns true if the element is moved by the conveyor belt */
384 static inline boolean moved_by_conveyor_bottom_dir(const GdCave *cave, const int x, const int y,
385 const GdDirection dir)
387 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_BOTTOM) != 0;
390 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
391 const GdDirection dir)
393 return (get_dir(cave, x, y, dir) & SCANNED) != 0;
396 /* returns true if neighbouring element is "e" */
397 /* treats dirt specially */
398 /* treats lava specially */
399 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
400 const GdDirection dir, GdElement e)
402 GdElement examined = get_dir(cave, x, y, dir);
404 /* if it is a dirt-like, change to dirt, so equality will evaluate to true */
405 if (gd_elements[examined & O_MASK].properties & P_DIRT)
408 if (gd_elements[e & O_MASK].properties & P_DIRT)
411 /* if the element on the map is a lava, it should be like space */
412 if (examined == O_LAVA)
415 return (e == examined);
418 /* returns true if neighbouring element is space */
419 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
420 const GdDirection dir)
422 GdElement e = get_dir(cave, x, y, dir)&O_MASK;
424 return (e == O_SPACE || e == O_LAVA);
427 /* store an element at the given position */
428 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
430 GdElement *e = getp(cave, x, y);
434 play_sound_of_element(cave, O_LAVA, x, y);
442 /* store an element with SCANNED flag turned on */
443 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
445 store(cave, x, y, element | SCANNED);
448 /* store an element to a neighbouring cell */
449 static inline void store_dir(GdCave *cave, const int x, const int y,
450 const GdDirection dir, const GdElement element)
452 store(cave, x + gd_dx[dir], y + gd_dy[dir], element|SCANNED);
455 /* store an element to a neighbouring cell */
456 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
457 const GdDirection dir, const GdElement element)
459 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
462 /* move element to direction; then place space at x, y */
463 static inline void move(GdCave *cave, const int x, const int y,
464 const GdDirection dir, const GdElement e)
466 store_dir(cave, x, y, dir, e);
467 store(cave, x, y, O_SPACE);
470 /* increment a cave element; can be used for elements which are one after
471 the other, for example bladder1, bladder2, bladder3... */
472 static inline void next(GdCave *cave, const int x, const int y)
474 (*getp(cave, x, y))++;
477 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
479 if (non_explodable (cave, x, y))
482 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
483 cave->voodoo_touched = TRUE;
485 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
486 /* voodoo turns into a time penalty */
487 store_sc(cave, x, y, O_TIME_PENALTY);
488 else if (get(cave, x, y) == O_NITRO_PACK ||
489 get(cave, x, y) == O_NITRO_PACK_F)
490 /* nitro pack inside an explosion - it is now triggered */
491 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
493 /* for everything else */
494 store_sc(cave, x, y, explode_to);
497 /* a creature explodes to a 3x3 something. */
498 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
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_EXPLOSION, 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, explode_to);
511 static void nitro_explode(GdCave *cave, int x, int y)
515 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
516 cave->ckdelay += 1200;
517 gd_sound_play(cave, GD_S_NITRO_EXPLOSION, get(cave, x, y), x, y);
519 for (yy = y - 1; yy <= y + 1; yy++)
520 for (xx = x - 1; xx <= x + 1; xx++)
521 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
523 /* the current cell is explicitly changed into a nitro expl,
524 as cell_explode changes it to a triggered nitro pack */
525 store_sc(cave, x, y, O_NITRO_EXPL_1);
528 /* a voodoo explodes, leaving a 3x3 steel and a time penalty behind. */
529 static void voodoo_explode(GdCave *cave, int x, int y)
533 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
534 cave->ckdelay += 1000;
536 gd_sound_play(cave, GD_S_VOODOO_EXPLOSION, get(cave, x, y), x, y);
537 if (cave->voodoo_any_hurt_kills_player)
538 cave->voodoo_touched = TRUE;
540 /* voodoo explodes to 3x3 steel */
541 for (yy = y - 1; yy <= y + 1; yy++)
542 for (xx = x - 1; xx <= x + 1; xx++)
543 store_sc(cave, xx, yy, O_PRE_STEEL_1);
545 /* middle is a time penalty (which will be turned into a gravestone) */
546 store_sc(cave, x, y, O_TIME_PENALTY);
549 /* a bomb does not explode the voodoo, neither does the ghost.
550 this function check this, and stores the new element or not.
551 destroying the voodoo is also controlled by the
552 voodoo_disappear_in_explosion flag. */
553 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
555 if (non_explodable (cave, x, y))
558 /* bomb does not explode voodoo */
559 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
562 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
563 cave->voodoo_touched = TRUE;
565 store_sc (cave, x, y, expl);
568 /* X shaped ghost explosion; does not touch voodoo! */
569 static void ghost_explode(GdCave *cave, const int x, const int y)
571 gd_sound_play(cave, GD_S_GHOST_EXPLOSION, get(cave, x, y), x, y);
573 /* the processing of an explosion took pretty much time: processing 5 elements */
574 cave->ckdelay += 650;
576 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
577 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
578 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
579 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
580 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
583 /* +shaped bomb explosion; does not touch voodoo! */
584 static void bomb_explode(GdCave *cave, const int x, const int y)
586 gd_sound_play(cave, GD_S_BOMB_EXPLOSION, get(cave, x, y), x, y);
588 /* the processing of an explosion took pretty much time: processing 5 elements */
589 cave->ckdelay += 650;
591 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
592 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
593 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
594 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
595 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
599 explode an element with the appropriate type of exlposion.
601 static void explode(GdCave *cave, int x, int y)
603 GdElement e = get(cave, x, y) & O_MASK;
608 ghost_explode(cave, x, y);
612 bomb_explode(cave, x, y);
616 voodoo_explode(cave, x, y);
621 case O_NITRO_PACK_EXPLODE:
622 nitro_explode(cave, x, y);
626 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
629 case O_FALLING_WALL_F:
630 creature_explode(cave, x, y, O_EXPLODE_1);
637 creature_explode(cave, x, y, cave->butterfly_explode_to);
644 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
651 creature_explode(cave, x, y, cave->firefly_explode_to);
654 case O_ALT_FIREFLY_1:
655 case O_ALT_FIREFLY_2:
656 case O_ALT_FIREFLY_3:
657 case O_ALT_FIREFLY_4:
658 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
664 case O_PLAYER_STIRRING:
665 case O_PLAYER_PNEUMATIC_LEFT:
666 case O_PLAYER_PNEUMATIC_RIGHT:
667 creature_explode(cave, x, y, O_EXPLODE_1);
674 creature_explode(cave, x, y, cave->stonefly_explode_to);
681 creature_explode(cave, x, y, cave->dragonfly_explode_to);
690 explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
692 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
696 player eats specified object.
697 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
698 returns other element if something other appears there and he can't move.
699 cave pointer is needed to know the diamond values.
701 static GdElement player_get_element (GdCave* cave, const GdElement object, int x, int y)
708 cave->diamond_key_collected = TRUE;
709 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECT, object, x, y);
714 gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
719 gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
724 gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
731 gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
738 gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
745 gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
750 case O_CREATURE_SWITCH: /* creatures change direction. */
751 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
752 cave->creatures_backwards = !cave->creatures_backwards;
755 case O_EXPANDING_WALL_SWITCH: /* expanding wall change direction. */
756 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
757 cave->expanding_wall_changed = !cave->expanding_wall_changed;
760 case O_BITER_SWITCH: /* biter change delay */
761 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
762 cave->biter_delay_frame++;
763 if (cave->biter_delay_frame == 4)
764 cave->biter_delay_frame = 0;
767 case O_REPLICATOR_SWITCH: /* replicator on/off switch */
768 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
769 cave->replicators_active = !cave->replicators_active;
772 case O_CONVEYOR_SWITCH: /* conveyor belts on/off */
773 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
774 cave->conveyor_belts_active = !cave->conveyor_belts_active;
777 case O_CONVEYOR_DIR_SWITCH: /* conveyor belts switch direction */
778 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
779 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
785 case O_STEEL_EATABLE:
786 case O_BRICK_EATABLE:
787 case O_DIRT_SLOPED_UP_RIGHT:
788 case O_DIRT_SLOPED_UP_LEFT:
789 case O_DIRT_SLOPED_DOWN_LEFT:
790 case O_DIRT_SLOPED_DOWN_RIGHT:
793 gd_sound_play(cave, GD_S_WALK_EARTH, object, x, y);
797 gd_sound_play(cave, GD_S_SWEET_COLLECT, object, x, y);
798 cave->sweet_eaten = TRUE;
801 case O_PNEUMATIC_HAMMER:
802 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECT, object, x, y);
803 cave->got_pneumatic_hammer = TRUE;
808 gd_sound_play(cave, GD_S_CLOCK_COLLECT, object, x, y);
809 cave->time += cave->time_bonus * cave->timing_factor;
810 if (cave->time > cave->max_time * cave->timing_factor)
811 cave->time -= cave->max_time * cave->timing_factor;
812 /* no space, rather a dirt remains there... */
816 case O_FLYING_DIAMOND:
817 // prevent diamond sounds for O_SKELETON (see below)
818 if (x != -1 && y != -1)
819 gd_sound_play(cave, GD_S_DIAMOND_COLLECT, object, x, y);
821 cave->score += cave->diamond_value;
822 cave->diamonds_collected++;
824 if (cave->diamonds_needed == cave->diamonds_collected)
826 cave->gate_open = TRUE;
828 /* extra is worth more points. */
829 cave->diamond_value = cave->extra_diamond_value;
831 cave->gate_open_flash = 1;
832 cave->sound3 = GD_S_CRACK;
833 gd_sound_play(cave, GD_S_CRACK, O_OUTBOX, x, y);
838 cave->skeletons_collected++;
840 /* as if player got a diamond */
841 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
842 player_get_element(cave, O_DIAMOND, -1, -1);
844 /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
845 gd_sound_play(cave, GD_S_SKELETON_COLLECT, object, x, y);
850 cave->player_state = GD_PL_EXITED; /* player now exits the cave! */
854 case O_LAVA: /* player goes into lava, as if it was space */
855 gd_sound_play(cave, GD_S_WALK_EMPTY, object, x, y);
859 /* the object will remain there. */
865 process a crazy dream-style teleporter.
866 called from gd_cave_iterate, for a player or a player_bomb.
867 player is standing at px, py, and trying to move in the direction player_move,
868 where there is a teleporter.
869 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
870 around). the first teleporter we find, and which is suitable, will be the destination.
871 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
873 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
882 /* jump to next element; wrap around columns and rows. */
894 /* if we found a teleporter... */
895 if (get(cave, tx, ty) == O_TELEPORTER &&
896 is_space_dir(cave, tx, ty, player_move))
898 /* new player appears near teleporter found */
899 store_dir(cave, tx, ty, player_move, get(cave, px, py));
901 /* current player disappears */
902 store(cave, px, py, O_SPACE);
904 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
906 return TRUE; /* return true as teleporter worked */
909 /* loop until we get back to original coordinates */
910 while (tx != px || ty != py);
912 /* return false as we did not find any usable teleporter */
917 try to push an element.
918 returns true if the push is possible; also does move the specified _element_.
919 up to the caller to move the _player_itself_.
921 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
924 GdElement what = get_dir(cave, x, y, player_move);
926 /* gravity for falling wall, bladder, ... */
927 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
933 case O_WAITING_STONE:
936 case O_CHASING_STONE:
940 /* pushing some kind of stone or nut */
941 /* directions possible: 90degrees cw or ccw to current gravity. */
942 /* only push if player dir is orthogonal to gravity,
943 ie. gravity down, pushing left & right possible */
944 if (player_move == ccw_fourth[cave->gravity] ||
945 player_move == cw_fourth[cave->gravity])
951 /* different probabilities for different elements. */
954 case O_WAITING_STONE:
955 /* waiting stones are light, can always push */
959 case O_CHASING_STONE:
960 /* chasing can be pushed if player is turbo */
961 if (cave->sweet_eaten)
966 /* mega may(!) be pushed if player is turbo */
967 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
975 if (cave->sweet_eaten)
976 prob = cave->pushing_stone_prob_sweet; /* probability with sweet */
978 prob = cave->pushing_stone_prob; /* probability without sweet. */
985 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
986 g_rand_int_range(cave->random, 0, 1000000) < prob)
988 /* if decided that he will be able to push, */
989 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
990 play_sound_of_element(cave, what, x, y);
1005 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1006 * not an O_BLADDER_x. */
1007 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
1009 /* first check: we cannot push a bladder "up" */
1010 if (player_move != opposite[grav_compat])
1012 /* pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check. */
1013 /* player moving in the direction of gravity. */
1017 if (player_move == grav_compat)
1019 /* pushing bladder down */
1020 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1021 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1022 /* if no space to push down, maybe left (down-left to player) */
1023 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1025 /* left is "down, turned right (cw)" */
1026 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1027 /* if not, maybe right (down-right to player) */
1028 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1029 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1032 /* pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check. */
1036 else if (player_move == cw_fourth[grav_compat])
1038 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) /* pushing it left */
1039 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1040 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) /* maybe down, and player will move left */
1041 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1042 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) /* maybe up, and player will move left */
1043 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1046 /* pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check. */
1050 else if (player_move == ccw_fourth[grav_compat])
1052 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) /* pushing it right */
1053 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1054 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) /* maybe down, and player will move right */
1055 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1056 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) /* maybe up, and player will move right */
1057 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1061 play_sound_of_element(cave, O_BLADDER, x, y);
1066 /* a box is only pushed with the fire pressed */
1069 /* but always with 100% probability */
1070 switch (player_move)
1076 /* pushing in some dir, two steps in that dir - is there space? */
1077 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1080 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1082 gd_sound_play(cave, GD_S_BOX_PUSH, what, x, y);
1087 /* push in no other directions possible */
1093 /* pushing of other elements not possible */
1101 /* from the key press booleans, create a direction */
1102 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1104 GdDirection player_move;
1106 /* from the key press booleans, create a direction */
1108 player_move = GD_MV_UP_RIGHT;
1109 else if (down && right)
1110 player_move = GD_MV_DOWN_RIGHT;
1111 else if (down && left)
1112 player_move = GD_MV_DOWN_LEFT;
1113 else if (up && left)
1114 player_move = GD_MV_UP_LEFT;
1116 player_move = GD_MV_UP;
1118 player_move = GD_MV_DOWN;
1120 player_move = GD_MV_LEFT;
1122 player_move = GD_MV_RIGHT;
1124 player_move = GD_MV_STILL;
1129 /* clear these to no sound; and they will be set during iteration. */
1130 void gd_cave_clear_sounds(GdCave *cave)
1132 cave->sound1 = GD_S_NONE;
1133 cave->sound2 = GD_S_NONE;
1134 cave->sound3 = GD_S_NONE;
1137 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1138 GdElement falling_element)
1140 if (cave->gravity_disabled)
1143 if (is_space_dir(cave, x, y, falling_direction))
1145 /* beginning to fall */
1146 play_sound_of_element(cave, get(cave, x, y), x, y);
1147 move(cave, x, y, falling_direction, falling_element);
1150 /* check if it is on a sloped element, and it can roll. */
1151 /* for example, sloped wall looks like: */
1154 /* this is tagged as sloped up&left. */
1155 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1156 /* then check the direction to roll (left or right) */
1157 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1158 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1160 /* rolling down, if sitting on a sloped object */
1161 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1162 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1163 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1165 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1166 so here we use cw_fourth */
1167 play_sound_of_element(cave, get(cave, x, y), x, y);
1168 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1170 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1171 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1172 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1174 /* rolling right? */
1175 play_sound_of_element(cave, get(cave, x, y), x, y);
1176 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1181 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1183 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1184 cave->voodoo_dies_by_stone)
1186 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1187 explode_dir(cave, x, y, fall_dir);
1194 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1196 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1197 cave->voodoo_collects_diamonds)
1199 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1200 player_get_element(cave, O_DIAMOND, x, y); /* as if player got diamond */
1201 store(cave, x, y, O_SPACE); /* diamond disappears */
1208 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1209 GdDirection fall_dir, GdElement bouncing)
1211 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1212 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1215 store(cave, x, y, bouncing);
1216 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1218 if (cave->nut_sound)
1219 gd_sound_play(cave, GD_S_NUT_CRACK, O_NUT, x, y);
1227 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1228 GdDirection fall_dir, GdElement magic)
1230 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1232 play_sound_of_element(cave, O_DIAMOND, x, y); /* always play diamond sound */
1234 if (cave->magic_wall_state==GD_MW_DORMANT)
1235 cave->magic_wall_state = GD_MW_ACTIVE;
1237 if (cave->magic_wall_state==GD_MW_ACTIVE &&
1238 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1240 /* if magic wall active and place underneath, it turns element
1241 into anything the effect says to do. */
1242 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1245 /* active or non-active or anything, element falling in will always disappear */
1246 store(cave, x, y, O_SPACE);
1254 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1256 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1258 explode_dir(cave, x, y, fall_dir);
1265 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1266 GdDirection fall_dir, GdElement bouncing)
1268 if (is_space_dir(cave, x, y, fall_dir))
1270 /* falling further */
1271 move(cave, x, y, fall_dir, get(cave, x, y));
1276 /* check if it is on a sloped element, and it can roll. */
1277 /* for example, sloped wall looks like: */
1280 /* this is tagged as sloped up&left. */
1281 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1282 /* then check the direction to roll (left or right) */
1283 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1285 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1287 /* sloped element, falling to left or right */
1288 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1289 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1290 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1292 play_sound_of_element(cave, get(cave, x, y), x, y);
1294 /* try to roll left first - see O_STONE to understand why cw_fourth */
1295 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1297 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1298 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1299 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1301 play_sound_of_element(cave, get(cave, x, y), x, y);
1303 /* if not, try to roll right */
1304 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1308 /* cannot roll in any direction, so it stops */
1309 play_sound_of_element(cave, get(cave, x, y), x, y);
1310 store(cave, x, y, bouncing);
1316 /* any other element, stops */
1317 play_sound_of_element(cave, get(cave, x, y), x, y);
1318 store(cave, x, y, bouncing);
1322 static void update_cave_speed(GdCave *cave)
1324 /* update timing calculated by iterating and counting elements which were slow to process on c64 */
1325 switch (cave->scheduling)
1327 case GD_SCHEDULING_MILLISECONDS:
1328 /* cave->speed already contains the milliseconds value, do not touch it */
1331 case GD_SCHEDULING_BD1:
1332 if (!cave->intermission)
1333 /* non-intermissions */
1334 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1336 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
1337 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1340 case GD_SCHEDULING_BD1_ATARI:
1341 /* about 20ms/frame faster than c64 version */
1342 if (!cave->intermission)
1343 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); /* non-intermissions */
1345 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); /* for intermissions */
1348 case GD_SCHEDULING_BD2:
1349 /* 60 is a guess. */
1350 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1353 case GD_SCHEDULING_PLCK:
1354 /* 65 is totally empty cave in construction kit, with delay = 0) */
1355 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1358 case GD_SCHEDULING_BD2_PLCK_ATARI:
1359 /* a really fast engine; timing works like c64 plck. */
1360 /* 40 ms was measured in the construction kit, with delay = 0 */
1361 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1364 case GD_SCHEDULING_CRDR:
1365 if (cave->hammered_walls_reappear) /* this made the engine very slow. */
1366 cave->ckdelay += 60000;
1367 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1370 case GD_SCHEDULING_MAX:
1375 /* process a cave. */
1376 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1380 /* for border scan */
1383 /* amoeba found to be enclosed. if not, this is cleared */
1384 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1386 /* counting the number of amoebas. after scan, check if too much */
1387 int amoeba_count, amoeba_2_count;
1389 /* cave scan found water - for sound */
1390 boolean found_water;
1392 boolean inbox_toggle;
1393 boolean start_signal;
1395 /* gravity for falling wall, bladder, ... */
1396 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1398 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1399 static const GdDirection creature_dir[] =
1406 static const GdDirection creature_chdir[] =
1413 int time_decrement_sec;
1415 /* biters eating elements preference, they try to go in this order */
1416 GdElement biter_try[] =
1423 boolean amoeba_sound, magic_sound;
1425 gd_cave_clear_sounds(cave);
1427 /* if diagonal movements not allowed, */
1428 /* horizontal movements have precedence. [BROADRIBB] */
1429 if (!cave->diagonal_movements)
1431 switch (player_move)
1433 case GD_MV_UP_RIGHT:
1434 case GD_MV_DOWN_RIGHT:
1435 player_move = GD_MV_RIGHT;
1439 case GD_MV_DOWN_LEFT:
1440 player_move = GD_MV_LEFT;
1444 /* no correction needed */
1449 /* set cave get function; to implement perfect or lineshifting borders */
1450 if (cave->lineshift)
1451 cave->getp = getp_shift;
1453 cave->getp = getp_perfect;
1455 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1456 if (cave->player_seen_ago < 100)
1457 cave->player_seen_ago++;
1459 if (cave->pneumatic_hammer_active_delay > 0)
1460 cave->pneumatic_hammer_active_delay--;
1462 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1463 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1464 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1465 inbox_toggle = cave->inbox_flash_toggle;
1467 if (cave->gate_open_flash > 0)
1468 cave->gate_open_flash--;
1470 /* score collected this frame */
1473 /* suicide only kills the active player */
1474 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1475 /* we must check if there is a player or not - he may have exploded or something like that */
1476 if (suicide && cave->player_state == GD_PL_LIVING &&
1477 is_player(cave, cave->player_x, cave->player_y))
1478 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1480 /* check for walls reappearing */
1481 if (cave->hammered_reappear)
1483 for (y = 0; y < cave->h; y++)
1485 for (x = 0; x < cave->w; x++)
1487 /* timer for the cell > 0? */
1488 if (cave->hammered_reappear[y][x]>0)
1490 /* decrease timer */
1491 cave->hammered_reappear[y][x]--;
1493 /* check if it became zero */
1494 if (cave->hammered_reappear[y][x] == 0)
1496 store(cave, x, y, O_BRICK);
1497 gd_sound_play(cave, GD_S_WALL_REAPPEAR, O_BRICK, x, y);
1504 /* variables to check during the scan */
1506 /* will be set to false if any of the amoeba is found free. */
1507 amoeba_found_enclosed = TRUE;
1508 amoeba_2_found_enclosed = TRUE;
1511 found_water = FALSE;
1513 time_decrement_sec = 0;
1515 /* check whether to scan the first and last line */
1516 if (cave->border_scan_first_and_last)
1527 /* the cave scan routine */
1528 for (y = ymin; y <= ymax; y++)
1530 for (x = 0; x < cave->w; x++)
1532 /* if we find a scanned element, change it to the normal one, and that's all. */
1533 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1534 if (get(cave, x, y)&SCANNED)
1536 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1541 /* add the ckdelay correction value for every element seen. */
1542 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1544 switch (get(cave, x, y))
1550 if (cave->kill_player)
1552 explode (cave, x, y);
1556 cave->player_seen_ago = 0;
1557 /* bd4 intermission caves have many players. so if one of them has exited,
1558 * do not change the flag anymore. so this if () is needed */
1559 if (cave->player_state!=GD_PL_EXITED)
1560 cave->player_state = GD_PL_LIVING;
1562 /* check for pneumatic hammer things */
1563 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right
1564 for hammer 5) stand on something */
1565 if (player_fire && cave->got_pneumatic_hammer &&
1566 is_space_dir(cave, x, y, player_move) &&
1567 !is_space_dir(cave, x, y, GD_MV_DOWN))
1569 if (player_move == GD_MV_LEFT &&
1570 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1572 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1573 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1574 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1575 break; /* finished. */
1578 if (player_move == GD_MV_RIGHT &&
1579 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1581 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1582 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1583 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1584 break; /* finished. */
1588 if (player_move != GD_MV_STILL)
1590 /* only do every check if he is not moving */
1591 GdElement what = get_dir(cave, x, y, player_move);
1592 GdElement remains = what;
1595 /* if we are 'eating' a teleporter, and the function returns true
1596 (teleporting worked), break here */
1597 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1600 /* try to push element; if successful, break */
1601 push = do_push(cave, x, y, player_move, player_fire);
1608 /* if its a bomb, remember he now has one. */
1609 /* we do not change the "remains" and "what" variables,
1610 so that part of the code will be ineffective */
1611 gd_sound_play(cave, GD_S_BOMB_COLLECT, what, x, y);
1612 store_dir(cave, x, y, player_move, O_SPACE);
1615 store(cave, x, y, O_PLAYER_BOMB);
1617 move(cave, x, y, player_move, O_PLAYER_BOMB);
1621 /* we do not change the "remains" and "what" variables,
1622 so that part of the code will be ineffective */
1623 if (!player_fire && !cave->gravity_switch_active &&
1624 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1626 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1627 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1628 cave->gravity_disabled = TRUE;
1632 case O_GRAVITY_SWITCH:
1633 /* (we cannot use player_get for this as it does not have player_move parameter) */
1634 /* only allow changing direction if the new dir is not diagonal */
1635 if (cave->gravity_switch_active &&
1636 (player_move == GD_MV_LEFT ||
1637 player_move == GD_MV_RIGHT ||
1638 player_move == GD_MV_UP ||
1639 player_move == GD_MV_DOWN))
1641 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1642 cave->gravity_will_change =
1643 cave->gravity_change_time * cave->timing_factor;
1644 cave->gravity_next_direction = player_move;
1645 cave->gravity_switch_active = FALSE;
1650 /* get element - process others.
1651 if cannot get, player_get_element will return the same */
1652 remains = player_get_element (cave, what, x, y);
1656 if (remains != what || remains == O_SPACE)
1658 /* if anything changed, apply the change. */
1660 /* if snapping anything and we have snapping explosions set.
1661 but these is not true for pushing. */
1662 if (remains == O_SPACE && player_fire && !push)
1663 remains = cave->snap_element;
1665 if (remains != O_SPACE || player_fire)
1666 /* if any other element than space, player cannot move.
1667 also if pressing fire, will not move. */
1668 store_dir(cave, x, y, player_move, remains);
1670 /* if space remains there, the player moves. */
1671 move(cave, x, y, player_move, O_PLAYER);
1677 /* much simpler; cannot steal stones */
1678 if (cave->kill_player)
1680 explode(cave, x, y);
1684 cave->player_seen_ago = 0;
1685 /* bd4 intermission caves have many players. so if one of them has exited,
1686 * do not change the flag anymore. so this if () is needed */
1687 if (cave->player_state != GD_PL_EXITED)
1688 cave->player_state = GD_PL_LIVING;
1690 if (player_move != GD_MV_STILL)
1692 /* if the player does not move, nothing to do */
1693 GdElement what = get_dir(cave, x, y, player_move);
1694 GdElement remains = what;
1698 /* placing a bomb into empty space or dirt */
1699 if (is_space_dir(cave, x, y, player_move) ||
1700 is_element_dir(cave, x, y, player_move, O_DIRT))
1702 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1704 /* placed bomb, he is normal player again */
1705 store(cave, x, y, O_PLAYER);
1706 gd_sound_play(cave, GD_S_BOMB_PLACE, O_BOMB, x, y);
1711 /* pushing and collecting */
1712 /* if we are 'eating' a teleporter, and the function returns true
1713 (teleporting worked), break here */
1714 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1717 /* player fire is false... */
1718 if (do_push(cave, x, y, player_move, FALSE))
1724 case O_GRAVITY_SWITCH:
1725 /* (we cannot use player_get for this as it does not have
1726 player_move parameter) */
1727 /* only allow changing direction if the new dir is not diagonal */
1728 if (cave->gravity_switch_active &&
1729 (player_move==GD_MV_LEFT ||
1730 player_move==GD_MV_RIGHT ||
1731 player_move==GD_MV_UP ||
1732 player_move==GD_MV_DOWN))
1734 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1735 cave->gravity_will_change =
1736 cave->gravity_change_time * cave->timing_factor;
1737 cave->gravity_next_direction = player_move;
1738 cave->gravity_switch_active = FALSE;
1743 /* get element. if cannot get, player_get_element will return the same */
1744 remains = player_get_element (cave, what, x, y);
1749 /* if element changed, OR there is space, move. */
1750 if (remains != what || remains == O_SPACE)
1752 /* if anything changed, apply the change. */
1753 move(cave, x, y, player_move, O_PLAYER_BOMB);
1758 case O_PLAYER_STIRRING:
1759 if (cave->kill_player)
1761 explode(cave, x, y);
1765 /* stirring sound, if no other walking sound or explosion */
1766 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1768 cave->player_seen_ago = 0;
1769 /* bd4 intermission caves have many players. so if one of them has exited,
1770 * do not change the flag anymore. so this if () is needed */
1771 if (cave->player_state!=GD_PL_EXITED)
1772 cave->player_state = GD_PL_LIVING;
1776 /* player "exits" stirring the pot by pressing fire */
1777 cave->gravity_disabled = FALSE;
1778 store(cave, x, y, O_PLAYER);
1779 cave->gravity_switch_active = TRUE;
1783 /* player holding pneumatic hammer */
1784 case O_PLAYER_PNEUMATIC_LEFT:
1785 case O_PLAYER_PNEUMATIC_RIGHT:
1786 /* usual player stuff */
1787 if (cave->kill_player)
1789 explode(cave, x, y);
1793 cave->player_seen_ago = 0;
1794 if (cave->player_state!=GD_PL_EXITED)
1795 cave->player_state = GD_PL_LIVING;
1797 /* if hammering time is up, becomes a normal player again. */
1798 if (cave->pneumatic_hammer_active_delay == 0)
1799 store(cave, x, y, O_PLAYER);
1802 /* the active pneumatic hammer itself */
1803 case O_PNEUMATIC_ACTIVE_RIGHT:
1804 case O_PNEUMATIC_ACTIVE_LEFT:
1805 if (cave->pneumatic_hammer_active_delay == 0)
1809 /* pneumatic hammer element disappears */
1810 store(cave, x, y, O_SPACE);
1812 /* which is the new element which appears after that one is hammered? */
1813 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1815 /* if there is a new element, display it */
1816 /* O_NONE might be returned, for example if the element being
1817 hammered explodes during hammering (by a nearby explosion) */
1818 if (new_elem != O_NONE)
1820 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1822 /* and if walls reappear, remember it in array */
1823 if (cave->hammered_walls_reappear)
1827 wall_y = (y + 1) % cave->h;
1828 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1835 * S T O N E S, D I A M O N D S
1837 case O_STONE: /* standing stone */
1838 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1841 case O_MEGA_STONE: /* standing mega_stone */
1842 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1845 case O_DIAMOND: /* standing diamond */
1846 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1849 case O_NUT: /* standing nut */
1850 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1853 case O_DIRT_BALL: /* standing dirt ball */
1854 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1857 case O_DIRT_LOOSE: /* standing loose dirt */
1858 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1861 case O_FLYING_STONE: /* standing stone */
1862 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1865 case O_FLYING_DIAMOND: /* standing diamond */
1866 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1870 * 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
1872 case O_DIRT_BALL_F: /* falling dirt ball */
1873 if (!cave->gravity_disabled)
1874 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
1877 case O_DIRT_LOOSE_F: /* falling loose dirt */
1878 if (!cave->gravity_disabled)
1879 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
1882 case O_STONE_F: /* falling stone */
1883 if (!cave->gravity_disabled)
1885 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1888 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
1891 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
1894 if (do_fall_try_crush(cave, x, y, cave->gravity))
1897 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
1901 case O_MEGA_STONE_F: /* falling mega */
1902 if (!cave->gravity_disabled)
1904 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1907 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
1910 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
1913 if (do_fall_try_crush(cave, x, y, cave->gravity))
1916 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
1920 case O_DIAMOND_F: /* falling diamond */
1921 if (!cave->gravity_disabled)
1923 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
1926 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
1929 if (do_fall_try_crush(cave, x, y, cave->gravity))
1932 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
1936 case O_NUT_F: /* falling nut */
1937 if (!cave->gravity_disabled)
1939 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
1942 if (do_fall_try_crush(cave, x, y, cave->gravity))
1945 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
1949 case O_FLYING_STONE_F: /* falling stone */
1950 if (!cave->gravity_disabled)
1952 GdDirection fall_dir = opposite[cave->gravity];
1954 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
1957 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
1960 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
1963 if (do_fall_try_crush(cave, x, y, fall_dir))
1966 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
1970 case O_FLYING_DIAMOND_F: /* falling diamond */
1971 if (!cave->gravity_disabled)
1973 GdDirection fall_dir = opposite[cave->gravity];
1975 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
1978 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
1981 if (do_fall_try_crush(cave, x, y, fall_dir))
1984 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
1991 case O_NITRO_PACK: /* standing nitro pack */
1992 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
1995 case O_NITRO_PACK_F: /* falling nitro pack */
1996 if (!cave->gravity_disabled)
1998 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
1999 move(cave, x, y, cave->gravity, get(cave, x, y));
2000 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2002 /* try magic wall; if true, function did the work */
2004 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2006 /* falling on a dirt, it does NOT explode - just stops at its place. */
2007 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2008 store(cave, x, y, O_NITRO_PACK);
2011 /* falling on any other element it explodes */
2012 explode(cave, x, y);
2016 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
2017 explode(cave, x, y);
2028 /* if cannot move in any direction, becomes an enclosed cow */
2029 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2030 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2031 store(cave, x, y, O_COW_ENCLOSED_1);
2034 /* THIS IS THE CREATURE MOVE thing copied. */
2035 const GdDirection *creature_move;
2036 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2037 GdElement base; /* base element number (which is like O_***_1) */
2038 int dir, dirn, dirp; /* direction */
2042 dir = get(cave, x, y)-base; /* facing where */
2043 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2045 /* now change direction if backwards */
2046 if (cave->creatures_backwards)
2051 dirn = (dir + 3) & 3; /* fast turn */
2052 dirp = (dir + 1) & 3; /* slow turn */
2056 dirn = (dir + 1) & 3; /* fast turn */
2057 dirp = (dir + 3) & 3; /* slow turn */
2060 if (is_space_dir(cave, x, y, creature_move[dirn]))
2061 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2062 else if (is_space_dir(cave, x, y, creature_move[dir]))
2063 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2065 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2069 /* enclosed cows wait some time before turning to a skeleton */
2070 case O_COW_ENCLOSED_1:
2071 case O_COW_ENCLOSED_2:
2072 case O_COW_ENCLOSED_3:
2073 case O_COW_ENCLOSED_4:
2074 case O_COW_ENCLOSED_5:
2075 case O_COW_ENCLOSED_6:
2076 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2077 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2078 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2079 is_space_dir(cave, x, y, GD_MV_DOWN))
2080 store(cave, x, y, O_COW_1);
2085 case O_COW_ENCLOSED_7:
2086 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2087 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2088 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2089 is_space_dir(cave, x, y, GD_MV_DOWN))
2090 store(cave, x, y, O_COW_1);
2092 store(cave, x, y, O_SKELETON);
2099 case O_ALT_FIREFLY_1:
2100 case O_ALT_FIREFLY_2:
2101 case O_ALT_FIREFLY_3:
2102 case O_ALT_FIREFLY_4:
2107 case O_ALT_BUTTER_1:
2108 case O_ALT_BUTTER_2:
2109 case O_ALT_BUTTER_3:
2110 case O_ALT_BUTTER_4:
2115 /* check if touches a voodoo */
2116 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2117 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2118 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2119 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2120 cave->voodoo_touched = TRUE;
2122 /* check if touches something bad and should explode (includes voodoo by the flags) */
2123 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2124 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2125 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2126 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2127 explode (cave, x, y);
2128 /* otherwise move */
2131 const GdDirection *creature_move;
2132 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2133 GdElement base; /* base element number (which is like O_***_1) */
2134 int dir, dirn, dirp; /* direction */
2136 if (get(cave, x, y) >= O_FIREFLY_1 &&
2137 get(cave, x, y) <= O_FIREFLY_4)
2139 else if (get(cave, x, y) >= O_BUTTER_1 &&
2140 get(cave, x, y) <= O_BUTTER_4)
2142 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2143 get(cave, x, y) <= O_STONEFLY_4)
2144 base = O_STONEFLY_1;
2145 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2146 get(cave, x, y) <= O_ALT_FIREFLY_4)
2147 base = O_ALT_FIREFLY_1;
2148 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2149 get(cave, x, y) <= O_ALT_BUTTER_4)
2150 base = O_ALT_BUTTER_1;
2152 dir = get(cave, x, y)-base; /* facing where */
2153 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2155 /* now change direction if backwards */
2156 if (cave->creatures_backwards)
2161 dirn = (dir + 3) & 3; /* fast turn */
2162 dirp = (dir + 1) & 3; /* slow turn */
2166 dirn = (dir + 1) & 3; /* fast turn */
2167 dirp = (dir + 3) & 3; /* slow turn */
2170 if (is_space_dir(cave, x, y, creature_move[dirn]))
2171 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2172 else if (is_space_dir(cave, x, y, creature_move[dir]))
2173 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2175 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2179 case O_WAITING_STONE:
2180 if (is_space_dir(cave, x, y, grav_compat))
2182 /* beginning to fall */
2184 move(cave, x, y, grav_compat, O_CHASING_STONE);
2186 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2188 /* rolling down a brick wall or a stone */
2189 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2190 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2191 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2193 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2194 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2196 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2197 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2198 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2200 /* or maybe right */
2201 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2206 case O_CHASING_STONE:
2208 int px = cave->px[0];
2209 int py = cave->py[0];
2210 boolean horizontal = g_rand_boolean(cave->random);
2211 boolean dont_move = FALSE;
2214 /* try to move... */
2219 /*********************************/
2220 /* check for a horizontal movement */
2223 /* if coordinates are the same */
2225 horizontal = !horizontal;
2232 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2234 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2238 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2240 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2249 horizontal = !horizontal;
2257 /********************************/
2258 /* check for a vertical movement */
2261 /* if coordinates are the same */
2263 horizontal = !horizontal;
2269 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2271 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2275 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2277 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2286 horizontal = !horizontal;
2299 /* if we should move in both directions, but can not move in any, stop. */
2304 /* check for horizontal */
2307 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2308 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2309 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2310 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2311 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2312 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2316 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2317 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2318 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2319 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2320 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2321 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2326 /* check for vertical */
2329 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2330 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2331 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2332 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2333 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2334 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2338 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2339 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2340 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2341 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2342 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2343 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2351 if (cave->replicators_wait_frame == 0 &&
2352 cave->replicators_active &&
2353 !cave->gravity_disabled)
2355 /* only replicate, if space is under it. */
2356 /* do not replicate players! */
2357 /* also obeys gravity settings. */
2358 /* only replicate element if it is not a scanned one */
2359 /* do not replicate space... that condition looks like it
2360 makes no sense, but otherwise it generates SCANNED spaces,
2361 which cannot be "collected" by the player, so he cannot run
2362 under a replicator */
2363 if (is_space_dir(cave, x, y, cave->gravity) &&
2364 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2365 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2367 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2368 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2377 if (cave->biters_wait_frame == 0)
2379 static GdDirection biter_move[] =
2387 /* direction, last two bits 0..3 */
2388 int dir = get(cave, x, y) - O_BITER_1;
2389 int dirn = (dir + 3) & 3;
2390 int dirp = (dir + 1) & 3;
2392 GdElement made_sound_of = O_NONE;
2394 for (i = 0; i < G_N_ELEMENTS (biter_try); i++)
2396 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2398 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2399 if (biter_try[i] != O_SPACE)
2400 made_sound_of = O_BITER_1; /* sound of a biter eating */
2403 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2405 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2406 if (biter_try[i] != O_SPACE)
2407 made_sound_of = O_BITER_1; /* sound of a biter eating */
2410 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2412 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2413 if (biter_try[i] != O_SPACE)
2414 made_sound_of = O_BITER_1; /* sound of a biter eating */
2419 if (i == G_N_ELEMENTS(biter_try))
2420 /* i = number of elements in array: could not move, so just turn */
2421 store(cave, x, y, O_BITER_1 + dirp);
2422 else if (biter_try[i] == O_STONE)
2424 /* if there was a stone there, where we moved...
2425 do not eat stones, just throw them back */
2426 store(cave, x, y, O_STONE);
2427 made_sound_of = O_STONE;
2430 /* if biter did move, we had sound. play it. */
2431 if (made_sound_of!=O_NONE)
2432 play_sound_of_element(cave, made_sound_of, x, y);
2440 /* check if touches a voodoo */
2441 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2442 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2443 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2444 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2445 cave->voodoo_touched = TRUE;
2447 /* check if touches something bad and should explode (includes voodoo by the flags) */
2448 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2449 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2450 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2451 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2452 explode (cave, x, y);
2453 /* otherwise move */
2456 const GdDirection *creature_move;
2457 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2458 GdElement base = O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2459 int dir, dirn; /* direction */
2461 dir = get(cave, x, y)-base; /* facing where */
2462 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2464 /* now change direction if backwards */
2465 if (cave->creatures_backwards)
2469 dirn = (dir + 3) & 3; /* fast turn */
2471 dirn = (dir + 1) & 3; /* fast turn */
2473 /* if can move forward, does so. */
2474 if (is_space_dir(cave, x, y, creature_move[dir]))
2475 move(cave, x, y, creature_move[dir], base + dir);
2477 /* otherwise turns 90 degrees in place. */
2478 store(cave, x, y, base + dirn);
2483 store(cave, x, y, O_BLADDER_1);
2494 /* bladder with any delay state: try to convert to clock. */
2495 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2496 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))
2498 /* if touches the specified element, let it be a clock */
2499 store(cave, x, y, O_PRE_CLOCK_1);
2501 /* plays the bladder convert sound */
2502 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2506 /* is space over the bladder? */
2507 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2509 if (get(cave, x, y)==O_BLADDER_8)
2511 /* if it is a bladder 8, really move up */
2512 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2513 play_sound_of_element(cave, O_BLADDER, x, y);
2516 /* if smaller delay, just increase delay. */
2520 /* if not space, is something sloped over the bladder? */
2521 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2522 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2524 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2525 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2526 is_space_dir(cave, x, y, ccw_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, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2533 play_sound_of_element(cave, O_BLADDER, x, y);
2536 /* if smaller delay, just increase delay. */
2539 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2540 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2541 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2543 /* rolling up, to left */
2544 if (get(cave, x, y) == O_BLADDER_8)
2546 /* if it is a bladder 8, really roll */
2547 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2548 play_sound_of_element(cave, O_BLADDER, x, y);
2551 /* if smaller delay, just increase delay. */
2556 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2558 store(cave, x, y, O_BLADDER_1);
2563 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2564 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2565 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2566 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2567 explode (cave, x, y);
2572 /* the ghost is given four possibilities to move. */
2573 for (i = 0; i < 4; i++)
2575 static GdDirection dirs[] =
2582 GdDirection random_dir;
2584 random_dir = dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2585 if (is_space_dir(cave, x, y, random_dir))
2587 move(cave, x, y, random_dir, O_GHOST);
2588 break; /* ghost did move -> exit loop */
2595 * A C T I V E E L E M E N T S
2600 switch (cave->amoeba_state)
2603 store(cave, x, y, cave->amoeba_too_big_effect);
2606 case GD_AM_ENCLOSED:
2607 store(cave, x, y, cave->amoeba_enclosed_effect);
2610 case GD_AM_SLEEPING:
2612 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2613 if (amoeba_found_enclosed)
2614 /* if still found enclosed, check all four directions,
2615 if this one is able to grow. */
2616 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2617 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2618 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2619 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2621 /* not enclosed. this is a local (per scan) flag! */
2622 amoeba_found_enclosed = FALSE;
2623 cave->amoeba_state = GD_AM_AWAKE;
2626 /* if alive, check in which dir to grow (or not) */
2627 if (cave->amoeba_state==GD_AM_AWAKE)
2629 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2631 switch (g_rand_int_range(cave->random, 0, 4))
2633 /* decided to grow, choose a random direction. */
2634 case 0: /* let this be up. numbers indifferent. */
2635 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2636 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2640 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2641 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2645 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2646 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2650 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2651 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2663 /* check if it is touching an amoeba, and explosion is enabled */
2664 if (cave->amoeba_2_explodes_by_amoeba &&
2665 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2666 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2667 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2668 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2669 explode (cave, x, y);
2671 switch (cave->amoeba_2_state)
2674 store(cave, x, y, cave->amoeba_2_too_big_effect);
2677 case GD_AM_ENCLOSED:
2678 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2681 case GD_AM_SLEEPING:
2683 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2684 if (amoeba_2_found_enclosed)
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_2_found_enclosed = FALSE;
2692 cave->amoeba_2_state = GD_AM_AWAKE;
2695 /* if it is alive, decide if it attempts to grow */
2696 if (cave->amoeba_2_state == GD_AM_AWAKE)
2697 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2699 switch (g_rand_int_range(cave->random, 0, 4))
2701 /* decided to grow, choose a random direction. */
2702 case 0: /* let this be up. numbers indifferent. */
2703 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2704 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2708 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2709 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2713 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2714 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2718 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2719 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2729 /* choose randomly, if it spreads */
2730 if (g_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2732 /* the current one explodes */
2733 store(cave, x, y, cave->acid_turns_to);
2735 /* and if neighbours are eaten, put acid there. */
2736 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2738 play_sound_of_element(cave, O_ACID, x, y);
2739 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2742 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2744 play_sound_of_element(cave, O_ACID, x, y);
2745 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2748 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2750 play_sound_of_element(cave, O_ACID, x, y);
2751 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2754 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2756 play_sound_of_element(cave, O_ACID, x, y);
2757 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2764 if (!cave->water_does_not_flow_down &&
2765 is_space_dir(cave, x, y, GD_MV_DOWN))
2766 /* emulating the odd behaviour in crdr */
2767 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2769 if (is_space_dir(cave, x, y, GD_MV_UP))
2770 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2772 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2773 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2775 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2776 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2780 store(cave, x, y, O_WATER);
2783 case O_H_EXPANDING_WALL:
2784 case O_V_EXPANDING_WALL:
2785 case O_H_EXPANDING_STEEL_WALL:
2786 case O_V_EXPANDING_STEEL_WALL:
2787 /* checks first if direction is changed. */
2788 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2789 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2790 !cave->expanding_wall_changed) ||
2791 ((get(cave, x, y)==O_V_EXPANDING_WALL ||
2792 get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) &&
2793 cave->expanding_wall_changed))
2795 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2797 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2798 play_sound_of_element(cave, get(cave, x, y), x, y);
2801 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2802 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2803 play_sound_of_element(cave, get(cave, x, y), x, y);
2808 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2809 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2810 play_sound_of_element(cave, get(cave, x, y), x, y);
2813 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2814 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2815 play_sound_of_element(cave, get(cave, x, y), x, y);
2820 case O_EXPANDING_WALL:
2821 case O_EXPANDING_STEEL_WALL:
2822 /* the wall which grows in all four directions. */
2823 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2825 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2826 play_sound_of_element(cave, get(cave, x, y), x, y);
2829 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2830 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2831 play_sound_of_element(cave, get(cave, x, y), x, y);
2834 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2835 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2836 play_sound_of_element(cave, get(cave, x, y), x, y);
2839 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2840 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2841 play_sound_of_element(cave, get(cave, x, y), x, y);
2847 ; // to make compilers happy ...
2849 g_print("Step[%03d]", cave->frame); /* XXX */
2851 int rrr = gd_cave_c64_random(cave);
2854 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2855 (rrr & cave->slime_permeability_c64) == 0);
2858 * unpredictable: g_rand_int
2859 * predictable: c64 predictable random generator.
2860 * for predictable, a random number is generated,
2861 * whether or not it is even possible that the stone will be able to pass.
2863 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : g_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2865 GdDirection grav = cave->gravity;
2866 GdDirection oppos = opposite[cave->gravity];
2868 /* space under the slime? elements may pass from top to bottom then. */
2869 if (is_space_dir(cave, x, y, grav))
2871 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
2873 /* output a falling xy under */
2874 store_dir(cave, x, y, grav, cave->slime_converts_1);
2876 store_dir(cave, x, y, oppos, O_SPACE);
2877 play_sound_of_element(cave, O_SLIME, x, y);
2879 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
2881 store_dir(cave, x, y, grav, cave->slime_converts_2);
2882 store_dir(cave, x, y, oppos, O_SPACE);
2883 play_sound_of_element(cave, O_SLIME, x, y);
2885 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
2887 store_dir(cave, x, y, grav, cave->slime_converts_3);
2888 store_dir(cave, x, y, oppos, O_SPACE);
2889 play_sound_of_element(cave, O_SLIME, x, y);
2891 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
2893 /* waiting stones pass without awakening */
2894 store_dir(cave, x, y, grav, O_WAITING_STONE);
2895 store_dir(cave, x, y, oppos, O_SPACE);
2896 play_sound_of_element(cave, O_SLIME, x, y);
2898 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
2900 /* chasing stones pass */
2901 store_dir(cave, x, y, grav, O_CHASING_STONE);
2902 store_dir(cave, x, y, oppos, O_SPACE);
2903 play_sound_of_element(cave, O_SLIME, x, y);
2907 /* or space over the slime? elements may pass from bottom to up then. */
2908 if (is_space_dir(cave, x, y, oppos))
2910 if (get_dir(cave, x, y, grav) == O_BLADDER)
2912 /* bladders move UP the slime */
2913 store_dir(cave, x, y, grav, O_SPACE);
2914 store_dir(cave, x, y, oppos, O_BLADDER_1);
2915 play_sound_of_element(cave, O_SLIME, x, y);
2917 else if (get_dir(cave, x, y, grav)==O_FLYING_STONE)
2919 store_dir(cave, x, y, grav, O_SPACE);
2920 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
2921 play_sound_of_element(cave, O_SLIME, x, y);
2923 else if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND)
2925 store_dir(cave, x, y, grav, O_SPACE);
2926 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
2927 play_sound_of_element(cave, O_SLIME, x, y);
2933 case O_FALLING_WALL:
2934 if (is_space_dir(cave, x, y, grav_compat))
2936 /* try falling if space under. */
2939 for (yy = y + 1; yy < y + cave->h; yy++)
2940 /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
2941 if (get(cave, x, yy) != O_SPACE)
2942 /* stop cycle when other than space */
2945 /* if scanning stopped by a player... start falling! */
2946 if (get(cave, x, yy) == O_PLAYER ||
2947 get(cave, x, yy) == O_PLAYER_GLUED ||
2948 get(cave, x, yy) == O_PLAYER_BOMB)
2950 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2951 /* no sound when the falling wall starts falling! */
2956 case O_FALLING_WALL_F:
2957 switch (get_dir(cave, x, y, grav_compat))
2960 case O_PLAYER_GLUED:
2962 /* if player under, it explodes - the falling wall, not the player! */
2963 explode(cave, x, y);
2967 /* continue falling */
2968 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2973 play_sound_of_element(cave, get(cave, x, y), x, y);
2974 store(cave, x, y, O_FALLING_WALL);
2980 * C O N V E Y O R B E L T S
2983 case O_CONVEYOR_RIGHT:
2984 case O_CONVEYOR_LEFT:
2985 /* only works if gravity is up or down!!! */
2986 /* first, check for gravity and running belts. */
2987 if (!cave->gravity_disabled && cave->conveyor_belts_active)
2989 const GdDirection *dir;
2992 /* decide direction */
2993 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
2994 if (cave->conveyor_belts_direction_changed)
2996 dir = left ? ccw_eighth : cw_eighth;
2998 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
2999 /* if gravity is normal, and the conveyor belt has something
3000 ABOVE which can be moved
3002 the gravity is up, so anything that should float now goes
3003 DOWN and touches the conveyor */
3004 if ((cave->gravity == GD_MV_DOWN &&
3005 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3006 (cave->gravity == GD_MV_UP &&
3007 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3009 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3010 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3012 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); /* move */
3013 store_dir(cave, x, y, GD_MV_UP, O_SPACE); /* and place a space. */
3017 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3018 if ((cave->gravity == GD_MV_UP &&
3019 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3020 (cave->gravity == GD_MV_DOWN &&
3021 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3023 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3024 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3026 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); /* move */
3027 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); /* and clear. */
3034 * S I M P L E C H A N G I N G; E X P L O S I O N S
3038 store(cave, x, y, cave->explosion_effect);
3042 store(cave, x, y, O_DIAMOND);
3046 store(cave, x, y, cave->diamond_birth_effect);
3050 store(cave, x, y, O_STONE);
3053 case O_NITRO_EXPL_4:
3054 store(cave, x, y, cave->nitro_explosion_effect);
3058 store(cave, x, y, cave->bomb_explosion_effect);
3061 case O_AMOEBA_2_EXPL_4:
3062 store(cave, x, y, cave->amoeba_2_explosion_effect);
3065 case O_GHOST_EXPL_4:
3067 static GdElement ghost_explode[] =
3069 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3070 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3071 O_WAITING_STONE, O_BITER_1
3074 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
3079 store(cave, x, y, O_STEEL);
3083 store(cave, x, y, O_CLOCK);
3087 explode(cave, x, y);
3090 case O_TRAPPED_DIAMOND:
3091 if (cave->diamond_key_collected)
3092 store(cave, x, y, O_DIAMOND);
3096 if (cave->gate_open) /* if no more diamonds needed */
3097 store(cave, x, y, O_OUTBOX); /* open outbox */
3100 case O_PRE_INVIS_OUTBOX:
3101 if (cave->gate_open) /* if no more diamonds needed */
3102 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
3106 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
3107 store(cave, x, y, O_PRE_PL_1);
3108 inbox_toggle = !inbox_toggle;
3112 store(cave, x, y, O_PLAYER);
3137 case O_GHOST_EXPL_1:
3138 case O_GHOST_EXPL_2:
3139 case O_GHOST_EXPL_3:
3149 case O_NITRO_EXPL_1:
3150 case O_NITRO_EXPL_2:
3151 case O_NITRO_EXPL_3:
3152 case O_AMOEBA_2_EXPL_1:
3153 case O_AMOEBA_2_EXPL_2:
3154 case O_AMOEBA_2_EXPL_3:
3155 /* simply the next identifier */
3174 found_water = TRUE; /* for sound */
3175 /* simply the next identifier */
3179 case O_BLADDER_SPENDER:
3180 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3182 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3183 store(cave, x, y, O_PRE_STEEL_1);
3184 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3189 /* other inanimate elements that do nothing */
3195 /* POSTPROCESSING */
3197 /* another scan-like routine: */
3198 /* short explosions (for example, in bd1) started with explode_2. */
3199 /* internally we use explode_1; and change it to explode_2 if needed. */
3200 if (cave->short_explosions)
3202 for (y = 0; y < cave->h; y++)
3204 for (x = 0; x < cave->w; x++)
3206 if (is_first_stage_of_explosion(cave, x, y))
3208 next(cave, x, y); /* select next frame of explosion */
3210 /* forget scanned flag immediately */
3211 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3217 /* finally: forget "scanned" flags for objects. */
3218 /* also, check for time penalties. */
3219 /* these is something like an effect table, but we do not really use one. */
3220 for (y = 0; y < cave->h; y++)
3222 for (x = 0; x < cave->w; x++)
3224 if (get(cave, x, y) & SCANNED)
3225 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3227 if (get(cave, x, y) == O_TIME_PENALTY)
3229 store(cave, x, y, O_GRAVESTONE);
3231 /* there is time penalty for destroying the voodoo */
3232 time_decrement_sec += cave->time_penalty;
3237 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3238 /* but we only do this, if a living player was found. if not yet, the setup
3239 routine coordinates are used */
3240 if (cave->player_state==GD_PL_LIVING)
3242 if (cave->active_is_first_found)
3244 /* to be 1stb compatible, we do everything backwards. */
3245 for (y = cave->h - 1; y >= 0; y--)
3247 for (x = cave->w - 1; x >= 0; x--)
3249 if (is_player(cave, x, y))
3251 /* here we remember the coordinates. */
3260 /* as in the original: look for the last one */
3261 for (y = 0; y < cave->h; y++)
3263 for (x = 0; x < cave->w; x++)
3265 if (is_player(cave, x, y))
3267 /* here we remember the coordinates. */
3276 /* record coordinates of player for chasing stone */
3277 for (i = 0; i < G_N_ELEMENTS(cave->px) - 1; i++)
3279 cave->px[i] = cave->px[i + 1];
3280 cave->py[i] = cave->py[i + 1];
3283 cave->px[G_N_ELEMENTS(cave->px) - 1] = cave->player_x;
3284 cave->py[G_N_ELEMENTS(cave->py) - 1] = cave->player_y;
3288 /* update timing calculated by iterating and counting elements */
3289 update_cave_speed(cave);
3291 /* cave 3 sounds. precedence is controlled by the sound_play function. */
3292 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3293 if (found_water && cave->water_sound)
3294 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3296 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3297 cave->magic_wall_sound);
3299 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3300 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3301 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3303 if (amoeba_sound && magic_sound)
3305 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3310 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3311 else if (magic_sound)
3312 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3317 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3318 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3319 play_sound_of_element(cave, O_AMOEBA, x, y);
3322 /* pneumatic hammer sound - overrides everything. */
3323 if (cave->pneumatic_hammer_active_delay > 0 && cave->pneumatic_hammer_sound)
3324 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3326 /* CAVE VARIABLES */
3330 /* check if player is alive. */
3331 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3332 cave->player_state = GD_PL_DIED;
3334 /* check if any voodoo exploded, and kill players the next scan if that happended. */
3335 if (cave->voodoo_touched)
3336 cave->kill_player = TRUE;
3340 /* check flags after evaluating. */
3341 if (cave->amoeba_state == GD_AM_AWAKE)
3343 if (amoeba_count >= cave->amoeba_max_count)
3344 cave->amoeba_state = GD_AM_TOO_BIG;
3345 if (amoeba_found_enclosed)
3346 cave->amoeba_state = GD_AM_ENCLOSED;
3349 /* amoeba 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_state = GD_AM_ENCLOSED;
3354 if (cave->amoeba_2_state == GD_AM_AWAKE)
3356 /* check flags after evaluating. */
3357 if (amoeba_2_count >= cave->amoeba_2_max_count)
3358 cave->amoeba_2_state = GD_AM_TOO_BIG;
3360 if (amoeba_2_found_enclosed)
3361 cave->amoeba_2_state = GD_AM_ENCLOSED;
3364 /* amoeba 2 can also be turned into diamond by magic wall */
3365 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3366 cave->amoeba_2_state = GD_AM_ENCLOSED;
3368 /* now check times. --------------------------- */
3369 /* decrement time if a voodoo was killed. */
3370 cave->time -= time_decrement_sec * cave->timing_factor;
3374 /* only decrement time when player is already born. */
3377 int secondsbefore, secondsafter;
3379 secondsbefore = cave->time / cave->timing_factor;
3380 cave->time -= cave->speed;
3381 if (cave->time <= 0)
3384 secondsafter = cave->time / cave->timing_factor;
3385 if (cave->time / cave->timing_factor < 10)
3386 /* if less than 10 seconds, no walking sound, but play explosion sound */
3387 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3389 if (secondsbefore != secondsafter)
3390 gd_cave_set_seconds_sound(cave);
3393 /* a gravity switch was activated; seconds counting down */
3394 if (cave->gravity_will_change > 0)
3396 cave->gravity_will_change -= cave->speed;
3397 if (cave->gravity_will_change < 0)
3398 cave->gravity_will_change = 0;
3400 if (cave->gravity_will_change == 0)
3402 cave->gravity = cave->gravity_next_direction;
3403 if (cave->gravity_change_sound)
3404 gd_sound_play(cave, GD_S_GRAVITY_CHANGE, O_GRAVITY_SWITCH, -1, -1); /* takes precedence over amoeba and magic wall sound */
3408 /* creatures direction automatically change */
3409 if (cave->creatures_direction_will_change > 0)
3411 cave->creatures_direction_will_change -= cave->speed;
3412 if (cave->creatures_direction_will_change < 0)
3413 cave->creatures_direction_will_change = 0;
3415 if (cave->creatures_direction_will_change == 0)
3417 if (cave->creature_direction_auto_change_sound)
3418 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3420 cave->creatures_backwards = !cave->creatures_backwards;
3421 cave->creatures_direction_will_change =
3422 cave->creatures_direction_auto_change_time * cave->timing_factor;
3426 /* magic wall; if active&wait or not wait for hatching */
3427 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3428 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3430 cave->magic_wall_time -= cave->speed;
3431 if (cave->magic_wall_time < 0)
3432 cave->magic_wall_time = 0;
3433 if (cave->magic_wall_time == 0)
3434 cave->magic_wall_state = GD_MW_EXPIRED;
3437 /* we may wait for hatching, when starting amoeba */
3438 if (cave->amoeba_timer_started_immediately ||
3439 (cave->amoeba_state == GD_AM_AWAKE &&
3440 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3442 cave->amoeba_time -= cave->speed;
3443 if (cave->amoeba_time < 0)
3444 cave->amoeba_time = 0;
3445 if (cave->amoeba_time == 0)
3446 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3449 /* we may wait for hatching, when starting amoeba */
3450 if (cave->amoeba_timer_started_immediately ||
3451 (cave->amoeba_2_state == GD_AM_AWAKE &&
3452 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3454 cave->amoeba_2_time -= cave->speed;
3455 if (cave->amoeba_2_time < 0)
3456 cave->amoeba_2_time = 0;
3457 if (cave->amoeba_2_time == 0)
3458 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3461 /* check for player hatching. */
3462 start_signal = FALSE;
3464 /* if not the c64 scheduling, but the correct frametime is used,
3465 hatching delay should always be decremented. */
3466 /* otherwise, the if (millisecs...) condition below will set this. */
3467 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3469 /* NON-C64 scheduling */
3470 if (cave->hatching_delay_frame > 0)
3472 /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3473 cave->hatching_delay_frame--;
3474 if (cave->hatching_delay_frame == 0)
3475 start_signal = TRUE;
3480 /* C64 scheduling */
3481 if (cave->hatching_delay_time > 0)
3483 /* for c64 schedulings, hatching delay means milliseconds. */
3484 cave->hatching_delay_time -= cave->speed;
3485 if (cave->hatching_delay_time <= 0)
3487 cave->hatching_delay_time = 0;
3488 start_signal = TRUE;
3493 /* if decremented hatching, and it became zero: */
3496 /* THIS IS THE CAVE START SIGNAL */
3498 /* record that now the cave is in its normal state */
3499 cave->hatched = TRUE;
3501 /* if diamonds needed is below zero, we count the available diamonds now. */
3502 gd_cave_count_diamonds(cave);
3504 /* setup direction auto change */
3505 if (cave->creatures_direction_auto_change_time)
3507 cave->creatures_direction_will_change =
3508 cave->creatures_direction_auto_change_time * cave->timing_factor;
3510 if (cave->creatures_direction_auto_change_on_start)
3511 cave->creatures_backwards = !cave->creatures_backwards;
3514 gd_sound_play(cave, GD_S_CRACK, O_INBOX, -1, -1);
3518 if (cave->biters_wait_frame == 0)
3519 cave->biters_wait_frame = cave->biter_delay_frame;
3521 cave->biters_wait_frame--;
3523 /* replicators delay */
3524 if (cave->replicators_wait_frame == 0)
3525 cave->replicators_wait_frame = cave->replicator_delay_frame;
3527 cave->replicators_wait_frame--;
3532 /* check if cave failed by timeout is done in main game engine */
3534 /* check if cave failed by timeout */
3535 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3537 gd_cave_clear_sounds(cave);
3538 cave->player_state = GD_PL_TIMEOUT;
3539 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3543 /* set these for drawing. */
3544 cave->last_direction = player_move;
3545 /* here we remember last movements for animation. this is needed here,
3546 as animation is in sync with the game, not the keyboard directly.
3547 (for example, after exiting the cave, the player was "running" in the
3548 original, till bonus points were counted for remaining time and so on. */
3549 if (player_move == GD_MV_LEFT ||
3550 player_move == GD_MV_UP_LEFT ||
3551 player_move == GD_MV_DOWN_LEFT)
3552 cave->last_horizontal_direction = GD_MV_LEFT;
3554 if (player_move == GD_MV_RIGHT ||
3555 player_move == GD_MV_UP_RIGHT ||
3556 player_move == GD_MV_DOWN_RIGHT)
3557 cave->last_horizontal_direction = GD_MV_RIGHT;
3559 cave->frame++; /* XXX */
3562 void set_initial_cave_speed(GdCave *cave)
3567 /* set cave get function; to implement perfect or lineshifting borders */
3568 if (cave->lineshift)
3569 cave->getp = getp_shift;
3571 cave->getp = getp_perfect;
3573 /* check whether to scan the first and last line */
3574 if (cave->border_scan_first_and_last)
3585 for (y = ymin; y <= ymax; y++)
3587 for (x = 0; x < cave->w; x++)
3589 /* add the ckdelay correction value for every element seen. */
3590 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3594 /* update timing calculated by iterating and counting elements */
3595 update_cave_speed(cave);