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, 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_CONVERT, 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_SPREAD, element, x, y);
200 gd_sound_play(cave, GD_S_BLADDER_MOVE, element, x, y);
207 gd_sound_play(cave, GD_S_BITER_EAT, 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_EXPLOSION, 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_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, 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_EXPLOSION, 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_EXPLOSION, 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_EXPLOSION, 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);
677 explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
679 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
683 player eats specified object.
684 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
685 returns other element if something other appears there and he can't move.
686 cave pointer is needed to know the diamond values.
688 static GdElement player_get_element (GdCave* cave, const GdElement object, int x, int y)
695 cave->diamond_key_collected = TRUE;
696 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECT, object, x, y);
701 gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
706 gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
711 gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
718 gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
725 gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
732 gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
737 case O_CREATURE_SWITCH: /* creatures change direction. */
738 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
739 cave->creatures_backwards = !cave->creatures_backwards;
742 case O_EXPANDING_WALL_SWITCH: /* expanding wall change direction. */
743 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
744 cave->expanding_wall_changed = !cave->expanding_wall_changed;
747 case O_BITER_SWITCH: /* biter change delay */
748 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
749 cave->biter_delay_frame++;
750 if (cave->biter_delay_frame == 4)
751 cave->biter_delay_frame = 0;
754 case O_REPLICATOR_SWITCH: /* replicator on/off switch */
755 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
756 cave->replicators_active = !cave->replicators_active;
759 case O_CONVEYOR_SWITCH: /* conveyor belts on/off */
760 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
761 cave->conveyor_belts_active = !cave->conveyor_belts_active;
764 case O_CONVEYOR_DIR_SWITCH: /* conveyor belts switch direction */
765 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
766 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
772 case O_STEEL_EATABLE:
773 case O_BRICK_EATABLE:
774 case O_DIRT_SLOPED_UP_RIGHT:
775 case O_DIRT_SLOPED_UP_LEFT:
776 case O_DIRT_SLOPED_DOWN_LEFT:
777 case O_DIRT_SLOPED_DOWN_RIGHT:
780 gd_sound_play(cave, GD_S_WALK_EARTH, object, x, y);
784 gd_sound_play(cave, GD_S_SWEET_COLLECT, object, x, y);
785 cave->sweet_eaten = TRUE;
788 case O_PNEUMATIC_HAMMER:
789 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECT, object, x, y);
790 cave->got_pneumatic_hammer = TRUE;
795 gd_sound_play(cave, GD_S_CLOCK_COLLECT, object, x, y);
796 cave->time += cave->time_bonus * cave->timing_factor;
797 if (cave->time > cave->max_time * cave->timing_factor)
798 cave->time -= cave->max_time * cave->timing_factor;
799 /* no space, rather a dirt remains there... */
803 case O_FLYING_DIAMOND:
804 // prevent diamond sounds for O_SKELETON (see below)
805 if (x != -1 && y != -1)
806 gd_sound_play(cave, GD_S_DIAMOND_COLLECT, object, x, y);
808 cave->score += cave->diamond_value;
809 cave->diamonds_collected++;
811 if (cave->diamonds_needed == cave->diamonds_collected)
813 cave->gate_open = TRUE;
815 /* extra is worth more points. */
816 cave->diamond_value = cave->extra_diamond_value;
818 cave->gate_open_flash = 1;
819 cave->sound3 = GD_S_CRACK;
820 gd_sound_play(cave, GD_S_CRACK, O_OUTBOX, x, y);
825 cave->skeletons_collected++;
827 /* as if player got a diamond */
828 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
829 player_get_element(cave, O_DIAMOND, -1, -1);
831 /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
832 gd_sound_play(cave, GD_S_SKELETON_COLLECT, object, x, y);
837 cave->player_state = GD_PL_EXITED; /* player now exits the cave! */
841 case O_LAVA: /* player goes into lava, as if it was space */
842 gd_sound_play(cave, GD_S_WALK_EMPTY, object, x, y);
846 /* the object will remain there. */
852 process a crazy dream-style teleporter.
853 called from gd_cave_iterate, for a player or a player_bomb.
854 player is standing at px, py, and trying to move in the direction player_move,
855 where there is a teleporter.
856 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
857 around). the first teleporter we find, and which is suitable, will be the destination.
858 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
860 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
869 /* jump to next element; wrap around columns and rows. */
881 /* if we found a teleporter... */
882 if (get(cave, tx, ty) == O_TELEPORTER &&
883 is_space_dir(cave, tx, ty, player_move))
885 /* new player appears near teleporter found */
886 store_dir(cave, tx, ty, player_move, get(cave, px, py));
888 /* current player disappears */
889 store(cave, px, py, O_SPACE);
891 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
893 return TRUE; /* return true as teleporter worked */
896 /* loop until we get back to original coordinates */
897 while (tx != px || ty != py);
899 /* return false as we did not find any usable teleporter */
904 try to push an element.
905 returns true if the push is possible; also does move the specified _element_.
906 up to the caller to move the _player_itself_.
908 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
911 GdElement what = get_dir(cave, x, y, player_move);
913 /* gravity for falling wall, bladder, ... */
914 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
920 case O_WAITING_STONE:
923 case O_CHASING_STONE:
927 /* pushing some kind of stone or nut */
928 /* directions possible: 90degrees cw or ccw to current gravity. */
929 /* only push if player dir is orthogonal to gravity,
930 ie. gravity down, pushing left & right possible */
931 if (player_move == ccw_fourth[cave->gravity] ||
932 player_move == cw_fourth[cave->gravity])
938 /* different probabilities for different elements. */
941 case O_WAITING_STONE:
942 /* waiting stones are light, can always push */
946 case O_CHASING_STONE:
947 /* chasing can be pushed if player is turbo */
948 if (cave->sweet_eaten)
953 /* mega may(!) be pushed if player is turbo */
954 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
962 if (cave->sweet_eaten)
963 prob = cave->pushing_stone_prob_sweet; /* probability with sweet */
965 prob = cave->pushing_stone_prob; /* probability without sweet. */
972 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
973 g_rand_int_range(cave->random, 0, 1000000) < prob)
975 /* if decided that he will be able to push, */
976 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
977 play_sound_of_element(cave, what, x, y);
992 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
993 * not an O_BLADDER_x. */
994 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
996 /* first check: we cannot push a bladder "up" */
997 if (player_move != opposite[grav_compat])
999 /* pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check. */
1000 /* player moving in the direction of gravity. */
1004 if (player_move == grav_compat)
1006 /* pushing bladder down */
1007 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1008 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1009 /* if no space to push down, maybe left (down-left to player) */
1010 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1012 /* left is "down, turned right (cw)" */
1013 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1014 /* if not, maybe right (down-right to player) */
1015 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1016 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1019 /* pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check. */
1023 else if (player_move == cw_fourth[grav_compat])
1025 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) /* pushing it left */
1026 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1027 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) /* maybe down, and player will move left */
1028 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1029 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) /* maybe up, and player will move left */
1030 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1033 /* pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check. */
1037 else if (player_move == ccw_fourth[grav_compat])
1039 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) /* pushing it right */
1040 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1041 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) /* maybe down, and player will move right */
1042 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1043 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) /* maybe up, and player will move right */
1044 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1048 play_sound_of_element(cave, O_BLADDER, x, y);
1053 /* a box is only pushed with the fire pressed */
1056 /* but always with 100% probability */
1057 switch (player_move)
1063 /* pushing in some dir, two steps in that dir - is there space? */
1064 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1067 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1069 gd_sound_play(cave, GD_S_BOX_PUSH, what, x, y);
1074 /* push in no other directions possible */
1080 /* pushing of other elements not possible */
1088 /* from the key press booleans, create a direction */
1089 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1091 GdDirection player_move;
1093 /* from the key press booleans, create a direction */
1095 player_move = GD_MV_UP_RIGHT;
1096 else if (down && right)
1097 player_move = GD_MV_DOWN_RIGHT;
1098 else if (down && left)
1099 player_move = GD_MV_DOWN_LEFT;
1100 else if (up && left)
1101 player_move = GD_MV_UP_LEFT;
1103 player_move = GD_MV_UP;
1105 player_move = GD_MV_DOWN;
1107 player_move = GD_MV_LEFT;
1109 player_move = GD_MV_RIGHT;
1111 player_move = GD_MV_STILL;
1116 /* clear these to no sound; and they will be set during iteration. */
1117 void gd_cave_clear_sounds(GdCave *cave)
1119 cave->sound1 = GD_S_NONE;
1120 cave->sound2 = GD_S_NONE;
1121 cave->sound3 = GD_S_NONE;
1124 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1125 GdElement falling_element)
1127 if (cave->gravity_disabled)
1130 if (is_space_dir(cave, x, y, falling_direction))
1132 /* beginning to fall */
1133 play_sound_of_element(cave, get(cave, x, y), x, y);
1134 move(cave, x, y, falling_direction, falling_element);
1137 /* check if it is on a sloped element, and it can roll. */
1138 /* for example, sloped wall looks like: */
1141 /* this is tagged as sloped up&left. */
1142 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1143 /* then check the direction to roll (left or right) */
1144 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1145 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1147 /* rolling down, if sitting on a sloped object */
1148 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1149 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1150 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1152 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1153 so here we use cw_fourth */
1154 play_sound_of_element(cave, get(cave, x, y), x, y);
1155 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1157 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1158 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1159 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1161 /* rolling right? */
1162 play_sound_of_element(cave, get(cave, x, y), x, y);
1163 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1168 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1170 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1171 cave->voodoo_dies_by_stone)
1173 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1174 explode_dir(cave, x, y, fall_dir);
1181 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1183 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1184 cave->voodoo_collects_diamonds)
1186 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1187 player_get_element(cave, O_DIAMOND, x, y); /* as if player got diamond */
1188 store(cave, x, y, O_SPACE); /* diamond disappears */
1195 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1196 GdDirection fall_dir, GdElement bouncing)
1198 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1199 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1202 store(cave, x, y, bouncing);
1203 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1205 gd_sound_play(cave, GD_S_NUT_CRACK, O_NUT, x, y);
1213 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1214 GdDirection fall_dir, GdElement magic)
1216 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1218 play_sound_of_element(cave, O_DIAMOND, x, y); /* always play diamond sound */
1220 if (cave->magic_wall_state==GD_MW_DORMANT)
1221 cave->magic_wall_state = GD_MW_ACTIVE;
1223 if (cave->magic_wall_state==GD_MW_ACTIVE &&
1224 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1226 /* if magic wall active and place underneath, it turns element
1227 into anything the effect says to do. */
1228 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1231 /* active or non-active or anything, element falling in will always disappear */
1232 store(cave, x, y, O_SPACE);
1240 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1242 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1244 explode_dir(cave, x, y, fall_dir);
1251 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1252 GdDirection fall_dir, GdElement bouncing)
1254 if (is_space_dir(cave, x, y, fall_dir))
1256 /* falling further */
1257 move(cave, x, y, fall_dir, get(cave, x, y));
1262 /* check if it is on a sloped element, and it can roll. */
1263 /* for example, sloped wall looks like: */
1266 /* this is tagged as sloped up&left. */
1267 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1268 /* then check the direction to roll (left or right) */
1269 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1271 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1273 /* sloped element, falling to left or right */
1274 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1275 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1276 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1278 play_sound_of_element(cave, get(cave, x, y), x, y);
1280 /* try to roll left first - see O_STONE to understand why cw_fourth */
1281 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1283 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1284 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1285 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1287 play_sound_of_element(cave, get(cave, x, y), x, y);
1289 /* if not, try to roll right */
1290 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1294 /* cannot roll in any direction, so it stops */
1295 play_sound_of_element(cave, get(cave, x, y), x, y);
1296 store(cave, x, y, bouncing);
1302 /* any other element, stops */
1303 play_sound_of_element(cave, get(cave, x, y), x, y);
1304 store(cave, x, y, bouncing);
1308 static void update_cave_speed(GdCave *cave)
1310 /* update timing calculated by iterating and counting elements which were slow to process on c64 */
1311 switch (cave->scheduling)
1313 case GD_SCHEDULING_MILLISECONDS:
1314 /* cave->speed already contains the milliseconds value, do not touch it */
1317 case GD_SCHEDULING_BD1:
1318 if (!cave->intermission)
1319 /* non-intermissions */
1320 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1322 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
1323 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1326 case GD_SCHEDULING_BD1_ATARI:
1327 /* about 20ms/frame faster than c64 version */
1328 if (!cave->intermission)
1329 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); /* non-intermissions */
1331 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); /* for intermissions */
1334 case GD_SCHEDULING_BD2:
1335 /* 60 is a guess. */
1336 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1339 case GD_SCHEDULING_PLCK:
1340 /* 65 is totally empty cave in construction kit, with delay = 0) */
1341 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1344 case GD_SCHEDULING_BD2_PLCK_ATARI:
1345 /* a really fast engine; timing works like c64 plck. */
1346 /* 40 ms was measured in the construction kit, with delay = 0 */
1347 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1350 case GD_SCHEDULING_CRDR:
1351 if (cave->hammered_walls_reappear) /* this made the engine very slow. */
1352 cave->ckdelay += 60000;
1353 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1356 case GD_SCHEDULING_MAX:
1361 /* process a cave. */
1362 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1366 /* for border scan */
1369 /* amoeba found to be enclosed. if not, this is cleared */
1370 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1372 /* counting the number of amoebas. after scan, check if too much */
1373 int amoeba_count, amoeba_2_count;
1375 /* cave scan found water - for sound */
1376 boolean found_water;
1378 boolean inbox_toggle;
1379 boolean start_signal;
1381 /* gravity for falling wall, bladder, ... */
1382 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1384 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1385 static const GdDirection creature_dir[] =
1392 static const GdDirection creature_chdir[] =
1399 int time_decrement_sec;
1401 /* biters eating elements preference, they try to go in this order */
1402 GdElement biter_try[] =
1409 boolean amoeba_sound, magic_sound;
1411 gd_cave_clear_sounds(cave);
1413 /* if diagonal movements not allowed, */
1414 /* horizontal movements have precedence. [BROADRIBB] */
1415 if (!cave->diagonal_movements)
1417 switch (player_move)
1419 case GD_MV_UP_RIGHT:
1420 case GD_MV_DOWN_RIGHT:
1421 player_move = GD_MV_RIGHT;
1425 case GD_MV_DOWN_LEFT:
1426 player_move = GD_MV_LEFT;
1430 /* no correction needed */
1435 /* set cave get function; to implement perfect or lineshifting borders */
1436 if (cave->lineshift)
1437 cave->getp = getp_shift;
1439 cave->getp = getp_perfect;
1441 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1442 if (cave->player_seen_ago < 100)
1443 cave->player_seen_ago++;
1445 if (cave->pneumatic_hammer_active_delay > 0)
1446 cave->pneumatic_hammer_active_delay--;
1448 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1449 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1450 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1451 inbox_toggle = cave->inbox_flash_toggle;
1453 if (cave->gate_open_flash > 0)
1454 cave->gate_open_flash--;
1456 /* score collected this frame */
1459 /* suicide only kills the active player */
1460 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1461 /* we must check if there is a player or not - he may have exploded or something like that */
1462 if (suicide && cave->player_state == GD_PL_LIVING &&
1463 is_player(cave, cave->player_x, cave->player_y))
1464 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1466 /* check for walls reappearing */
1467 if (cave->hammered_reappear)
1469 for (y = 0; y < cave->h; y++)
1471 for (x = 0; x < cave->w; x++)
1473 /* timer for the cell > 0? */
1474 if (cave->hammered_reappear[y][x]>0)
1476 /* decrease timer */
1477 cave->hammered_reappear[y][x]--;
1479 /* check if it became zero */
1480 if (cave->hammered_reappear[y][x] == 0)
1482 store(cave, x, y, O_BRICK);
1483 gd_sound_play(cave, GD_S_WALL_REAPPEAR, O_BRICK, x, y);
1490 /* variables to check during the scan */
1492 /* will be set to false if any of the amoeba is found free. */
1493 amoeba_found_enclosed = TRUE;
1494 amoeba_2_found_enclosed = TRUE;
1497 found_water = FALSE;
1499 time_decrement_sec = 0;
1501 /* check whether to scan the first and last line */
1502 if (cave->border_scan_first_and_last)
1513 /* the cave scan routine */
1514 for (y = ymin; y <= ymax; y++)
1516 for (x = 0; x < cave->w; x++)
1518 /* if we find a scanned element, change it to the normal one, and that's all. */
1519 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1520 if (get(cave, x, y)&SCANNED)
1522 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1527 /* add the ckdelay correction value for every element seen. */
1528 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1530 switch (get(cave, x, y))
1536 if (cave->kill_player)
1538 explode (cave, x, y);
1542 cave->player_seen_ago = 0;
1543 /* bd4 intermission caves have many players. so if one of them has exited,
1544 * do not change the flag anymore. so this if () is needed */
1545 if (cave->player_state!=GD_PL_EXITED)
1546 cave->player_state = GD_PL_LIVING;
1548 /* check for pneumatic hammer things */
1549 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right
1550 for hammer 5) stand on something */
1551 if (player_fire && cave->got_pneumatic_hammer &&
1552 is_space_dir(cave, x, y, player_move) &&
1553 !is_space_dir(cave, x, y, GD_MV_DOWN))
1555 if (player_move == GD_MV_LEFT &&
1556 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1558 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1559 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1560 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1561 break; /* finished. */
1564 if (player_move == GD_MV_RIGHT &&
1565 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1567 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1568 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1569 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1570 break; /* finished. */
1574 if (player_move != GD_MV_STILL)
1576 /* only do every check if he is not moving */
1577 GdElement what = get_dir(cave, x, y, player_move);
1578 GdElement remains = what;
1581 /* if we are 'eating' a teleporter, and the function returns true
1582 (teleporting worked), break here */
1583 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1586 /* try to push element; if successful, break */
1587 push = do_push(cave, x, y, player_move, player_fire);
1594 /* if its a bomb, remember he now has one. */
1595 /* we do not change the "remains" and "what" variables,
1596 so that part of the code will be ineffective */
1597 gd_sound_play(cave, GD_S_BOMB_COLLECT, what, x, y);
1598 store_dir(cave, x, y, player_move, O_SPACE);
1601 store(cave, x, y, O_PLAYER_BOMB);
1603 move(cave, x, y, player_move, O_PLAYER_BOMB);
1607 /* we do not change the "remains" and "what" variables,
1608 so that part of the code will be ineffective */
1609 if (!player_fire && !cave->gravity_switch_active &&
1610 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1612 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1613 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1614 cave->gravity_disabled = TRUE;
1618 case O_GRAVITY_SWITCH:
1619 /* (we cannot use player_get for this as it does not have player_move parameter) */
1620 /* only allow changing direction if the new dir is not diagonal */
1621 if (cave->gravity_switch_active &&
1622 (player_move == GD_MV_LEFT ||
1623 player_move == GD_MV_RIGHT ||
1624 player_move == GD_MV_UP ||
1625 player_move == GD_MV_DOWN))
1627 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1628 cave->gravity_will_change =
1629 cave->gravity_change_time * cave->timing_factor;
1630 cave->gravity_next_direction = player_move;
1631 cave->gravity_switch_active = FALSE;
1636 /* get element - process others.
1637 if cannot get, player_get_element will return the same */
1638 remains = player_get_element (cave, what, x, y);
1642 if (remains != what || remains == O_SPACE)
1644 /* if anything changed, apply the change. */
1646 /* if snapping anything and we have snapping explosions set.
1647 but these is not true for pushing. */
1648 if (remains == O_SPACE && player_fire && !push)
1649 remains = cave->snap_element;
1651 if (remains != O_SPACE || player_fire)
1652 /* if any other element than space, player cannot move.
1653 also if pressing fire, will not move. */
1654 store_dir(cave, x, y, player_move, remains);
1656 /* if space remains there, the player moves. */
1657 move(cave, x, y, player_move, O_PLAYER);
1663 /* much simpler; cannot steal stones */
1664 if (cave->kill_player)
1666 explode(cave, x, y);
1670 cave->player_seen_ago = 0;
1671 /* bd4 intermission caves have many players. so if one of them has exited,
1672 * do not change the flag anymore. so this if () is needed */
1673 if (cave->player_state != GD_PL_EXITED)
1674 cave->player_state = GD_PL_LIVING;
1676 if (player_move != GD_MV_STILL)
1678 /* if the player does not move, nothing to do */
1679 GdElement what = get_dir(cave, x, y, player_move);
1680 GdElement remains = what;
1684 /* placing a bomb into empty space or dirt */
1685 if (is_space_dir(cave, x, y, player_move) ||
1686 is_element_dir(cave, x, y, player_move, O_DIRT))
1688 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1690 /* placed bomb, he is normal player again */
1691 store(cave, x, y, O_PLAYER);
1692 gd_sound_play(cave, GD_S_BOMB_PLACE, O_BOMB, x, y);
1697 /* pushing and collecting */
1698 /* if we are 'eating' a teleporter, and the function returns true
1699 (teleporting worked), break here */
1700 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1703 /* player fire is false... */
1704 if (do_push(cave, x, y, player_move, FALSE))
1710 case O_GRAVITY_SWITCH:
1711 /* (we cannot use player_get for this as it does not have
1712 player_move parameter) */
1713 /* only allow changing direction if the new dir is not diagonal */
1714 if (cave->gravity_switch_active &&
1715 (player_move==GD_MV_LEFT ||
1716 player_move==GD_MV_RIGHT ||
1717 player_move==GD_MV_UP ||
1718 player_move==GD_MV_DOWN))
1720 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1721 cave->gravity_will_change =
1722 cave->gravity_change_time * cave->timing_factor;
1723 cave->gravity_next_direction = player_move;
1724 cave->gravity_switch_active = FALSE;
1729 /* get element. if cannot get, player_get_element will return the same */
1730 remains = player_get_element (cave, what, x, y);
1735 /* if element changed, OR there is space, move. */
1736 if (remains != what || remains == O_SPACE)
1738 /* if anything changed, apply the change. */
1739 move(cave, x, y, player_move, O_PLAYER_BOMB);
1744 case O_PLAYER_STIRRING:
1745 if (cave->kill_player)
1747 explode(cave, x, y);
1751 /* stirring sound, if no other walking sound or explosion */
1752 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1754 cave->player_seen_ago = 0;
1755 /* bd4 intermission caves have many players. so if one of them has exited,
1756 * do not change the flag anymore. so this if () is needed */
1757 if (cave->player_state!=GD_PL_EXITED)
1758 cave->player_state = GD_PL_LIVING;
1762 /* player "exits" stirring the pot by pressing fire */
1763 cave->gravity_disabled = FALSE;
1764 store(cave, x, y, O_PLAYER);
1765 cave->gravity_switch_active = TRUE;
1769 /* player holding pneumatic hammer */
1770 case O_PLAYER_PNEUMATIC_LEFT:
1771 case O_PLAYER_PNEUMATIC_RIGHT:
1772 /* usual player stuff */
1773 if (cave->kill_player)
1775 explode(cave, x, y);
1779 cave->player_seen_ago = 0;
1780 if (cave->player_state!=GD_PL_EXITED)
1781 cave->player_state = GD_PL_LIVING;
1783 /* if hammering time is up, becomes a normal player again. */
1784 if (cave->pneumatic_hammer_active_delay == 0)
1785 store(cave, x, y, O_PLAYER);
1788 /* the active pneumatic hammer itself */
1789 case O_PNEUMATIC_ACTIVE_RIGHT:
1790 case O_PNEUMATIC_ACTIVE_LEFT:
1791 if (cave->pneumatic_hammer_active_delay == 0)
1795 /* pneumatic hammer element disappears */
1796 store(cave, x, y, O_SPACE);
1798 /* which is the new element which appears after that one is hammered? */
1799 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1801 /* if there is a new element, display it */
1802 /* O_NONE might be returned, for example if the element being
1803 hammered explodes during hammering (by a nearby explosion) */
1804 if (new_elem != O_NONE)
1806 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1808 /* and if walls reappear, remember it in array */
1809 if (cave->hammered_walls_reappear)
1813 wall_y = (y + 1) % cave->h;
1814 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1821 * S T O N E S, D I A M O N D S
1823 case O_STONE: /* standing stone */
1824 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1827 case O_MEGA_STONE: /* standing mega_stone */
1828 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1831 case O_DIAMOND: /* standing diamond */
1832 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1835 case O_NUT: /* standing nut */
1836 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1839 case O_DIRT_BALL: /* standing dirt ball */
1840 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1843 case O_DIRT_LOOSE: /* standing loose dirt */
1844 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1847 case O_FLYING_STONE: /* standing stone */
1848 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1851 case O_FLYING_DIAMOND: /* standing diamond */
1852 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1856 * 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
1858 case O_DIRT_BALL_F: /* falling dirt ball */
1859 if (!cave->gravity_disabled)
1860 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
1863 case O_DIRT_LOOSE_F: /* falling loose dirt */
1864 if (!cave->gravity_disabled)
1865 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
1868 case O_STONE_F: /* falling stone */
1869 if (!cave->gravity_disabled)
1871 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1874 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
1877 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
1880 if (do_fall_try_crush(cave, x, y, cave->gravity))
1883 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
1887 case O_MEGA_STONE_F: /* falling mega */
1888 if (!cave->gravity_disabled)
1890 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1893 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
1896 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
1899 if (do_fall_try_crush(cave, x, y, cave->gravity))
1902 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
1906 case O_DIAMOND_F: /* falling diamond */
1907 if (!cave->gravity_disabled)
1909 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
1912 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
1915 if (do_fall_try_crush(cave, x, y, cave->gravity))
1918 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
1922 case O_NUT_F: /* falling nut */
1923 if (!cave->gravity_disabled)
1925 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
1928 if (do_fall_try_crush(cave, x, y, cave->gravity))
1931 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
1935 case O_FLYING_STONE_F: /* falling stone */
1936 if (!cave->gravity_disabled)
1938 GdDirection fall_dir = opposite[cave->gravity];
1940 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
1943 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
1946 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
1949 if (do_fall_try_crush(cave, x, y, fall_dir))
1952 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
1956 case O_FLYING_DIAMOND_F: /* falling diamond */
1957 if (!cave->gravity_disabled)
1959 GdDirection fall_dir = opposite[cave->gravity];
1961 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
1964 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
1967 if (do_fall_try_crush(cave, x, y, fall_dir))
1970 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
1977 case O_NITRO_PACK: /* standing nitro pack */
1978 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
1981 case O_NITRO_PACK_F: /* falling nitro pack */
1982 if (!cave->gravity_disabled)
1984 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
1985 move(cave, x, y, cave->gravity, get(cave, x, y));
1986 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
1988 /* try magic wall; if true, function did the work */
1990 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
1992 /* falling on a dirt, it does NOT explode - just stops at its place. */
1993 play_sound_of_element(cave, O_NITRO_PACK, x, y);
1994 store(cave, x, y, O_NITRO_PACK);
1997 /* falling on any other element it explodes */
1998 explode(cave, x, y);
2002 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
2003 explode(cave, x, y);
2014 /* if cannot move in any direction, becomes an enclosed cow */
2015 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2016 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2017 store(cave, x, y, O_COW_ENCLOSED_1);
2020 /* THIS IS THE CREATURE MOVE thing copied. */
2021 const GdDirection *creature_move;
2022 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2023 GdElement base; /* base element number (which is like O_***_1) */
2024 int dir, dirn, dirp; /* direction */
2028 dir = get(cave, x, y)-base; /* facing where */
2029 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2031 /* now change direction if backwards */
2032 if (cave->creatures_backwards)
2037 dirn = (dir + 3) & 3; /* fast turn */
2038 dirp = (dir + 1) & 3; /* slow turn */
2042 dirn = (dir + 1) & 3; /* fast turn */
2043 dirp = (dir + 3) & 3; /* slow turn */
2046 if (is_space_dir(cave, x, y, creature_move[dirn]))
2047 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2048 else if (is_space_dir(cave, x, y, creature_move[dir]))
2049 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2051 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2055 /* enclosed cows wait some time before turning to a skeleton */
2056 case O_COW_ENCLOSED_1:
2057 case O_COW_ENCLOSED_2:
2058 case O_COW_ENCLOSED_3:
2059 case O_COW_ENCLOSED_4:
2060 case O_COW_ENCLOSED_5:
2061 case O_COW_ENCLOSED_6:
2062 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2063 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2064 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2065 is_space_dir(cave, x, y, GD_MV_DOWN))
2066 store(cave, x, y, O_COW_1);
2071 case O_COW_ENCLOSED_7:
2072 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2073 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2074 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2075 is_space_dir(cave, x, y, GD_MV_DOWN))
2076 store(cave, x, y, O_COW_1);
2078 store(cave, x, y, O_SKELETON);
2085 case O_ALT_FIREFLY_1:
2086 case O_ALT_FIREFLY_2:
2087 case O_ALT_FIREFLY_3:
2088 case O_ALT_FIREFLY_4:
2093 case O_ALT_BUTTER_1:
2094 case O_ALT_BUTTER_2:
2095 case O_ALT_BUTTER_3:
2096 case O_ALT_BUTTER_4:
2101 /* check if touches a voodoo */
2102 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2103 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2104 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2105 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2106 cave->voodoo_touched = TRUE;
2108 /* check if touches something bad and should explode (includes voodoo by the flags) */
2109 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2110 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2111 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2112 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2113 explode (cave, x, y);
2114 /* otherwise move */
2117 const GdDirection *creature_move;
2118 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2119 GdElement base; /* base element number (which is like O_***_1) */
2120 int dir, dirn, dirp; /* direction */
2122 if (get(cave, x, y) >= O_FIREFLY_1 &&
2123 get(cave, x, y) <= O_FIREFLY_4)
2125 else if (get(cave, x, y) >= O_BUTTER_1 &&
2126 get(cave, x, y) <= O_BUTTER_4)
2128 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2129 get(cave, x, y) <= O_STONEFLY_4)
2130 base = O_STONEFLY_1;
2131 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2132 get(cave, x, y) <= O_ALT_FIREFLY_4)
2133 base = O_ALT_FIREFLY_1;
2134 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2135 get(cave, x, y) <= O_ALT_BUTTER_4)
2136 base = O_ALT_BUTTER_1;
2138 dir = get(cave, x, y)-base; /* facing where */
2139 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2141 /* now change direction if backwards */
2142 if (cave->creatures_backwards)
2147 dirn = (dir + 3) & 3; /* fast turn */
2148 dirp = (dir + 1) & 3; /* slow turn */
2152 dirn = (dir + 1) & 3; /* fast turn */
2153 dirp = (dir + 3) & 3; /* slow turn */
2156 if (is_space_dir(cave, x, y, creature_move[dirn]))
2157 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2158 else if (is_space_dir(cave, x, y, creature_move[dir]))
2159 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2161 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2165 case O_WAITING_STONE:
2166 if (is_space_dir(cave, x, y, grav_compat))
2168 /* beginning to fall */
2170 move(cave, x, y, grav_compat, O_CHASING_STONE);
2172 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2174 /* rolling down a brick wall or a stone */
2175 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2176 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2177 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2179 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2180 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2182 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2183 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2184 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2186 /* or maybe right */
2187 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2192 case O_CHASING_STONE:
2194 int px = cave->px[0];
2195 int py = cave->py[0];
2196 boolean horizontal = g_rand_boolean(cave->random);
2197 boolean dont_move = FALSE;
2200 /* try to move... */
2205 /*********************************/
2206 /* check for a horizontal movement */
2209 /* if coordinates are the same */
2211 horizontal = !horizontal;
2218 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2220 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2224 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2226 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2235 horizontal = !horizontal;
2243 /********************************/
2244 /* check for a vertical movement */
2247 /* if coordinates are the same */
2249 horizontal = !horizontal;
2255 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2257 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2261 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2263 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2272 horizontal = !horizontal;
2285 /* if we should move in both directions, but can not move in any, stop. */
2290 /* check for horizontal */
2293 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2294 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2295 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2296 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2297 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2298 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2302 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2303 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2304 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2305 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2306 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2307 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2312 /* check for vertical */
2315 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2316 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2317 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2318 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2319 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2320 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2324 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2325 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2326 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2327 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2328 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2329 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2337 if (cave->replicators_wait_frame == 0 &&
2338 cave->replicators_active &&
2339 !cave->gravity_disabled)
2341 /* only replicate, if space is under it. */
2342 /* do not replicate players! */
2343 /* also obeys gravity settings. */
2344 /* only replicate element if it is not a scanned one */
2345 /* do not replicate space... that condition looks like it
2346 makes no sense, but otherwise it generates SCANNED spaces,
2347 which cannot be "collected" by the player, so he cannot run
2348 under a replicator */
2349 if (is_space_dir(cave, x, y, cave->gravity) &&
2350 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2351 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2353 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2354 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2363 if (cave->biters_wait_frame == 0)
2365 static GdDirection biter_move[] =
2373 /* direction, last two bits 0..3 */
2374 int dir = get(cave, x, y) - O_BITER_1;
2375 int dirn = (dir + 3) & 3;
2376 int dirp = (dir + 1) & 3;
2378 GdElement made_sound_of = O_NONE;
2380 for (i = 0; i < G_N_ELEMENTS (biter_try); i++)
2382 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2384 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2385 if (biter_try[i] != O_SPACE)
2386 made_sound_of = O_BITER_1; /* sound of a biter eating */
2389 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2391 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2392 if (biter_try[i] != O_SPACE)
2393 made_sound_of = O_BITER_1; /* sound of a biter eating */
2396 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2398 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2399 if (biter_try[i] != O_SPACE)
2400 made_sound_of = O_BITER_1; /* sound of a biter eating */
2405 if (i == G_N_ELEMENTS(biter_try))
2406 /* i = number of elements in array: could not move, so just turn */
2407 store(cave, x, y, O_BITER_1 + dirp);
2408 else if (biter_try[i] == O_STONE)
2410 /* if there was a stone there, where we moved...
2411 do not eat stones, just throw them back */
2412 store(cave, x, y, O_STONE);
2413 made_sound_of = O_STONE;
2416 /* if biter did move, we had sound. play it. */
2417 if (made_sound_of != O_NONE)
2418 play_sound_of_element(cave, made_sound_of, x, y);
2426 /* check if touches a voodoo */
2427 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2428 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2429 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2430 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2431 cave->voodoo_touched = TRUE;
2433 /* check if touches something bad and should explode (includes voodoo by the flags) */
2434 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2435 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2436 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2437 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2438 explode (cave, x, y);
2439 /* otherwise move */
2442 const GdDirection *creature_move;
2443 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2444 GdElement base = O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2445 int dir, dirn; /* direction */
2447 dir = get(cave, x, y)-base; /* facing where */
2448 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2450 /* now change direction if backwards */
2451 if (cave->creatures_backwards)
2455 dirn = (dir + 3) & 3; /* fast turn */
2457 dirn = (dir + 1) & 3; /* fast turn */
2459 /* if can move forward, does so. */
2460 if (is_space_dir(cave, x, y, creature_move[dir]))
2461 move(cave, x, y, creature_move[dir], base + dir);
2463 /* otherwise turns 90 degrees in place. */
2464 store(cave, x, y, base + dirn);
2469 store(cave, x, y, O_BLADDER_1);
2480 /* bladder with any delay state: try to convert to clock. */
2481 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2482 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))
2484 /* if touches the specified element, let it be a clock */
2485 store(cave, x, y, O_PRE_CLOCK_1);
2487 /* plays the bladder convert sound */
2488 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2492 /* is space over the bladder? */
2493 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2495 if (get(cave, x, y)==O_BLADDER_8)
2497 /* if it is a bladder 8, really move up */
2498 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2499 play_sound_of_element(cave, O_BLADDER, x, y);
2502 /* if smaller delay, just increase delay. */
2506 /* if not space, is something sloped over the bladder? */
2507 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2508 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2510 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2511 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2512 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2514 /* rolling up, to left */
2515 if (get(cave, x, y) == O_BLADDER_8)
2517 /* if it is a bladder 8, really roll */
2518 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2519 play_sound_of_element(cave, O_BLADDER, x, y);
2522 /* if smaller delay, just increase delay. */
2525 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2526 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2527 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2529 /* rolling up, to left */
2530 if (get(cave, x, y) == O_BLADDER_8)
2532 /* if it is a bladder 8, really roll */
2533 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2534 play_sound_of_element(cave, O_BLADDER, x, y);
2537 /* if smaller delay, just increase delay. */
2542 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2544 store(cave, x, y, O_BLADDER_1);
2549 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2550 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2551 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2552 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2553 explode (cave, x, y);
2558 /* the ghost is given four possibilities to move. */
2559 for (i = 0; i < 4; i++)
2561 static GdDirection dirs[] =
2568 GdDirection random_dir;
2570 random_dir = dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2571 if (is_space_dir(cave, x, y, random_dir))
2573 move(cave, x, y, random_dir, O_GHOST);
2574 break; /* ghost did move -> exit loop */
2581 * A C T I V E E L E M E N T S
2586 switch (cave->amoeba_state)
2589 store(cave, x, y, cave->amoeba_too_big_effect);
2592 case GD_AM_ENCLOSED:
2593 store(cave, x, y, cave->amoeba_enclosed_effect);
2596 case GD_AM_SLEEPING:
2598 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2599 if (amoeba_found_enclosed)
2600 /* if still found enclosed, check all four directions,
2601 if this one is able to grow. */
2602 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2603 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2604 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2605 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2607 /* not enclosed. this is a local (per scan) flag! */
2608 amoeba_found_enclosed = FALSE;
2609 cave->amoeba_state = GD_AM_AWAKE;
2612 /* if alive, check in which dir to grow (or not) */
2613 if (cave->amoeba_state==GD_AM_AWAKE)
2615 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2617 switch (g_rand_int_range(cave->random, 0, 4))
2619 /* decided to grow, choose a random direction. */
2620 case 0: /* let this be up. numbers indifferent. */
2621 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2622 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2626 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2627 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2631 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2632 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2636 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2637 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2649 /* check if it is touching an amoeba, and explosion is enabled */
2650 if (cave->amoeba_2_explodes_by_amoeba &&
2651 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2652 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2653 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2654 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2655 explode (cave, x, y);
2657 switch (cave->amoeba_2_state)
2660 store(cave, x, y, cave->amoeba_2_too_big_effect);
2663 case GD_AM_ENCLOSED:
2664 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2667 case GD_AM_SLEEPING:
2669 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2670 if (amoeba_2_found_enclosed)
2671 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2672 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2673 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2674 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2676 /* not enclosed. this is a local (per scan) flag! */
2677 amoeba_2_found_enclosed = FALSE;
2678 cave->amoeba_2_state = GD_AM_AWAKE;
2681 /* if it is alive, decide if it attempts to grow */
2682 if (cave->amoeba_2_state == GD_AM_AWAKE)
2683 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2685 switch (g_rand_int_range(cave->random, 0, 4))
2687 /* decided to grow, choose a random direction. */
2688 case 0: /* let this be up. numbers indifferent. */
2689 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2690 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2694 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2695 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2699 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2700 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2704 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2705 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2715 /* choose randomly, if it spreads */
2716 if (g_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2718 /* the current one explodes */
2719 store(cave, x, y, cave->acid_turns_to);
2721 /* and if neighbours are eaten, put acid there. */
2722 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2724 play_sound_of_element(cave, O_ACID, x, y);
2725 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2728 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2730 play_sound_of_element(cave, O_ACID, x, y);
2731 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2734 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2736 play_sound_of_element(cave, O_ACID, x, y);
2737 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2740 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2742 play_sound_of_element(cave, O_ACID, x, y);
2743 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2750 if (!cave->water_does_not_flow_down &&
2751 is_space_dir(cave, x, y, GD_MV_DOWN))
2752 /* emulating the odd behaviour in crdr */
2753 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2755 if (is_space_dir(cave, x, y, GD_MV_UP))
2756 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2758 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2759 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2761 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2762 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2766 store(cave, x, y, O_WATER);
2769 case O_H_EXPANDING_WALL:
2770 case O_V_EXPANDING_WALL:
2771 case O_H_EXPANDING_STEEL_WALL:
2772 case O_V_EXPANDING_STEEL_WALL:
2773 /* checks first if direction is changed. */
2774 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2775 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2776 !cave->expanding_wall_changed) ||
2777 ((get(cave, x, y)==O_V_EXPANDING_WALL ||
2778 get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) &&
2779 cave->expanding_wall_changed))
2781 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2783 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2784 play_sound_of_element(cave, get(cave, x, y), x, y);
2787 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2788 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2789 play_sound_of_element(cave, get(cave, x, y), x, y);
2794 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2795 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2796 play_sound_of_element(cave, get(cave, x, y), x, y);
2799 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2800 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2801 play_sound_of_element(cave, get(cave, x, y), x, y);
2806 case O_EXPANDING_WALL:
2807 case O_EXPANDING_STEEL_WALL:
2808 /* the wall which grows in all four directions. */
2809 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2811 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2812 play_sound_of_element(cave, get(cave, x, y), x, y);
2815 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2816 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2817 play_sound_of_element(cave, get(cave, x, y), x, y);
2820 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2821 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2822 play_sound_of_element(cave, get(cave, x, y), x, y);
2825 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2826 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2827 play_sound_of_element(cave, get(cave, x, y), x, y);
2833 ; // to make compilers happy ...
2835 g_print("Step[%03d]", cave->frame); /* XXX */
2837 int rrr = gd_cave_c64_random(cave);
2840 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2841 (rrr & cave->slime_permeability_c64) == 0);
2844 * unpredictable: g_rand_int
2845 * predictable: c64 predictable random generator.
2846 * for predictable, a random number is generated,
2847 * whether or not it is even possible that the stone will be able to pass.
2849 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : g_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2851 GdDirection grav = cave->gravity;
2852 GdDirection oppos = opposite[cave->gravity];
2854 /* space under the slime? elements may pass from top to bottom then. */
2855 if (is_space_dir(cave, x, y, grav))
2857 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
2859 /* output a falling xy under */
2860 store_dir(cave, x, y, grav, cave->slime_converts_1);
2862 store_dir(cave, x, y, oppos, O_SPACE);
2863 play_sound_of_element(cave, O_SLIME, x, y);
2865 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
2867 store_dir(cave, x, y, grav, cave->slime_converts_2);
2868 store_dir(cave, x, y, oppos, O_SPACE);
2869 play_sound_of_element(cave, O_SLIME, x, y);
2871 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
2873 store_dir(cave, x, y, grav, cave->slime_converts_3);
2874 store_dir(cave, x, y, oppos, O_SPACE);
2875 play_sound_of_element(cave, O_SLIME, x, y);
2877 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
2879 /* waiting stones pass without awakening */
2880 store_dir(cave, x, y, grav, O_WAITING_STONE);
2881 store_dir(cave, x, y, oppos, O_SPACE);
2882 play_sound_of_element(cave, O_SLIME, x, y);
2884 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
2886 /* chasing stones pass */
2887 store_dir(cave, x, y, grav, O_CHASING_STONE);
2888 store_dir(cave, x, y, oppos, O_SPACE);
2889 play_sound_of_element(cave, O_SLIME, x, y);
2893 /* or space over the slime? elements may pass from bottom to up then. */
2894 if (is_space_dir(cave, x, y, oppos))
2896 if (get_dir(cave, x, y, grav) == O_BLADDER)
2898 /* bladders move UP the slime */
2899 store_dir(cave, x, y, grav, O_SPACE);
2900 store_dir(cave, x, y, oppos, O_BLADDER_1);
2901 play_sound_of_element(cave, O_SLIME, x, y);
2903 else if (get_dir(cave, x, y, grav)==O_FLYING_STONE)
2905 store_dir(cave, x, y, grav, O_SPACE);
2906 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
2907 play_sound_of_element(cave, O_SLIME, x, y);
2909 else if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND)
2911 store_dir(cave, x, y, grav, O_SPACE);
2912 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
2913 play_sound_of_element(cave, O_SLIME, x, y);
2919 case O_FALLING_WALL:
2920 if (is_space_dir(cave, x, y, grav_compat))
2922 /* try falling if space under. */
2925 for (yy = y + 1; yy < y + cave->h; yy++)
2926 /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
2927 if (get(cave, x, yy) != O_SPACE)
2928 /* stop cycle when other than space */
2931 /* if scanning stopped by a player... start falling! */
2932 if (get(cave, x, yy) == O_PLAYER ||
2933 get(cave, x, yy) == O_PLAYER_GLUED ||
2934 get(cave, x, yy) == O_PLAYER_BOMB)
2936 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2937 /* no sound when the falling wall starts falling! */
2942 case O_FALLING_WALL_F:
2943 switch (get_dir(cave, x, y, grav_compat))
2946 case O_PLAYER_GLUED:
2948 /* if player under, it explodes - the falling wall, not the player! */
2949 explode(cave, x, y);
2953 /* continue falling */
2954 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2959 play_sound_of_element(cave, get(cave, x, y), x, y);
2960 store(cave, x, y, O_FALLING_WALL);
2966 * C O N V E Y O R B E L T S
2969 case O_CONVEYOR_RIGHT:
2970 case O_CONVEYOR_LEFT:
2971 /* only works if gravity is up or down!!! */
2972 /* first, check for gravity and running belts. */
2973 if (!cave->gravity_disabled && cave->conveyor_belts_active)
2975 const GdDirection *dir;
2978 /* decide direction */
2979 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
2980 if (cave->conveyor_belts_direction_changed)
2982 dir = left ? ccw_eighth : cw_eighth;
2984 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
2985 /* if gravity is normal, and the conveyor belt has something
2986 ABOVE which can be moved
2988 the gravity is up, so anything that should float now goes
2989 DOWN and touches the conveyor */
2990 if ((cave->gravity == GD_MV_DOWN &&
2991 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
2992 (cave->gravity == GD_MV_UP &&
2993 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
2995 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
2996 is_space_dir(cave, x, y, dir[GD_MV_UP]))
2998 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); /* move */
2999 store_dir(cave, x, y, GD_MV_UP, O_SPACE); /* and place a space. */
3003 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3004 if ((cave->gravity == GD_MV_UP &&
3005 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3006 (cave->gravity == GD_MV_DOWN &&
3007 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3009 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3010 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3012 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); /* move */
3013 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); /* and clear. */
3020 * S I M P L E C H A N G I N G; E X P L O S I O N S
3024 store(cave, x, y, cave->explosion_effect);
3028 store(cave, x, y, O_DIAMOND);
3032 store(cave, x, y, cave->diamond_birth_effect);
3036 store(cave, x, y, O_STONE);
3039 case O_NITRO_EXPL_4:
3040 store(cave, x, y, cave->nitro_explosion_effect);
3044 store(cave, x, y, cave->bomb_explosion_effect);
3047 case O_AMOEBA_2_EXPL_4:
3048 store(cave, x, y, cave->amoeba_2_explosion_effect);
3051 case O_GHOST_EXPL_4:
3053 static GdElement ghost_explode[] =
3055 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3056 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3057 O_WAITING_STONE, O_BITER_1
3060 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
3065 store(cave, x, y, O_STEEL);
3069 store(cave, x, y, O_CLOCK);
3073 explode(cave, x, y);
3076 case O_TRAPPED_DIAMOND:
3077 if (cave->diamond_key_collected)
3078 store(cave, x, y, O_DIAMOND);
3082 if (cave->gate_open) /* if no more diamonds needed */
3083 store(cave, x, y, O_OUTBOX); /* open outbox */
3086 case O_PRE_INVIS_OUTBOX:
3087 if (cave->gate_open) /* if no more diamonds needed */
3088 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
3092 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
3093 store(cave, x, y, O_PRE_PL_1);
3094 inbox_toggle = !inbox_toggle;
3098 store(cave, x, y, O_PLAYER);
3123 case O_GHOST_EXPL_1:
3124 case O_GHOST_EXPL_2:
3125 case O_GHOST_EXPL_3:
3135 case O_NITRO_EXPL_1:
3136 case O_NITRO_EXPL_2:
3137 case O_NITRO_EXPL_3:
3138 case O_AMOEBA_2_EXPL_1:
3139 case O_AMOEBA_2_EXPL_2:
3140 case O_AMOEBA_2_EXPL_3:
3141 /* simply the next identifier */
3160 found_water = TRUE; /* for sound */
3161 /* simply the next identifier */
3165 case O_BLADDER_SPENDER:
3166 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3168 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3169 store(cave, x, y, O_PRE_STEEL_1);
3170 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3175 /* other inanimate elements that do nothing */
3181 /* POSTPROCESSING */
3183 /* another scan-like routine: */
3184 /* short explosions (for example, in bd1) started with explode_2. */
3185 /* internally we use explode_1; and change it to explode_2 if needed. */
3186 if (cave->short_explosions)
3188 for (y = 0; y < cave->h; y++)
3190 for (x = 0; x < cave->w; x++)
3192 if (is_first_stage_of_explosion(cave, x, y))
3194 next(cave, x, y); /* select next frame of explosion */
3196 /* forget scanned flag immediately */
3197 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3203 /* finally: forget "scanned" flags for objects. */
3204 /* also, check for time penalties. */
3205 /* these is something like an effect table, but we do not really use one. */
3206 for (y = 0; y < cave->h; y++)
3208 for (x = 0; x < cave->w; x++)
3210 if (get(cave, x, y) & SCANNED)
3211 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3213 if (get(cave, x, y) == O_TIME_PENALTY)
3215 store(cave, x, y, O_GRAVESTONE);
3217 /* there is time penalty for destroying the voodoo */
3218 time_decrement_sec += cave->time_penalty;
3223 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3224 /* but we only do this, if a living player was found. if not yet, the setup
3225 routine coordinates are used */
3226 if (cave->player_state==GD_PL_LIVING)
3228 if (cave->active_is_first_found)
3230 /* to be 1stb compatible, we do everything backwards. */
3231 for (y = cave->h - 1; y >= 0; y--)
3233 for (x = cave->w - 1; x >= 0; x--)
3235 if (is_player(cave, x, y))
3237 /* here we remember the coordinates. */
3246 /* as in the original: look for the last one */
3247 for (y = 0; y < cave->h; y++)
3249 for (x = 0; x < cave->w; x++)
3251 if (is_player(cave, x, y))
3253 /* here we remember the coordinates. */
3262 /* record coordinates of player for chasing stone */
3263 for (i = 0; i < G_N_ELEMENTS(cave->px) - 1; i++)
3265 cave->px[i] = cave->px[i + 1];
3266 cave->py[i] = cave->py[i + 1];
3269 cave->px[G_N_ELEMENTS(cave->px) - 1] = cave->player_x;
3270 cave->py[G_N_ELEMENTS(cave->py) - 1] = cave->player_y;
3274 /* update timing calculated by iterating and counting elements */
3275 update_cave_speed(cave);
3277 /* cave 3 sounds. precedence is controlled by the sound_play function. */
3278 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3280 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3282 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3283 cave->magic_wall_sound);
3285 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3286 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3287 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3289 if (amoeba_sound && magic_sound)
3291 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3296 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3297 else if (magic_sound)
3298 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3303 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3304 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3305 play_sound_of_element(cave, O_AMOEBA, x, y);
3308 /* pneumatic hammer sound - overrides everything. */
3309 if (cave->pneumatic_hammer_active_delay > 0)
3310 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3312 /* CAVE VARIABLES */
3316 /* check if player is alive. */
3317 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3318 cave->player_state = GD_PL_DIED;
3320 /* check if any voodoo exploded, and kill players the next scan if that happended. */
3321 if (cave->voodoo_touched)
3322 cave->kill_player = TRUE;
3326 /* check flags after evaluating. */
3327 if (cave->amoeba_state == GD_AM_AWAKE)
3329 if (amoeba_count >= cave->amoeba_max_count)
3330 cave->amoeba_state = GD_AM_TOO_BIG;
3331 if (amoeba_found_enclosed)
3332 cave->amoeba_state = GD_AM_ENCLOSED;
3335 /* amoeba can also be turned into diamond by magic wall */
3336 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3337 cave->amoeba_state = GD_AM_ENCLOSED;
3340 if (cave->amoeba_2_state == GD_AM_AWAKE)
3342 /* check flags after evaluating. */
3343 if (amoeba_2_count >= cave->amoeba_2_max_count)
3344 cave->amoeba_2_state = GD_AM_TOO_BIG;
3346 if (amoeba_2_found_enclosed)
3347 cave->amoeba_2_state = GD_AM_ENCLOSED;
3350 /* amoeba 2 can also be turned into diamond by magic wall */
3351 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3352 cave->amoeba_2_state = GD_AM_ENCLOSED;
3354 /* now check times. --------------------------- */
3355 /* decrement time if a voodoo was killed. */
3356 cave->time -= time_decrement_sec * cave->timing_factor;
3360 /* only decrement time when player is already born. */
3363 int secondsbefore, secondsafter;
3365 secondsbefore = cave->time / cave->timing_factor;
3366 cave->time -= cave->speed;
3367 if (cave->time <= 0)
3370 secondsafter = cave->time / cave->timing_factor;
3371 if (cave->time / cave->timing_factor < 10)
3372 /* if less than 10 seconds, no walking sound, but play explosion sound */
3373 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3375 if (secondsbefore != secondsafter)
3376 gd_cave_set_seconds_sound(cave);
3379 /* a gravity switch was activated; seconds counting down */
3380 if (cave->gravity_will_change > 0)
3382 cave->gravity_will_change -= cave->speed;
3383 if (cave->gravity_will_change < 0)
3384 cave->gravity_will_change = 0;
3386 if (cave->gravity_will_change == 0)
3388 cave->gravity = cave->gravity_next_direction;
3389 gd_sound_play(cave, GD_S_GRAVITY_CHANGE, O_GRAVITY_SWITCH, -1, -1); /* takes precedence over amoeba and magic wall sound */
3393 /* creatures direction automatically change */
3394 if (cave->creatures_direction_will_change > 0)
3396 cave->creatures_direction_will_change -= cave->speed;
3397 if (cave->creatures_direction_will_change < 0)
3398 cave->creatures_direction_will_change = 0;
3400 if (cave->creatures_direction_will_change == 0)
3402 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3404 cave->creatures_backwards = !cave->creatures_backwards;
3405 cave->creatures_direction_will_change =
3406 cave->creatures_direction_auto_change_time * cave->timing_factor;
3410 /* magic wall; if active&wait or not wait for hatching */
3411 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3412 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3414 cave->magic_wall_time -= cave->speed;
3415 if (cave->magic_wall_time < 0)
3416 cave->magic_wall_time = 0;
3417 if (cave->magic_wall_time == 0)
3418 cave->magic_wall_state = GD_MW_EXPIRED;
3421 /* we may wait for hatching, when starting amoeba */
3422 if (cave->amoeba_timer_started_immediately ||
3423 (cave->amoeba_state == GD_AM_AWAKE &&
3424 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3426 cave->amoeba_time -= cave->speed;
3427 if (cave->amoeba_time < 0)
3428 cave->amoeba_time = 0;
3429 if (cave->amoeba_time == 0)
3430 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3433 /* we may wait for hatching, when starting amoeba */
3434 if (cave->amoeba_timer_started_immediately ||
3435 (cave->amoeba_2_state == GD_AM_AWAKE &&
3436 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3438 cave->amoeba_2_time -= cave->speed;
3439 if (cave->amoeba_2_time < 0)
3440 cave->amoeba_2_time = 0;
3441 if (cave->amoeba_2_time == 0)
3442 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3445 /* check for player hatching. */
3446 start_signal = FALSE;
3448 /* if not the c64 scheduling, but the correct frametime is used,
3449 hatching delay should always be decremented. */
3450 /* otherwise, the if (millisecs...) condition below will set this. */
3451 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3453 /* NON-C64 scheduling */
3454 if (cave->hatching_delay_frame > 0)
3456 /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3457 cave->hatching_delay_frame--;
3458 if (cave->hatching_delay_frame == 0)
3459 start_signal = TRUE;
3464 /* C64 scheduling */
3465 if (cave->hatching_delay_time > 0)
3467 /* for c64 schedulings, hatching delay means milliseconds. */
3468 cave->hatching_delay_time -= cave->speed;
3469 if (cave->hatching_delay_time <= 0)
3471 cave->hatching_delay_time = 0;
3472 start_signal = TRUE;
3477 /* if decremented hatching, and it became zero: */
3480 /* THIS IS THE CAVE START SIGNAL */
3482 /* record that now the cave is in its normal state */
3483 cave->hatched = TRUE;
3485 /* if diamonds needed is below zero, we count the available diamonds now. */
3486 gd_cave_count_diamonds(cave);
3488 /* setup direction auto change */
3489 if (cave->creatures_direction_auto_change_time)
3491 cave->creatures_direction_will_change =
3492 cave->creatures_direction_auto_change_time * cave->timing_factor;
3494 if (cave->creatures_direction_auto_change_on_start)
3495 cave->creatures_backwards = !cave->creatures_backwards;
3498 gd_sound_play(cave, GD_S_CRACK, O_INBOX, -1, -1);
3502 if (cave->biters_wait_frame == 0)
3503 cave->biters_wait_frame = cave->biter_delay_frame;
3505 cave->biters_wait_frame--;
3507 /* replicators delay */
3508 if (cave->replicators_wait_frame == 0)
3509 cave->replicators_wait_frame = cave->replicator_delay_frame;
3511 cave->replicators_wait_frame--;
3516 /* check if cave failed by timeout is done in main game engine */
3518 /* check if cave failed by timeout */
3519 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3521 gd_cave_clear_sounds(cave);
3522 cave->player_state = GD_PL_TIMEOUT;
3523 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3527 /* set these for drawing. */
3528 cave->last_direction = player_move;
3529 /* here we remember last movements for animation. this is needed here,
3530 as animation is in sync with the game, not the keyboard directly.
3531 (for example, after exiting the cave, the player was "running" in the
3532 original, till bonus points were counted for remaining time and so on. */
3533 if (player_move == GD_MV_LEFT ||
3534 player_move == GD_MV_UP_LEFT ||
3535 player_move == GD_MV_DOWN_LEFT)
3536 cave->last_horizontal_direction = GD_MV_LEFT;
3538 if (player_move == GD_MV_RIGHT ||
3539 player_move == GD_MV_UP_RIGHT ||
3540 player_move == GD_MV_DOWN_RIGHT)
3541 cave->last_horizontal_direction = GD_MV_RIGHT;
3543 cave->frame++; /* XXX */
3546 void set_initial_cave_speed(GdCave *cave)
3551 /* set cave get function; to implement perfect or lineshifting borders */
3552 if (cave->lineshift)
3553 cave->getp = getp_shift;
3555 cave->getp = getp_perfect;
3557 /* check whether to scan the first and last line */
3558 if (cave->border_scan_first_and_last)
3569 for (y = ymin; y <= ymax; y++)
3571 for (x = 0; x < cave->w; x++)
3573 /* add the ckdelay correction value for every element seen. */
3574 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3578 /* update timing calculated by iterating and counting elements */
3579 update_cave_speed(cave);