2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * Lava absorbs everything going into it. Everything.
23 * But it does not "pull" elements; only the things disappear which
24 * _do_ go directly into it. So if the player steps into the lava,
25 * he will die. If a dragonfly flies over it, it will not.
27 * This behavior is implemented in the is_space_dir and the store
28 * functions. is_space_dir returns true for the lava, too. The store
29 * function ignores any store requests into the lava.
30 * The player_get function will also behave for lava as it does for space.
39 static const GdDirection ccw_eighth[] =
51 static const GdDirection ccw_fourth[] =
65 static const GdDirection cw_eighth[] =
78 static const GdDirection cw_fourth[] =
91 static const GdDirection opposite[] =
104 /* sets timeout sound. */
105 void gd_cave_set_seconds_sound(GdCave *cave)
107 // when not counting bonus time, timeout sounds will be played by main game engine;
108 // also skip timeout sounds when not using native sound engine
109 if (game_bd.game == NULL || game_bd.game->state_counter != GAME_INT_CHECK_BONUS_TIME ||
110 !game.use_native_bd_sound_engine)
113 /* this is an integer division, so 0 seconds can be 0.5 seconds... */
114 /* also, when this reaches 8, the player still has 8.9999 seconds.
115 so the sound is played at almost t = 9s. */
116 switch (cave->time / cave->timing_factor)
118 case 9: gd_sound_play(cave, GD_S_TIMEOUT_10, O_NONE, -1, -1); break;
119 case 8: gd_sound_play(cave, GD_S_TIMEOUT_9, O_NONE, -1, -1); break;
120 case 7: gd_sound_play(cave, GD_S_TIMEOUT_8, O_NONE, -1, -1); break;
121 case 6: gd_sound_play(cave, GD_S_TIMEOUT_7, O_NONE, -1, -1); break;
122 case 5: gd_sound_play(cave, GD_S_TIMEOUT_6, O_NONE, -1, -1); break;
123 case 4: gd_sound_play(cave, GD_S_TIMEOUT_5, O_NONE, -1, -1); break;
124 case 3: gd_sound_play(cave, GD_S_TIMEOUT_4, O_NONE, -1, -1); break;
125 case 2: gd_sound_play(cave, GD_S_TIMEOUT_3, O_NONE, -1, -1); break;
126 case 1: gd_sound_play(cave, GD_S_TIMEOUT_2, O_NONE, -1, -1); break;
127 case 0: gd_sound_play(cave, GD_S_TIMEOUT_1, O_NONE, -1, -1); break;
131 /* play diamond or stone sound of given element. */
132 static void play_sound_of_element(GdCave *cave, GdElement element, int x, int y)
134 /* stone and diamond fall sounds. */
138 gd_sound_play(cave, GD_S_NUT_FALLING, element, x, y);
142 gd_sound_play(cave, GD_S_NUT_IMPACT, element, x, y);
146 gd_sound_play(cave, GD_S_STONE_FALLING, element, x, y);
150 gd_sound_play(cave, GD_S_STONE_IMPACT, element, x, y);
154 gd_sound_play(cave, GD_S_FLYING_STONE_FALLING, element, x, y);
157 case O_FLYING_STONE_F:
158 gd_sound_play(cave, GD_S_FLYING_STONE_IMPACT, element, x, y);
162 gd_sound_play(cave, GD_S_MEGA_STONE_FALLING, element, x, y);
166 gd_sound_play(cave, GD_S_MEGA_STONE_IMPACT, element, x, y);
170 gd_sound_play(cave, GD_S_NITRO_PACK_FALLING, element, x, y);
174 gd_sound_play(cave, GD_S_NITRO_PACK_IMPACT, element, x, y);
178 gd_sound_play(cave, GD_S_FALLING_WALL_FALLING, element, x, y);
181 case O_FALLING_WALL_F:
182 gd_sound_play(cave, GD_S_FALLING_WALL_IMPACT, element, x, y);
185 case O_H_EXPANDING_WALL:
186 case O_V_EXPANDING_WALL:
187 case O_EXPANDING_WALL:
188 case O_H_EXPANDING_STEEL_WALL:
189 case O_V_EXPANDING_STEEL_WALL:
190 case O_EXPANDING_STEEL_WALL:
191 gd_sound_play(cave, GD_S_EXPANDING_WALL, element, x, y);
195 gd_sound_play(cave, GD_S_DIAMOND_FALLING_RANDOM, element, x, y);
199 gd_sound_play(cave, GD_S_DIAMOND_IMPACT_RANDOM, element, x, y);
202 case O_FLYING_DIAMOND:
203 gd_sound_play(cave, GD_S_FLYING_DIAMOND_FALLING_RANDOM, element, x, y);
206 case O_FLYING_DIAMOND_F:
207 gd_sound_play(cave, GD_S_FLYING_DIAMOND_IMPACT_RANDOM, element, x, y);
210 case O_BLADDER_SPENDER:
211 gd_sound_play(cave, GD_S_BLADDER_SPENDER, element, x, y);
215 gd_sound_play(cave, GD_S_BLADDER_CONVERTING, element, x, y);
219 gd_sound_play(cave, GD_S_SLIME, element, x, y);
223 gd_sound_play(cave, GD_S_LAVA, element, x, y);
227 gd_sound_play(cave, GD_S_ACID_SPREADING, element, x, y);
231 gd_sound_play(cave, GD_S_BLADDER_MOVING, element, x, y);
238 gd_sound_play(cave, GD_S_BITER_EATING, element, x, y);
242 gd_sound_play(cave, GD_S_DIRT_BALL_FALLING, element, x, y);
246 gd_sound_play(cave, GD_S_DIRT_BALL_IMPACT, element, x, y);
250 gd_sound_play(cave, GD_S_DIRT_LOOSE_FALLING, element, x, y);
254 gd_sound_play(cave, GD_S_DIRT_LOOSE_IMPACT, element, x, y);
263 /* play sound of given element being pushed. */
264 static void play_sound_of_element_pushing(GdCave *cave, GdElement element, int x, int y)
269 gd_sound_play(cave, GD_S_NUT_PUSHING, element, x, y);
273 gd_sound_play(cave, GD_S_STONE_PUSHING, element, x, y);
277 gd_sound_play(cave, GD_S_FLYING_STONE_PUSHING, element, x, y);
281 gd_sound_play(cave, GD_S_MEGA_STONE_PUSHING, element, x, y);
284 case O_WAITING_STONE:
285 gd_sound_play(cave, GD_S_WAITING_STONE_PUSHING, element, x, y);
288 case O_CHASING_STONE:
289 gd_sound_play(cave, GD_S_CHASING_STONE_PUSHING, element, x, y);
293 gd_sound_play(cave, GD_S_NITRO_PACK_PUSHING, element, x, y);
297 gd_sound_play(cave, GD_S_BLADDER_PUSHING, element, x, y);
306 static inline int getx(const GdCave *cave, const int x, const int y)
308 return cave->getx(cave, x, y);
311 static inline int gety(const GdCave *cave, const int x, const int y)
313 return cave->gety(cave, x, y);
316 /* perfect (non-lineshifting) GET x/y functions; returns range corrected x/y position */
317 static inline int getx_perfect(const GdCave *cave, const int x, const int y)
319 return (x + cave->w) % cave->w;
322 static inline int gety_perfect(const GdCave *cave, const int x, const int y)
324 return (y + cave->h) % cave->h;
327 /* line shifting GET x/y function; returns range corrected x/y position */
328 static inline int getx_shift(const GdCave *cave, int x, int y)
330 return (x + cave->w) % cave->w;
333 static inline int gety_shift(const GdCave *cave, int x, int y)
335 return ((x < 0 ? y - 1 : x >= cave->w ? y + 1 : y) + cave->h) % cave->h;
338 static inline GdElement *getp(const GdCave *cave, const int x, const int y)
340 return cave->getp(cave, x, y);
344 perfect (non-lineshifting) GET function.
345 returns a pointer to a selected cave element by its coordinates.
347 static inline GdElement *getp_perfect(const GdCave *cave, const int x, const int y)
349 /* (x + n) mod n: this works also for x >= n and -n + 1 < x < 0 */
350 return &(cave->map[(y + cave->h) % cave->h][(x + cave->w) % cave->w]);
354 line shifting GET function; returns a pointer to the selected cave element.
355 this is used to emulate the line-shifting behaviour of original games, so that
356 the player entering one side will appear one row above or below on the other.
358 static inline GdElement *getp_shift(const GdCave *cave, int x, int y)
371 y = (y + cave->h) % cave->h;
373 return &(cave->map[y][x]);
376 static inline GdElement get(const GdCave *cave, const int x, const int y)
378 return *getp(cave, x, y);
381 /* returns an element which is somewhere near x,y */
382 static inline GdElement get_dir(const GdCave *cave, const int x, const int y,
383 const GdDirection dir)
385 return get(cave, x + gd_dx[dir], y + gd_dy[dir]);
388 static inline boolean explodes_by_hit_dir(const GdCave *cave, const int x,
389 const int y, GdDirection dir)
391 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_EXPLODES_BY_HIT) != 0;
394 /* returns true if the element is not explodable, for example the steel wall */
395 static inline boolean non_explodable(const GdCave *cave, const int x, const int y)
397 return (gd_elements[get(cave, x,y) & O_MASK].properties & P_NON_EXPLODABLE) != 0;
400 /* returns true if the element can be eaten by the amoeba, eg. space and dirt. */
401 static inline boolean amoeba_eats_dir(const GdCave *cave, const int x, const int y,
402 const GdDirection dir)
404 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_AMOEBA_CONSUMES) != 0;
407 /* returns true if the element is sloped, so stones and diamonds roll down on it.
408 for example a stone or brick wall */
409 static inline boolean sloped_dir(const GdCave *cave, const int x, const int y,
410 const GdDirection dir, const GdDirection slop)
415 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_LEFT) != 0;
418 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_RIGHT) != 0;
421 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_UP) != 0;
424 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_DOWN) != 0;
433 /* returns true if the element is sloped for bladder movement
434 (brick = yes, diamond = no, for example) */
435 static inline boolean sloped_for_bladder_dir (const GdCave *cave, const int x, const int y,
436 const GdDirection dir)
438 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLADDER_SLOPED) != 0;
441 static inline boolean blows_up_flies_dir(const GdCave *cave, const int x, const int y,
442 const GdDirection dir)
444 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLOWS_UP_FLIES) != 0;
447 /* returns true if the element is a counter-clockwise creature */
448 static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
450 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_CCW) != 0;
453 /* returns true if the element is a player */
454 static inline boolean is_player(const GdCave *cave, const int x, const int y)
456 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
459 /* returns true if the element is a player */
460 static inline boolean is_player_dir(const GdCave *cave, const int x, const int y,
461 const GdDirection dir)
463 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PLAYER) != 0;
466 static inline boolean can_be_hammered_dir(const GdCave *cave, const int x, const int y,
467 const GdDirection dir)
469 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
472 /* returns true if the element is explodable and explodes to space, for example the player */
473 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
475 return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
478 /* returns true if the element is moved by the conveyor belt */
479 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
480 const GdDirection dir)
482 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
485 /* returns true if the element is moved by the conveyor belt */
486 static inline boolean moved_by_conveyor_bottom_dir(const GdCave *cave, const int x, const int y,
487 const GdDirection dir)
489 return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_BOTTOM) != 0;
492 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
493 const GdDirection dir)
495 return (get_dir(cave, x, y, dir) & SCANNED) != 0;
498 /* returns true if neighbouring element is "e" */
499 /* treats dirt specially */
500 /* treats lava specially */
501 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
502 const GdDirection dir, GdElement e)
504 GdElement examined = get_dir(cave, x, y, dir);
506 /* if it is a dirt-like, change to dirt, so equality will evaluate to true */
507 if (gd_elements[examined & O_MASK].properties & P_DIRT)
510 if (gd_elements[e & O_MASK].properties & P_DIRT)
513 /* if the element on the map is a lava, it should be like space */
514 if (examined == O_LAVA)
517 return (e == examined);
520 /* returns true if neighbouring element is space */
521 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
522 const GdDirection dir)
524 GdElement e = get_dir(cave, x, y, dir) & O_MASK;
526 return (e == O_SPACE || e == O_LAVA);
529 static inline void store_dir_buffer(GdCave *cave, const int x, const int y, const GdDirection dir)
531 /* raw values without range correction */
532 int raw_x = x + gd_dx[dir];
533 int raw_y = y + gd_dy[dir];
534 /* final values with range correction */
535 int new_x = getx(cave, raw_x, raw_y);
536 int new_y = gety(cave, raw_x, raw_y);
537 int new_dir = (dir > GD_MV_TWICE ? dir - GD_MV_TWICE : dir);
539 game_bd.game->dir_buffer[new_y][new_x] = new_dir;
542 /* store an element at the given position */
543 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
545 GdElement *e = getp(cave, x, y);
549 play_sound_of_element(cave, O_LAVA, x, y);
557 /* store an element with SCANNED flag turned on */
558 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
560 store(cave, x, y, element | SCANNED);
563 /* store an element to a neighbouring cell */
564 static inline void store_dir(GdCave *cave, const int x, const int y,
565 const GdDirection dir, const GdElement element)
567 store_dir_buffer(cave, x, y, dir);
568 store(cave, x + gd_dx[dir], y + gd_dy[dir], element | SCANNED);
571 /* store an element to a neighbouring cell */
572 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
573 const GdDirection dir, const GdElement element)
575 store_dir_buffer(cave, x, y, dir);
576 store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
579 /* move element to direction; then place space at x, y */
580 static inline void move(GdCave *cave, const int x, const int y,
581 const GdDirection dir, const GdElement e)
583 store_dir(cave, x, y, dir, e);
584 store(cave, x, y, O_SPACE);
587 /* increment a cave element; can be used for elements which are one after
588 the other, for example bladder1, bladder2, bladder3... */
589 static inline void next(GdCave *cave, const int x, const int y)
591 (*getp(cave, x, y))++;
594 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
596 if (non_explodable (cave, x, y))
599 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
600 cave->voodoo_touched = TRUE;
602 if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
603 /* voodoo turns into a time penalty */
604 store_sc(cave, x, y, O_TIME_PENALTY);
605 else if (get(cave, x, y) == O_NITRO_PACK ||
606 get(cave, x, y) == O_NITRO_PACK_F)
607 /* nitro pack inside an explosion - it is now triggered */
608 store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
610 /* for everything else */
611 store_sc(cave, x, y, explode_to);
614 /* a creature explodes to a 3x3 something. */
615 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
619 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
620 cave->ckdelay += 1200;
621 gd_sound_play(cave, GD_S_EXPLODING, get(cave, x, y), x, y);
623 for (yy = y - 1; yy <= y + 1; yy++)
624 for (xx = x - 1; xx <= x + 1; xx++)
625 cell_explode(cave, xx, yy, explode_to);
628 static void nitro_explode(GdCave *cave, int x, int y)
632 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
633 cave->ckdelay += 1200;
634 gd_sound_play(cave, GD_S_NITRO_PACK_EXPLODING, get(cave, x, y), x, y);
636 for (yy = y - 1; yy <= y + 1; yy++)
637 for (xx = x - 1; xx <= x + 1; xx++)
638 cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
640 /* the current cell is explicitly changed into a nitro expl,
641 as cell_explode changes it to a triggered nitro pack */
642 store_sc(cave, x, y, O_NITRO_EXPL_1);
645 /* a voodoo explodes, leaving a 3x3 steel and a time penalty behind. */
646 static void voodoo_explode(GdCave *cave, int x, int y)
650 /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
651 cave->ckdelay += 1000;
653 gd_sound_play(cave, GD_S_VOODOO_EXPLODING, get(cave, x, y), x, y);
654 if (cave->voodoo_any_hurt_kills_player)
655 cave->voodoo_touched = TRUE;
657 /* voodoo explodes to 3x3 steel */
658 for (yy = y - 1; yy <= y + 1; yy++)
659 for (xx = x - 1; xx <= x + 1; xx++)
660 store_sc(cave, xx, yy, O_PRE_STEEL_1);
662 /* middle is a time penalty (which will be turned into a gravestone) */
663 store_sc(cave, x, y, O_TIME_PENALTY);
666 /* a bomb does not explode the voodoo, neither does the ghost.
667 this function check this, and stores the new element or not.
668 destroying the voodoo is also controlled by the
669 voodoo_disappear_in_explosion flag. */
670 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
672 if (non_explodable (cave, x, y))
675 /* bomb does not explode voodoo */
676 if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
679 if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
680 cave->voodoo_touched = TRUE;
682 store_sc (cave, x, y, expl);
685 /* X shaped ghost explosion; does not touch voodoo! */
686 static void ghost_explode(GdCave *cave, const int x, const int y)
688 gd_sound_play(cave, GD_S_GHOST_EXPLODING, get(cave, x, y), x, y);
690 /* the processing of an explosion took pretty much time: processing 5 elements */
691 cave->ckdelay += 650;
693 explode_try_skip_voodoo(cave, x, y, O_GHOST_EXPL_1);
694 explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
695 explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
696 explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
697 explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
700 /* +shaped bomb explosion; does not touch voodoo! */
701 static void bomb_explode(GdCave *cave, const int x, const int y)
703 gd_sound_play(cave, GD_S_BOMB_EXPLODING, get(cave, x, y), x, y);
705 /* the processing of an explosion took pretty much time: processing 5 elements */
706 cave->ckdelay += 650;
708 explode_try_skip_voodoo(cave, x, y, O_BOMB_EXPL_1);
709 explode_try_skip_voodoo(cave, x - 1, y, O_BOMB_EXPL_1);
710 explode_try_skip_voodoo(cave, x + 1, y, O_BOMB_EXPL_1);
711 explode_try_skip_voodoo(cave, x, y + 1, O_BOMB_EXPL_1);
712 explode_try_skip_voodoo(cave, x, y - 1, O_BOMB_EXPL_1);
716 explode an element with the appropriate type of exlposion.
718 static void explode(GdCave *cave, int x, int y)
720 GdElement e = get(cave, x, y) & O_MASK;
725 ghost_explode(cave, x, y);
729 bomb_explode(cave, x, y);
733 voodoo_explode(cave, x, y);
738 case O_NITRO_PACK_EXPLODE:
739 nitro_explode(cave, x, y);
743 creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
746 case O_FALLING_WALL_F:
747 creature_explode(cave, x, y, O_EXPLODE_1);
754 creature_explode(cave, x, y, cave->butterfly_explode_to);
761 creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
768 creature_explode(cave, x, y, cave->firefly_explode_to);
771 case O_ALT_FIREFLY_1:
772 case O_ALT_FIREFLY_2:
773 case O_ALT_FIREFLY_3:
774 case O_ALT_FIREFLY_4:
775 creature_explode(cave, x, y, cave->alt_firefly_explode_to);
781 case O_PLAYER_STIRRING:
782 case O_PLAYER_PNEUMATIC_LEFT:
783 case O_PLAYER_PNEUMATIC_RIGHT:
784 creature_explode(cave, x, y, O_EXPLODE_1);
791 creature_explode(cave, x, y, cave->stonefly_explode_to);
798 creature_explode(cave, x, y, cave->dragonfly_explode_to);
806 static void inline explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
808 explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
812 player eats specified object.
813 returns O_SPACE if he eats it (diamond, dirt, space, outbox)
814 returns other element if something other appears there and he can't move.
815 cave pointer is needed to know the diamond values.
817 static GdElement player_get_element(GdCave* cave, const GdElement object, int x, int y)
824 cave->diamond_key_collected = TRUE;
825 gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECTING, object, x, y);
830 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
835 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
840 gd_sound_play(cave, GD_S_KEY_COLLECTING, object, x, y);
847 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
854 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
861 gd_sound_play(cave, GD_S_DOOR_OPENING, object, x, y);
866 case O_CREATURE_SWITCH: /* creatures change direction. */
867 gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
868 cave->creatures_backwards = !cave->creatures_backwards;
871 case O_EXPANDING_WALL_SWITCH: /* expanding wall change direction. */
872 gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
873 cave->expanding_wall_changed = !cave->expanding_wall_changed;
876 case O_BITER_SWITCH: /* biter change delay */
877 gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
878 cave->biter_delay_frame++;
879 if (cave->biter_delay_frame == 4)
880 cave->biter_delay_frame = 0;
883 case O_REPLICATOR_SWITCH: /* replicator on/off switch */
884 gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
885 cave->replicators_active = !cave->replicators_active;
888 case O_CONVEYOR_SWITCH: /* conveyor belts on/off */
889 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
890 cave->conveyor_belts_active = !cave->conveyor_belts_active;
893 case O_CONVEYOR_DIR_SWITCH: /* conveyor belts switch direction */
894 gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
895 cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
901 case O_STEEL_EATABLE:
902 case O_BRICK_EATABLE:
903 case O_DIRT_SLOPED_UP_RIGHT:
904 case O_DIRT_SLOPED_UP_LEFT:
905 case O_DIRT_SLOPED_DOWN_LEFT:
906 case O_DIRT_SLOPED_DOWN_RIGHT:
909 gd_sound_play(cave, GD_S_DIRT_WALKING, object, x, y);
913 gd_sound_play(cave, GD_S_SWEET_COLLECTING, object, x, y);
914 cave->sweet_eaten = TRUE;
917 case O_PNEUMATIC_HAMMER:
918 gd_sound_play(cave, GD_S_PNEUMATIC_COLLECTING, object, x, y);
919 cave->got_pneumatic_hammer = TRUE;
924 gd_sound_play(cave, GD_S_CLOCK_COLLECTING, object, x, y);
925 cave->time += cave->time_bonus * cave->timing_factor;
926 if (cave->time > cave->max_time * cave->timing_factor)
927 cave->time -= cave->max_time * cave->timing_factor;
928 /* no space, rather a dirt remains there... */
932 case O_FLYING_DIAMOND:
933 // prevent diamond sounds for O_SKELETON (see below)
934 if (x != -1 && y != -1)
935 gd_sound_play(cave, (object == O_DIAMOND ? GD_S_DIAMOND_COLLECTING :
936 GD_S_FLYING_DIAMOND_COLLECTING), object, x, y);
938 cave->score += cave->diamond_value;
939 cave->diamonds_collected++;
941 if (cave->diamonds_needed == cave->diamonds_collected)
943 cave->gate_open = TRUE;
945 /* extra is worth more points. */
946 cave->diamond_value = cave->extra_diamond_value;
948 cave->gate_open_flash = 1;
949 cave->sound3 = GD_S_CRACKING;
950 gd_sound_play(cave, GD_S_CRACKING, O_OUTBOX, x, y);
955 cave->skeletons_collected++;
957 /* as if player got a diamond */
958 for (i = 0; i < cave->skeletons_worth_diamonds; i++)
959 player_get_element(cave, O_DIAMOND, -1, -1);
961 /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
962 gd_sound_play(cave, GD_S_SKELETON_COLLECTING, object, x, y);
967 cave->player_state = GD_PL_EXITED; /* player now exits the cave! */
971 case O_LAVA: /* player goes into lava, as if it was space */
972 gd_sound_play(cave, GD_S_EMPTY_WALKING, object, x, y);
976 /* the object will remain there. */
982 process a crazy dream-style teleporter.
983 called from gd_cave_iterate, for a player or a player_bomb.
984 player is standing at px, py, and trying to move in the direction player_move,
985 where there is a teleporter.
986 we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
987 around). the first teleporter we find, and which is suitable, will be the destination.
988 return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
990 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
999 /* jump to next element; wrap around columns and rows. */
1011 /* if we found a teleporter... */
1012 if (get(cave, tx, ty) == O_TELEPORTER &&
1013 is_space_dir(cave, tx, ty, player_move))
1015 /* new player appears near teleporter found */
1016 store_dir(cave, tx, ty, player_move, get(cave, px, py));
1018 /* current player disappears */
1019 store(cave, px, py, O_SPACE);
1021 gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
1023 return TRUE; /* return true as teleporter worked */
1026 /* loop until we get back to original coordinates */
1027 while (tx != px || ty != py);
1029 /* return false as we did not find any usable teleporter */
1034 try to push an element.
1035 returns true if the push is possible; also does move the specified _element_.
1036 up to the caller to move the _player_itself_.
1038 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
1041 GdElement what = get_dir(cave, x, y, player_move);
1043 /* gravity for falling wall, bladder, ... */
1044 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1050 case O_WAITING_STONE:
1053 case O_CHASING_STONE:
1055 case O_FLYING_STONE:
1057 /* pushing some kind of stone or nut */
1058 /* directions possible: 90degrees cw or ccw to current gravity. */
1059 /* only push if player dir is orthogonal to gravity,
1060 ie. gravity down, pushing left & right possible */
1061 if (player_move == ccw_fourth[cave->gravity] ||
1062 player_move == cw_fourth[cave->gravity])
1068 /* different probabilities for different elements. */
1071 case O_WAITING_STONE:
1072 /* waiting stones are light, can always push */
1076 case O_CHASING_STONE:
1077 /* chasing can be pushed if player is turbo */
1078 if (cave->sweet_eaten)
1083 /* mega may(!) be pushed if player is turbo */
1084 if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
1090 case O_FLYING_STONE:
1092 if (cave->sweet_eaten)
1093 prob = cave->pushing_stone_prob_sweet; /* probability with sweet */
1095 prob = cave->pushing_stone_prob; /* probability without sweet. */
1102 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
1103 g_rand_int_range(cave->random, 0, 1000000) < prob)
1105 /* if decided that he will be able to push, */
1106 store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
1107 play_sound_of_element_pushing(cave, what, x, y);
1122 /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1123 * not an O_BLADDER_x. */
1124 /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
1126 /* first check: we cannot push a bladder "up" */
1127 if (player_move != opposite[grav_compat])
1129 /* pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check. */
1130 /* player moving in the direction of gravity. */
1134 if (player_move == grav_compat)
1136 /* pushing bladder down */
1137 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1138 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1139 /* if no space to push down, maybe left (down-left to player) */
1140 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1142 /* left is "down, turned right (cw)" */
1143 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1144 /* if not, maybe right (down-right to player) */
1145 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1146 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1149 /* pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check. */
1153 else if (player_move == cw_fourth[grav_compat])
1155 if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat])) /* pushing it left */
1156 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1157 else if (is_space_dir(cave, x, y, cw_eighth[grav_compat])) /* maybe down, and player will move left */
1158 store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1159 else if (is_space_dir(cave, x, y, cw_eighth[player_move])) /* maybe up, and player will move left */
1160 store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1163 /* pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check. */
1167 else if (player_move == ccw_fourth[grav_compat])
1169 if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move)) /* pushing it right */
1170 store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1171 else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat])) /* maybe down, and player will move right */
1172 store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1173 else if (is_space_dir(cave, x, y, ccw_eighth[player_move])) /* maybe up, and player will move right */
1174 store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1178 play_sound_of_element_pushing(cave, O_BLADDER, x, y);
1183 /* a box is only pushed with the fire pressed */
1186 /* but always with 100% probability */
1187 switch (player_move)
1193 /* pushing in some dir, two steps in that dir - is there space? */
1194 if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1197 store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1199 gd_sound_play(cave, GD_S_BOX_PUSHING, what, x, y);
1204 /* push in no other directions possible */
1210 /* pushing of other elements not possible */
1218 /* from the key press booleans, create a direction */
1219 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1221 GdDirection player_move;
1223 /* from the key press booleans, create a direction */
1225 player_move = GD_MV_UP_RIGHT;
1226 else if (down && right)
1227 player_move = GD_MV_DOWN_RIGHT;
1228 else if (down && left)
1229 player_move = GD_MV_DOWN_LEFT;
1230 else if (up && left)
1231 player_move = GD_MV_UP_LEFT;
1233 player_move = GD_MV_UP;
1235 player_move = GD_MV_DOWN;
1237 player_move = GD_MV_LEFT;
1239 player_move = GD_MV_RIGHT;
1241 player_move = GD_MV_STILL;
1246 /* clear these to no sound; and they will be set during iteration. */
1247 void gd_cave_clear_sounds(GdCave *cave)
1249 cave->sound1 = GD_S_NONE;
1250 cave->sound2 = GD_S_NONE;
1251 cave->sound3 = GD_S_NONE;
1254 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1255 GdElement falling_element)
1257 if (cave->gravity_disabled)
1260 if (is_space_dir(cave, x, y, falling_direction))
1262 /* beginning to fall */
1263 play_sound_of_element(cave, get(cave, x, y), x, y);
1264 move(cave, x, y, falling_direction, falling_element);
1267 /* check if it is on a sloped element, and it can roll. */
1268 /* for example, sloped wall looks like: */
1271 /* this is tagged as sloped up&left. */
1272 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1273 /* then check the direction to roll (left or right) */
1274 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1275 else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1277 /* rolling down, if sitting on a sloped object */
1278 if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1279 is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1280 is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1282 /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1283 so here we use cw_fourth */
1284 play_sound_of_element(cave, get(cave, x, y), x, y);
1285 move(cave, x, y, cw_fourth[falling_direction], falling_element);
1287 else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1288 is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1289 is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1291 /* rolling right? */
1292 play_sound_of_element(cave, get(cave, x, y), x, y);
1293 move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1298 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1300 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1301 cave->voodoo_dies_by_stone)
1303 /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1304 explode_dir(cave, x, y, fall_dir);
1311 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1313 if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1314 cave->voodoo_collects_diamonds)
1316 /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1317 player_get_element(cave, O_DIAMOND, x, y); /* as if player got diamond */
1318 store(cave, x, y, O_SPACE); /* diamond disappears */
1325 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1326 GdDirection fall_dir, GdElement bouncing)
1328 if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1329 get_dir(cave, x, y, fall_dir) == O_NUT_F)
1332 store(cave, x, y, bouncing);
1333 store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1335 gd_sound_play(cave, GD_S_NUT_CRACKING, O_NUT, x, y);
1343 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1344 GdDirection fall_dir, GdElement magic)
1346 if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1348 play_sound_of_element(cave, O_DIAMOND, x, y); /* always play diamond sound */
1350 if (cave->magic_wall_state == GD_MW_DORMANT)
1351 cave->magic_wall_state = GD_MW_ACTIVE;
1353 if (cave->magic_wall_state == GD_MW_ACTIVE &&
1354 is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1356 /* if magic wall active and place underneath, it turns element
1357 into anything the effect says to do. */
1358 store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1361 /* active or non-active or anything, element falling in will always disappear */
1362 store(cave, x, y, O_SPACE);
1370 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1372 if (explodes_by_hit_dir(cave, x, y, fall_dir))
1374 explode_dir(cave, x, y, fall_dir);
1381 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1382 GdDirection fall_dir, GdElement bouncing)
1384 if (is_space_dir(cave, x, y, fall_dir))
1386 /* falling further */
1387 move(cave, x, y, fall_dir, get(cave, x, y));
1392 /* check if it is on a sloped element, and it can roll. */
1393 /* for example, sloped wall looks like: */
1396 /* this is tagged as sloped up&left. */
1397 /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1398 /* then check the direction to roll (left or right) */
1399 /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1401 if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1403 /* sloped element, falling to left or right */
1404 if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1405 is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1406 is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1408 play_sound_of_element(cave, get(cave, x, y), x, y);
1410 /* try to roll left first - see O_STONE to understand why cw_fourth */
1411 move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1413 else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1414 is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1415 is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1417 play_sound_of_element(cave, get(cave, x, y), x, y);
1419 /* if not, try to roll right */
1420 move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1424 /* cannot roll in any direction, so it stops */
1425 play_sound_of_element(cave, get(cave, x, y), x, y);
1426 store(cave, x, y, bouncing);
1432 /* any other element, stops */
1433 play_sound_of_element(cave, get(cave, x, y), x, y);
1434 store(cave, x, y, bouncing);
1438 static void update_cave_speed(GdCave *cave)
1440 /* update timing calculated by iterating and counting elements which were slow to process on c64 */
1441 switch (cave->scheduling)
1443 case GD_SCHEDULING_MILLISECONDS:
1444 /* cave->speed already contains the milliseconds value, do not touch it */
1447 case GD_SCHEDULING_BD1:
1448 if (!cave->intermission)
1449 /* non-intermissions */
1450 cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1452 /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
1453 cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1456 case GD_SCHEDULING_BD1_ATARI:
1457 /* about 20ms/frame faster than c64 version */
1458 if (!cave->intermission)
1459 cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000); /* non-intermissions */
1461 cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000); /* for intermissions */
1464 case GD_SCHEDULING_BD2:
1465 /* 60 is a guess. */
1466 cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1469 case GD_SCHEDULING_PLCK:
1470 /* 65 is totally empty cave in construction kit, with delay = 0) */
1471 cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1474 case GD_SCHEDULING_BD2_PLCK_ATARI:
1475 /* a really fast engine; timing works like c64 plck. */
1476 /* 40 ms was measured in the construction kit, with delay = 0 */
1477 cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1480 case GD_SCHEDULING_CRDR:
1481 if (cave->hammered_walls_reappear) /* this made the engine very slow. */
1482 cave->ckdelay += 60000;
1483 cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1486 case GD_SCHEDULING_MAX:
1491 /* process a cave. */
1492 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1496 /* for border scan */
1499 /* amoeba found to be enclosed. if not, this is cleared */
1500 boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1502 /* counting the number of amoebas. after scan, check if too much */
1503 int amoeba_count, amoeba_2_count;
1505 /* cave scan found water - for sound */
1506 boolean found_water;
1508 boolean inbox_toggle;
1509 boolean start_signal;
1511 /* gravity for falling wall, bladder, ... */
1512 GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1514 /* directions for o_something_1, 2, 3 and 4 (creatures) */
1515 static const GdDirection creature_dir[] =
1522 static const GdDirection creature_chdir[] =
1529 int time_decrement_sec;
1531 /* biters eating elements preference, they try to go in this order */
1532 GdElement biter_try[] =
1539 boolean amoeba_sound, magic_sound;
1541 gd_cave_clear_sounds(cave);
1543 /* if diagonal movements not allowed, */
1544 /* horizontal movements have precedence. [BROADRIBB] */
1545 if (!cave->diagonal_movements)
1547 switch (player_move)
1549 case GD_MV_UP_RIGHT:
1550 case GD_MV_DOWN_RIGHT:
1551 player_move = GD_MV_RIGHT;
1555 case GD_MV_DOWN_LEFT:
1556 player_move = GD_MV_LEFT;
1560 /* no correction needed */
1565 /* set cave get function; to implement perfect or lineshifting borders */
1566 if (cave->lineshift)
1568 cave->getp = getp_shift;
1569 cave->getx = getx_shift;
1570 cave->gety = gety_shift;
1574 cave->getp = getp_perfect;
1575 cave->getx = getx_perfect;
1576 cave->gety = gety_perfect;
1579 /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1580 if (cave->player_seen_ago < 100)
1581 cave->player_seen_ago++;
1583 if (cave->pneumatic_hammer_active_delay > 0)
1584 cave->pneumatic_hammer_active_delay--;
1586 /* inboxes and outboxes flash with the rhythm of the game, not the display.
1587 * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1588 cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1589 inbox_toggle = cave->inbox_flash_toggle;
1591 if (cave->gate_open_flash > 0)
1592 cave->gate_open_flash--;
1594 /* score collected this frame */
1597 /* suicide only kills the active player */
1598 /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1599 /* we must check if there is a player or not - he may have exploded or something like that */
1600 if (suicide && cave->player_state == GD_PL_LIVING &&
1601 is_player(cave, cave->player_x, cave->player_y))
1602 store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1604 /* check for walls reappearing */
1605 if (cave->hammered_reappear)
1607 for (y = 0; y < cave->h; y++)
1609 for (x = 0; x < cave->w; x++)
1611 /* timer for the cell > 0? */
1612 if (cave->hammered_reappear[y][x] > 0)
1614 /* decrease timer */
1615 cave->hammered_reappear[y][x]--;
1617 /* check if it became zero */
1618 if (cave->hammered_reappear[y][x] == 0)
1620 store(cave, x, y, O_BRICK);
1621 gd_sound_play(cave, GD_S_WALL_REAPPEARING, O_BRICK, x, y);
1628 /* variables to check during the scan */
1630 /* will be set to false if any of the amoeba is found free. */
1631 amoeba_found_enclosed = TRUE;
1632 amoeba_2_found_enclosed = TRUE;
1635 found_water = FALSE;
1637 time_decrement_sec = 0;
1639 /* check whether to scan the first and last line */
1640 if (cave->border_scan_first_and_last)
1651 /* the cave scan routine */
1652 for (y = ymin; y <= ymax; y++)
1654 for (x = 0; x < cave->w; x++)
1656 /* if we find a scanned element, change it to the normal one, and that's all. */
1657 /* this is required, for example for chasing stones, which have moved, always passing slime! */
1658 if (get(cave, x, y) & SCANNED)
1660 store(cave, x, y, get(cave, x, y) & ~SCANNED);
1665 /* add the ckdelay correction value for every element seen. */
1666 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1668 switch (get(cave, x, y))
1674 if (cave->kill_player)
1676 explode (cave, x, y);
1680 cave->player_seen_ago = 0;
1681 /* bd4 intermission caves have many players. so if one of them has exited,
1682 * do not change the flag anymore. so this if () is needed */
1683 if (cave->player_state != GD_PL_EXITED)
1684 cave->player_state = GD_PL_LIVING;
1686 /* check for pneumatic hammer things */
1687 /* 1) press fire, 2) have pneumatic hammer 4) space on left or right
1688 for hammer 5) stand on something */
1689 if (player_fire && cave->got_pneumatic_hammer &&
1690 is_space_dir(cave, x, y, player_move) &&
1691 !is_space_dir(cave, x, y, GD_MV_DOWN))
1693 if (player_move == GD_MV_LEFT &&
1694 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1696 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1697 store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1698 store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1699 break; /* finished. */
1702 if (player_move == GD_MV_RIGHT &&
1703 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1705 cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1706 store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1707 store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1708 break; /* finished. */
1712 if (player_move != GD_MV_STILL)
1714 /* only do every check if he is not moving */
1715 GdElement what = get_dir(cave, x, y, player_move);
1716 GdElement remains = what;
1719 /* if we are 'eating' a teleporter, and the function returns true
1720 (teleporting worked), break here */
1721 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1724 /* try to push element; if successful, break */
1725 push = do_push(cave, x, y, player_move, player_fire);
1735 /* if its a bomb, remember he now has one. */
1736 /* we do not change the "remains" and "what" variables,
1737 so that part of the code will be ineffective */
1738 gd_sound_play(cave, GD_S_BOMB_COLLECTING, what, x, y);
1739 store_dir(cave, x, y, player_move, O_SPACE);
1742 store(cave, x, y, O_PLAYER_BOMB);
1744 move(cave, x, y, player_move, O_PLAYER_BOMB);
1748 /* we do not change the "remains" and "what" variables,
1749 so that part of the code will be ineffective */
1750 if (!player_fire && !cave->gravity_switch_active &&
1751 cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1753 cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1754 move(cave, x, y, player_move, O_PLAYER_STIRRING);
1755 cave->gravity_disabled = TRUE;
1759 case O_GRAVITY_SWITCH:
1760 /* (we cannot use player_get for this as it does not have player_move parameter) */
1761 /* only allow changing direction if the new dir is not diagonal */
1762 if (cave->gravity_switch_active &&
1763 (player_move == GD_MV_LEFT ||
1764 player_move == GD_MV_RIGHT ||
1765 player_move == GD_MV_UP ||
1766 player_move == GD_MV_DOWN))
1768 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1769 cave->gravity_will_change =
1770 cave->gravity_change_time * cave->timing_factor;
1771 cave->gravity_next_direction = player_move;
1772 cave->gravity_switch_active = FALSE;
1777 /* get element - process others.
1778 if cannot get, player_get_element will return the same */
1779 remains = player_get_element(cave, what, x, y);
1784 if (remains != what || remains == O_SPACE)
1786 /* if anything changed, apply the change. */
1788 /* if snapping anything and we have snapping explosions set.
1789 but these is not true for pushing. */
1790 if (remains == O_SPACE && player_fire && !push)
1791 remains = cave->snap_element;
1793 if (remains != O_SPACE || player_fire)
1794 /* if any other element than space, player cannot move.
1795 also if pressing fire, will not move. */
1796 store_dir(cave, x, y, player_move, remains);
1798 /* if space remains there, the player moves. */
1799 move(cave, x, y, player_move, O_PLAYER);
1805 /* much simpler; cannot steal stones */
1806 if (cave->kill_player)
1808 explode(cave, x, y);
1812 cave->player_seen_ago = 0;
1813 /* bd4 intermission caves have many players. so if one of them has exited,
1814 * do not change the flag anymore. so this if () is needed */
1815 if (cave->player_state != GD_PL_EXITED)
1816 cave->player_state = GD_PL_LIVING;
1818 if (player_move != GD_MV_STILL)
1820 /* if the player does not move, nothing to do */
1821 GdElement what = get_dir(cave, x, y, player_move);
1822 GdElement remains = what;
1826 /* placing a bomb into empty space or dirt */
1827 if (is_space_dir(cave, x, y, player_move) ||
1828 is_element_dir(cave, x, y, player_move, O_DIRT))
1830 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1832 /* placed bomb, he is normal player again */
1833 store(cave, x, y, O_PLAYER);
1834 gd_sound_play(cave, GD_S_BOMB_PLACING, O_BOMB, x, y);
1839 /* pushing and collecting */
1840 /* if we are 'eating' a teleporter, and the function returns true
1841 (teleporting worked), break here */
1842 if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1845 /* player fire is false... */
1846 if (do_push(cave, x, y, player_move, FALSE))
1854 case O_GRAVITY_SWITCH:
1855 /* (we cannot use player_get for this as it does not have
1856 player_move parameter) */
1857 /* only allow changing direction if the new dir is not diagonal */
1858 if (cave->gravity_switch_active &&
1859 (player_move == GD_MV_LEFT ||
1860 player_move == GD_MV_RIGHT ||
1861 player_move == GD_MV_UP ||
1862 player_move == GD_MV_DOWN))
1864 gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1865 cave->gravity_will_change =
1866 cave->gravity_change_time * cave->timing_factor;
1867 cave->gravity_next_direction = player_move;
1868 cave->gravity_switch_active = FALSE;
1873 /* get element. if cannot get, player_get_element will return the same */
1874 remains = player_get_element (cave, what, x, y);
1879 /* if element changed, OR there is space, move. */
1880 if (remains != what || remains == O_SPACE)
1882 /* if anything changed, apply the change. */
1883 move(cave, x, y, player_move, O_PLAYER_BOMB);
1888 case O_PLAYER_STIRRING:
1889 if (cave->kill_player)
1891 explode(cave, x, y);
1895 /* stirring sound, if no other walking sound or explosion */
1896 gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1898 cave->player_seen_ago = 0;
1899 /* bd4 intermission caves have many players. so if one of them has exited,
1900 * do not change the flag anymore. so this if () is needed */
1901 if (cave->player_state != GD_PL_EXITED)
1902 cave->player_state = GD_PL_LIVING;
1906 /* player "exits" stirring the pot by pressing fire */
1907 cave->gravity_disabled = FALSE;
1908 store(cave, x, y, O_PLAYER);
1909 cave->gravity_switch_active = TRUE;
1913 /* player holding pneumatic hammer */
1914 case O_PLAYER_PNEUMATIC_LEFT:
1915 case O_PLAYER_PNEUMATIC_RIGHT:
1916 /* usual player stuff */
1917 if (cave->kill_player)
1919 explode(cave, x, y);
1923 cave->player_seen_ago = 0;
1924 if (cave->player_state != GD_PL_EXITED)
1925 cave->player_state = GD_PL_LIVING;
1927 /* if hammering time is up, becomes a normal player again. */
1928 if (cave->pneumatic_hammer_active_delay == 0)
1929 store(cave, x, y, O_PLAYER);
1932 /* the active pneumatic hammer itself */
1933 case O_PNEUMATIC_ACTIVE_RIGHT:
1934 case O_PNEUMATIC_ACTIVE_LEFT:
1935 if (cave->pneumatic_hammer_active_delay == 0)
1939 /* pneumatic hammer element disappears */
1940 store(cave, x, y, O_SPACE);
1942 /* which is the new element which appears after that one is hammered? */
1943 new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1945 /* if there is a new element, display it */
1946 /* O_NONE might be returned, for example if the element being
1947 hammered explodes during hammering (by a nearby explosion) */
1948 if (new_elem != O_NONE)
1950 store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1952 /* and if walls reappear, remember it in array */
1953 if (cave->hammered_walls_reappear)
1957 wall_y = (y + 1) % cave->h;
1958 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1965 * S T O N E S, D I A M O N D S
1967 case O_STONE: /* standing stone */
1968 do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1971 case O_MEGA_STONE: /* standing mega_stone */
1972 do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1975 case O_DIAMOND: /* standing diamond */
1976 do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1979 case O_NUT: /* standing nut */
1980 do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1983 case O_DIRT_BALL: /* standing dirt ball */
1984 do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1987 case O_DIRT_LOOSE: /* standing loose dirt */
1988 do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1991 case O_FLYING_STONE: /* standing stone */
1992 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1995 case O_FLYING_DIAMOND: /* standing diamond */
1996 do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
2000 * 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
2002 case O_DIRT_BALL_F: /* falling dirt ball */
2003 if (!cave->gravity_disabled)
2004 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
2007 case O_DIRT_LOOSE_F: /* falling loose dirt */
2008 if (!cave->gravity_disabled)
2009 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
2012 case O_STONE_F: /* falling stone */
2013 if (!cave->gravity_disabled)
2015 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2018 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
2021 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
2024 if (do_fall_try_crush(cave, x, y, cave->gravity))
2027 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
2031 case O_MEGA_STONE_F: /* falling mega */
2032 if (!cave->gravity_disabled)
2034 if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
2037 if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
2040 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
2043 if (do_fall_try_crush(cave, x, y, cave->gravity))
2046 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
2050 case O_DIAMOND_F: /* falling diamond */
2051 if (!cave->gravity_disabled)
2053 if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
2056 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
2059 if (do_fall_try_crush(cave, x, y, cave->gravity))
2062 do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
2066 case O_NUT_F: /* falling nut */
2067 if (!cave->gravity_disabled)
2069 if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
2072 if (do_fall_try_crush(cave, x, y, cave->gravity))
2075 do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
2079 case O_FLYING_STONE_F: /* falling stone */
2080 if (!cave->gravity_disabled)
2082 GdDirection fall_dir = opposite[cave->gravity];
2084 if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
2087 if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
2090 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
2093 if (do_fall_try_crush(cave, x, y, fall_dir))
2096 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
2100 case O_FLYING_DIAMOND_F: /* falling diamond */
2101 if (!cave->gravity_disabled)
2103 GdDirection fall_dir = opposite[cave->gravity];
2105 if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
2108 if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
2111 if (do_fall_try_crush(cave, x, y, fall_dir))
2114 do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
2121 case O_NITRO_PACK: /* standing nitro pack */
2122 do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
2125 case O_NITRO_PACK_F: /* falling nitro pack */
2126 if (!cave->gravity_disabled)
2128 if (is_space_dir(cave, x, y, cave->gravity)) /* if space, falling further */
2129 move(cave, x, y, cave->gravity, get(cave, x, y));
2130 else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2132 /* try magic wall; if true, function did the work */
2134 else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2136 /* falling on a dirt, it does NOT explode - just stops at its place. */
2137 play_sound_of_element(cave, O_NITRO_PACK, x, y);
2138 store(cave, x, y, O_NITRO_PACK);
2141 /* falling on any other element it explodes */
2142 explode(cave, x, y);
2146 case O_NITRO_PACK_EXPLODE: /* a triggered nitro pack */
2147 explode(cave, x, y);
2158 /* if cannot move in any direction, becomes an enclosed cow */
2159 if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2160 !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2161 store(cave, x, y, O_COW_ENCLOSED_1);
2164 /* THIS IS THE CREATURE MOVE thing copied. */
2165 const GdDirection *creature_move;
2166 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2167 GdElement base; /* base element number (which is like O_***_1) */
2168 int dir, dirn, dirp; /* direction */
2172 dir = get(cave, x, y)-base; /* facing where */
2173 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2175 /* now change direction if backwards */
2176 if (cave->creatures_backwards)
2181 dirn = (dir + 3) & 3; /* fast turn */
2182 dirp = (dir + 1) & 3; /* slow turn */
2186 dirn = (dir + 1) & 3; /* fast turn */
2187 dirp = (dir + 3) & 3; /* slow turn */
2190 if (is_space_dir(cave, x, y, creature_move[dirn]))
2191 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2192 else if (is_space_dir(cave, x, y, creature_move[dir]))
2193 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2195 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2199 /* enclosed cows wait some time before turning to a skeleton */
2200 case O_COW_ENCLOSED_1:
2201 case O_COW_ENCLOSED_2:
2202 case O_COW_ENCLOSED_3:
2203 case O_COW_ENCLOSED_4:
2204 case O_COW_ENCLOSED_5:
2205 case O_COW_ENCLOSED_6:
2206 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2207 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2208 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2209 is_space_dir(cave, x, y, GD_MV_DOWN))
2210 store(cave, x, y, O_COW_1);
2215 case O_COW_ENCLOSED_7:
2216 if (is_space_dir(cave, x, y, GD_MV_UP) ||
2217 is_space_dir(cave, x, y, GD_MV_LEFT) ||
2218 is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2219 is_space_dir(cave, x, y, GD_MV_DOWN))
2220 store(cave, x, y, O_COW_1);
2222 store(cave, x, y, O_SKELETON);
2229 case O_ALT_FIREFLY_1:
2230 case O_ALT_FIREFLY_2:
2231 case O_ALT_FIREFLY_3:
2232 case O_ALT_FIREFLY_4:
2237 case O_ALT_BUTTER_1:
2238 case O_ALT_BUTTER_2:
2239 case O_ALT_BUTTER_3:
2240 case O_ALT_BUTTER_4:
2245 /* check if touches a voodoo */
2246 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2247 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2248 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2249 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2250 cave->voodoo_touched = TRUE;
2252 /* check if touches something bad and should explode (includes voodoo by the flags) */
2253 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2254 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2255 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2256 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2257 explode (cave, x, y);
2258 /* otherwise move */
2261 const GdDirection *creature_move;
2262 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2263 GdElement base = -1; /* base element number (which is like O_***_1) */
2264 int dir, dirn, dirp; /* direction */
2266 if (get(cave, x, y) >= O_FIREFLY_1 &&
2267 get(cave, x, y) <= O_FIREFLY_4)
2269 else if (get(cave, x, y) >= O_BUTTER_1 &&
2270 get(cave, x, y) <= O_BUTTER_4)
2272 else if (get(cave, x, y) >= O_STONEFLY_1 &&
2273 get(cave, x, y) <= O_STONEFLY_4)
2274 base = O_STONEFLY_1;
2275 else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2276 get(cave, x, y) <= O_ALT_FIREFLY_4)
2277 base = O_ALT_FIREFLY_1;
2278 else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2279 get(cave, x, y) <= O_ALT_BUTTER_4)
2280 base = O_ALT_BUTTER_1;
2282 dir = get(cave, x, y) - base; /* facing where */
2283 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2285 /* now change direction if backwards */
2286 if (cave->creatures_backwards)
2291 dirn = (dir + 3) & 3; /* fast turn */
2292 dirp = (dir + 1) & 3; /* slow turn */
2296 dirn = (dir + 1) & 3; /* fast turn */
2297 dirp = (dir + 3) & 3; /* slow turn */
2300 if (is_space_dir(cave, x, y, creature_move[dirn]))
2301 move(cave, x, y, creature_move[dirn], base + dirn); /* turn and move to preferred dir */
2302 else if (is_space_dir(cave, x, y, creature_move[dir]))
2303 move(cave, x, y, creature_move[dir], base + dir); /* go on */
2305 store(cave, x, y, base + dirp); /* turn in place if nothing else possible */
2309 case O_WAITING_STONE:
2310 if (is_space_dir(cave, x, y, grav_compat))
2312 /* beginning to fall */
2314 move(cave, x, y, grav_compat, O_CHASING_STONE);
2316 else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2318 /* rolling down a brick wall or a stone */
2319 if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2320 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2321 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2323 /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2324 move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2326 else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2327 is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2328 is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2330 /* or maybe right */
2331 move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2336 case O_CHASING_STONE:
2338 int px = cave->px[0];
2339 int py = cave->py[0];
2340 boolean horizontal = g_rand_boolean(cave->random);
2341 boolean dont_move = FALSE;
2344 /* try to move... */
2349 /*********************************/
2350 /* check for a horizontal movement */
2353 /* if coordinates are the same */
2355 horizontal = !horizontal;
2362 if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2364 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2368 else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2370 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2379 horizontal = !horizontal;
2387 /********************************/
2388 /* check for a vertical movement */
2391 /* if coordinates are the same */
2393 horizontal = !horizontal;
2399 if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2401 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2405 else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2407 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2416 horizontal = !horizontal;
2429 /* if we should move in both directions, but can not move in any, stop. */
2434 /* check for horizontal */
2437 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2438 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2439 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2440 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2441 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2442 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2446 if (is_space_dir(cave, x, y, GD_MV_UP) &&
2447 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2448 move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2449 else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2450 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2451 move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2456 /* check for vertical */
2459 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2460 is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2461 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2462 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2463 is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2464 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2468 if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2469 is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2470 move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2471 else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2472 is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2473 move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2481 if (cave->replicators_wait_frame == 0 &&
2482 cave->replicators_active &&
2483 !cave->gravity_disabled)
2485 /* only replicate, if space is under it. */
2486 /* do not replicate players! */
2487 /* also obeys gravity settings. */
2488 /* only replicate element if it is not a scanned one */
2489 /* do not replicate space... that condition looks like it
2490 makes no sense, but otherwise it generates SCANNED spaces,
2491 which cannot be "collected" by the player, so he cannot run
2492 under a replicator */
2493 if (is_space_dir(cave, x, y, cave->gravity) &&
2494 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2495 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2497 store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2498 gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2507 if (cave->biters_wait_frame == 0)
2509 static GdDirection biter_move[] =
2517 /* direction, last two bits 0..3 */
2518 int dir = get(cave, x, y) - O_BITER_1;
2519 int dirn = (dir + 3) & 3;
2520 int dirp = (dir + 1) & 3;
2522 GdElement made_sound_of = O_NONE;
2524 for (i = 0; i < G_N_ELEMENTS (biter_try); i++)
2526 if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2528 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2529 if (biter_try[i] != O_SPACE)
2530 made_sound_of = O_BITER_1; /* sound of a biter eating */
2533 else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2535 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2536 if (biter_try[i] != O_SPACE)
2537 made_sound_of = O_BITER_1; /* sound of a biter eating */
2540 else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2542 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2543 if (biter_try[i] != O_SPACE)
2544 made_sound_of = O_BITER_1; /* sound of a biter eating */
2549 if (i == G_N_ELEMENTS(biter_try))
2550 /* i = number of elements in array: could not move, so just turn */
2551 store(cave, x, y, O_BITER_1 + dirp);
2552 else if (biter_try[i] == O_STONE)
2554 /* if there was a stone there, where we moved...
2555 do not eat stones, just throw them back */
2556 store(cave, x, y, O_STONE);
2557 made_sound_of = O_STONE;
2560 /* if biter did move, we had sound. play it. */
2561 if (made_sound_of != O_NONE)
2562 play_sound_of_element(cave, made_sound_of, x, y);
2570 /* check if touches a voodoo */
2571 if (get_dir(cave, x, y, GD_MV_LEFT) == O_VOODOO ||
2572 get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2573 get_dir(cave, x, y, GD_MV_UP) == O_VOODOO ||
2574 get_dir(cave, x, y, GD_MV_DOWN) == O_VOODOO)
2575 cave->voodoo_touched = TRUE;
2577 /* check if touches something bad and should explode (includes voodoo by the flags) */
2578 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2579 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2580 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2581 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2582 explode (cave, x, y);
2583 /* otherwise move */
2586 const GdDirection *creature_move;
2587 boolean ccw = rotates_ccw(cave, x, y); /* check if default is counterclockwise */
2588 GdElement base = O_DRAGONFLY_1; /* base element number (which is like O_***_1) */
2589 int dir, dirn; /* direction */
2591 dir = get(cave, x, y)-base; /* facing where */
2592 creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2594 /* now change direction if backwards */
2595 if (cave->creatures_backwards)
2599 dirn = (dir + 3) & 3; /* fast turn */
2601 dirn = (dir + 1) & 3; /* fast turn */
2603 /* if can move forward, does so. */
2604 if (is_space_dir(cave, x, y, creature_move[dir]))
2605 move(cave, x, y, creature_move[dir], base + dir);
2607 /* otherwise turns 90 degrees in place. */
2608 store(cave, x, y, base + dirn);
2613 store(cave, x, y, O_BLADDER_1);
2624 /* bladder with any delay state: try to convert to clock. */
2625 if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2626 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))
2628 /* if touches the specified element, let it be a clock */
2629 store(cave, x, y, O_PRE_CLOCK_1);
2631 /* plays the bladder convert sound */
2632 play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2636 /* is space over the bladder? */
2637 if (is_space_dir(cave, x, y, opposite[grav_compat]))
2639 if (get(cave, x, y) == O_BLADDER_8)
2641 /* if it is a bladder 8, really move up */
2642 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2643 play_sound_of_element(cave, O_BLADDER, x, y);
2646 /* if smaller delay, just increase delay. */
2650 /* if not space, is something sloped over the bladder? */
2651 if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2652 sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2654 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2655 is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2656 is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2658 /* rolling up, to left */
2659 if (get(cave, x, y) == O_BLADDER_8)
2661 /* if it is a bladder 8, really roll */
2662 move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2663 play_sound_of_element(cave, O_BLADDER, x, y);
2666 /* if smaller delay, just increase delay. */
2669 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2670 is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2671 is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2673 /* rolling up, to left */
2674 if (get(cave, x, y) == O_BLADDER_8)
2676 /* if it is a bladder 8, really roll */
2677 move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2678 play_sound_of_element(cave, O_BLADDER, x, y);
2681 /* if smaller delay, just increase delay. */
2686 /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2688 store(cave, x, y, O_BLADDER_1);
2693 if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2694 blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2695 blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2696 blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2697 explode (cave, x, y);
2702 /* the ghost is given four possibilities to move. */
2703 for (i = 0; i < 4; i++)
2705 static GdDirection dirs[] =
2712 GdDirection random_dir;
2714 random_dir = dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2715 if (is_space_dir(cave, x, y, random_dir))
2717 move(cave, x, y, random_dir, O_GHOST);
2718 break; /* ghost did move -> exit loop */
2725 * A C T I V E E L E M E N T S
2730 switch (cave->amoeba_state)
2733 store(cave, x, y, cave->amoeba_too_big_effect);
2736 case GD_AM_ENCLOSED:
2737 store(cave, x, y, cave->amoeba_enclosed_effect);
2740 case GD_AM_SLEEPING:
2742 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2743 if (amoeba_found_enclosed)
2744 /* if still found enclosed, check all four directions,
2745 if this one is able to grow. */
2746 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2747 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2748 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2749 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2751 /* not enclosed. this is a local (per scan) flag! */
2752 amoeba_found_enclosed = FALSE;
2753 cave->amoeba_state = GD_AM_AWAKE;
2756 /* if alive, check in which dir to grow (or not) */
2757 if (cave->amoeba_state == GD_AM_AWAKE)
2759 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2761 switch (g_rand_int_range(cave->random, 0, 4))
2763 /* decided to grow, choose a random direction. */
2764 case 0: /* let this be up. numbers indifferent. */
2765 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2766 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2770 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2771 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2775 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2776 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2780 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2781 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2793 /* check if it is touching an amoeba, and explosion is enabled */
2794 if (cave->amoeba_2_explodes_by_amoeba &&
2795 (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2796 is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2797 is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2798 is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2799 explode (cave, x, y);
2801 switch (cave->amoeba_2_state)
2804 store(cave, x, y, cave->amoeba_2_too_big_effect);
2807 case GD_AM_ENCLOSED:
2808 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2811 case GD_AM_SLEEPING:
2813 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2814 if (amoeba_2_found_enclosed)
2815 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2816 amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2817 amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2818 amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2820 /* not enclosed. this is a local (per scan) flag! */
2821 amoeba_2_found_enclosed = FALSE;
2822 cave->amoeba_2_state = GD_AM_AWAKE;
2825 /* if it is alive, decide if it attempts to grow */
2826 if (cave->amoeba_2_state == GD_AM_AWAKE)
2827 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2829 switch (g_rand_int_range(cave->random, 0, 4))
2831 /* decided to grow, choose a random direction. */
2832 case 0: /* let this be up. numbers indifferent. */
2833 if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2834 store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2838 if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2839 store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2843 if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2844 store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2848 if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2849 store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2859 /* choose randomly, if it spreads */
2860 if (g_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2862 /* the current one explodes */
2863 store(cave, x, y, cave->acid_turns_to);
2865 /* and if neighbours are eaten, put acid there. */
2866 if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2868 play_sound_of_element(cave, O_ACID, x, y);
2869 store_dir(cave, x, y, GD_MV_UP, O_ACID);
2872 if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2874 play_sound_of_element(cave, O_ACID, x, y);
2875 store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2878 if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2880 play_sound_of_element(cave, O_ACID, x, y);
2881 store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2884 if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2886 play_sound_of_element(cave, O_ACID, x, y);
2887 store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2894 if (!cave->water_does_not_flow_down &&
2895 is_space_dir(cave, x, y, GD_MV_DOWN))
2896 /* emulating the odd behaviour in crdr */
2897 store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2899 if (is_space_dir(cave, x, y, GD_MV_UP))
2900 store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2902 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2903 store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2905 if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2906 store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2910 store(cave, x, y, O_WATER);
2913 case O_H_EXPANDING_WALL:
2914 case O_V_EXPANDING_WALL:
2915 case O_H_EXPANDING_STEEL_WALL:
2916 case O_V_EXPANDING_STEEL_WALL:
2917 /* checks first if direction is changed. */
2918 if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2919 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2920 !cave->expanding_wall_changed) ||
2921 ((get(cave, x, y) == O_V_EXPANDING_WALL ||
2922 get(cave, x, y) == O_V_EXPANDING_STEEL_WALL) &&
2923 cave->expanding_wall_changed))
2925 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2927 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2928 play_sound_of_element(cave, get(cave, x, y), x, y);
2931 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2932 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2933 play_sound_of_element(cave, get(cave, x, y), x, y);
2938 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2939 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2940 play_sound_of_element(cave, get(cave, x, y), x, y);
2943 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2944 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2945 play_sound_of_element(cave, get(cave, x, y), x, y);
2950 case O_EXPANDING_WALL:
2951 case O_EXPANDING_STEEL_WALL:
2952 /* the wall which grows in all four directions. */
2953 if (is_space_dir(cave, x, y, GD_MV_LEFT))
2955 store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2956 play_sound_of_element(cave, get(cave, x, y), x, y);
2959 if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2960 store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2961 play_sound_of_element(cave, get(cave, x, y), x, y);
2964 if (is_space_dir(cave, x, y, GD_MV_UP)) {
2965 store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2966 play_sound_of_element(cave, get(cave, x, y), x, y);
2969 if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2970 store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2971 play_sound_of_element(cave, get(cave, x, y), x, y);
2977 ; // to make compilers happy ...
2979 g_print("Step[%03d]", cave->frame); /* XXX */
2981 int rrr = gd_cave_c64_random(cave);
2984 g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2985 (rrr & cave->slime_permeability_c64) == 0);
2988 * unpredictable: g_rand_int
2989 * predictable: c64 predictable random generator.
2990 * for predictable, a random number is generated,
2991 * whether or not it is even possible that the stone will be able to pass.
2993 if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : g_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2995 GdDirection grav = cave->gravity;
2996 GdDirection oppos = opposite[cave->gravity];
2998 /* space under the slime? elements may pass from top to bottom then. */
2999 if (is_space_dir(cave, x, y, grav))
3001 if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
3003 /* output a falling xy under */
3004 store_dir(cave, x, y, grav, cave->slime_converts_1);
3006 store_dir(cave, x, y, oppos, O_SPACE);
3007 play_sound_of_element(cave, O_SLIME, x, y);
3009 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
3011 store_dir(cave, x, y, grav, cave->slime_converts_2);
3012 store_dir(cave, x, y, oppos, O_SPACE);
3013 play_sound_of_element(cave, O_SLIME, x, y);
3015 else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
3017 store_dir(cave, x, y, grav, cave->slime_converts_3);
3018 store_dir(cave, x, y, oppos, O_SPACE);
3019 play_sound_of_element(cave, O_SLIME, x, y);
3021 else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
3023 /* waiting stones pass without awakening */
3024 store_dir(cave, x, y, grav, O_WAITING_STONE);
3025 store_dir(cave, x, y, oppos, O_SPACE);
3026 play_sound_of_element(cave, O_SLIME, x, y);
3028 else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
3030 /* chasing stones pass */
3031 store_dir(cave, x, y, grav, O_CHASING_STONE);
3032 store_dir(cave, x, y, oppos, O_SPACE);
3033 play_sound_of_element(cave, O_SLIME, x, y);
3037 /* or space over the slime? elements may pass from bottom to up then. */
3038 if (is_space_dir(cave, x, y, oppos))
3040 if (get_dir(cave, x, y, grav) == O_BLADDER)
3042 /* bladders move UP the slime */
3043 store_dir(cave, x, y, grav, O_SPACE);
3044 store_dir(cave, x, y, oppos, O_BLADDER_1);
3045 play_sound_of_element(cave, O_SLIME, x, y);
3047 else if (get_dir(cave, x, y, grav) == O_FLYING_STONE)
3049 store_dir(cave, x, y, grav, O_SPACE);
3050 store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
3051 play_sound_of_element(cave, O_SLIME, x, y);
3053 else if (get_dir(cave, x, y, grav) == O_FLYING_DIAMOND)
3055 store_dir(cave, x, y, grav, O_SPACE);
3056 store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
3057 play_sound_of_element(cave, O_SLIME, x, y);
3063 case O_FALLING_WALL:
3064 if (is_space_dir(cave, x, y, grav_compat))
3066 /* try falling if space under. */
3069 for (yy = y + 1; yy < y + cave->h; yy++)
3070 /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
3071 if (get(cave, x, yy) != O_SPACE)
3072 /* stop cycle when other than space */
3075 /* if scanning stopped by a player... start falling! */
3076 if (get(cave, x, yy) == O_PLAYER ||
3077 get(cave, x, yy) == O_PLAYER_GLUED ||
3078 get(cave, x, yy) == O_PLAYER_BOMB)
3080 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3081 /* no sound when the falling wall starts falling! */
3086 case O_FALLING_WALL_F:
3087 switch (get_dir(cave, x, y, grav_compat))
3090 case O_PLAYER_GLUED:
3092 /* if player under, it explodes - the falling wall, not the player! */
3093 explode(cave, x, y);
3097 /* continue falling */
3098 move(cave, x, y, grav_compat, O_FALLING_WALL_F);
3103 play_sound_of_element(cave, get(cave, x, y), x, y);
3104 store(cave, x, y, O_FALLING_WALL);
3110 * C O N V E Y O R B E L T S
3113 case O_CONVEYOR_RIGHT:
3114 case O_CONVEYOR_LEFT:
3115 /* only works if gravity is up or down!!! */
3116 /* first, check for gravity and running belts. */
3117 if (!cave->gravity_disabled && cave->conveyor_belts_active)
3119 const GdDirection *dir;
3122 /* decide direction */
3123 left = get(cave, x, y) != O_CONVEYOR_RIGHT;
3124 if (cave->conveyor_belts_direction_changed)
3126 dir = left ? ccw_eighth : cw_eighth;
3128 /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
3129 /* if gravity is normal, and the conveyor belt has something
3130 ABOVE which can be moved
3132 the gravity is up, so anything that should float now goes
3133 DOWN and touches the conveyor */
3134 if ((cave->gravity == GD_MV_DOWN &&
3135 moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3136 (cave->gravity == GD_MV_UP &&
3137 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3139 if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3140 is_space_dir(cave, x, y, dir[GD_MV_UP]))
3142 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP)); /* move */
3143 store_dir(cave, x, y, GD_MV_UP, O_SPACE); /* and place a space. */
3147 /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3148 if ((cave->gravity == GD_MV_UP &&
3149 moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3150 (cave->gravity == GD_MV_DOWN &&
3151 moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3153 if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3154 is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3156 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN)); /* move */
3157 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE); /* and clear. */
3164 * S I M P L E C H A N G I N G; E X P L O S I O N S
3168 store(cave, x, y, cave->explosion_effect);
3172 store(cave, x, y, O_DIAMOND);
3176 store(cave, x, y, cave->diamond_birth_effect);
3180 store(cave, x, y, O_STONE);
3183 case O_NITRO_EXPL_4:
3184 store(cave, x, y, cave->nitro_explosion_effect);
3188 store(cave, x, y, cave->bomb_explosion_effect);
3191 case O_AMOEBA_2_EXPL_4:
3192 store(cave, x, y, cave->amoeba_2_explosion_effect);
3195 case O_GHOST_EXPL_4:
3197 static GdElement ghost_explode[] =
3199 O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3200 O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3201 O_WAITING_STONE, O_BITER_1
3204 store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
3209 store(cave, x, y, O_STEEL);
3213 store(cave, x, y, O_CLOCK);
3217 explode(cave, x, y);
3220 case O_TRAPPED_DIAMOND:
3221 if (cave->diamond_key_collected)
3222 store(cave, x, y, O_DIAMOND);
3226 if (cave->gate_open) /* if no more diamonds needed */
3227 store(cave, x, y, O_OUTBOX); /* open outbox */
3230 case O_PRE_INVIS_OUTBOX:
3231 if (cave->gate_open) /* if no more diamonds needed */
3232 store(cave, x, y, O_INVIS_OUTBOX); /* open outbox. invisible one :P */
3236 if (cave->hatched && !inbox_toggle) /* if it is time of birth */
3237 store(cave, x, y, O_PRE_PL_1);
3238 inbox_toggle = !inbox_toggle;
3242 store(cave, x, y, O_PLAYER);
3267 case O_GHOST_EXPL_1:
3268 case O_GHOST_EXPL_2:
3269 case O_GHOST_EXPL_3:
3279 case O_NITRO_EXPL_1:
3280 case O_NITRO_EXPL_2:
3281 case O_NITRO_EXPL_3:
3282 case O_AMOEBA_2_EXPL_1:
3283 case O_AMOEBA_2_EXPL_2:
3284 case O_AMOEBA_2_EXPL_3:
3285 /* simply the next identifier */
3304 found_water = TRUE; /* for sound */
3305 /* simply the next identifier */
3309 case O_BLADDER_SPENDER:
3310 if (is_space_dir(cave, x, y, opposite[grav_compat]))
3312 store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3313 store(cave, x, y, O_PRE_STEEL_1);
3314 play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3319 /* other inanimate elements that do nothing */
3325 /* POSTPROCESSING */
3327 /* another scan-like routine: */
3328 /* short explosions (for example, in bd1) started with explode_2. */
3329 /* internally we use explode_1; and change it to explode_2 if needed. */
3330 if (cave->short_explosions)
3332 for (y = 0; y < cave->h; y++)
3334 for (x = 0; x < cave->w; x++)
3336 if (is_first_stage_of_explosion(cave, x, y))
3338 next(cave, x, y); /* select next frame of explosion */
3340 /* forget scanned flag immediately */
3341 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3347 /* finally: forget "scanned" flags for objects. */
3348 /* also, check for time penalties. */
3349 /* these is something like an effect table, but we do not really use one. */
3350 for (y = 0; y < cave->h; y++)
3352 for (x = 0; x < cave->w; x++)
3354 if (get(cave, x, y) & SCANNED)
3355 store(cave, x, y, get(cave, x, y) & ~SCANNED);
3357 if (get(cave, x, y) == O_TIME_PENALTY)
3359 store(cave, x, y, O_GRAVESTONE);
3361 /* there is time penalty for destroying the voodoo */
3362 time_decrement_sec += cave->time_penalty;
3367 /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3368 /* but we only do this, if a living player was found. if not yet, the setup
3369 routine coordinates are used */
3370 if (cave->player_state == GD_PL_LIVING)
3372 if (cave->active_is_first_found)
3374 /* to be 1stb compatible, we do everything backwards. */
3375 for (y = cave->h - 1; y >= 0; y--)
3377 for (x = cave->w - 1; x >= 0; x--)
3379 if (is_player(cave, x, y))
3381 /* here we remember the coordinates. */
3390 /* as in the original: look for the last one */
3391 for (y = 0; y < cave->h; y++)
3393 for (x = 0; x < cave->w; x++)
3395 if (is_player(cave, x, y))
3397 /* here we remember the coordinates. */
3406 /* record coordinates of player for chasing stone */
3407 for (i = 0; i < G_N_ELEMENTS(cave->px) - 1; i++)
3409 cave->px[i] = cave->px[i + 1];
3410 cave->py[i] = cave->py[i + 1];
3413 cave->px[G_N_ELEMENTS(cave->px) - 1] = cave->player_x;
3414 cave->py[G_N_ELEMENTS(cave->py) - 1] = cave->player_y;
3418 /* update timing calculated by iterating and counting elements */
3419 update_cave_speed(cave);
3421 /* cave 3 sounds. precedence is controlled by the sound_play function. */
3422 /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3424 gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3426 magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3427 cave->magic_wall_sound);
3429 amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3430 ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3431 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3433 if (amoeba_sound && magic_sound)
3435 gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3440 gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3441 else if (magic_sound)
3442 gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3447 if ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3448 (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3449 play_sound_of_element(cave, O_AMOEBA, x, y);
3452 /* pneumatic hammer sound - overrides everything. */
3453 if (cave->pneumatic_hammer_active_delay > 0)
3454 gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3456 /* CAVE VARIABLES */
3460 /* check if player is alive. */
3461 if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3462 cave->player_state = GD_PL_DIED;
3464 /* check if any voodoo exploded, and kill players the next scan if that happended. */
3465 if (cave->voodoo_touched)
3466 cave->kill_player = TRUE;
3470 /* check flags after evaluating. */
3471 if (cave->amoeba_state == GD_AM_AWAKE)
3473 if (amoeba_count >= cave->amoeba_max_count)
3474 cave->amoeba_state = GD_AM_TOO_BIG;
3475 if (amoeba_found_enclosed)
3476 cave->amoeba_state = GD_AM_ENCLOSED;
3479 /* amoeba can also be turned into diamond by magic wall */
3480 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3481 cave->amoeba_state = GD_AM_ENCLOSED;
3484 if (cave->amoeba_2_state == GD_AM_AWAKE)
3486 /* check flags after evaluating. */
3487 if (amoeba_2_count >= cave->amoeba_2_max_count)
3488 cave->amoeba_2_state = GD_AM_TOO_BIG;
3490 if (amoeba_2_found_enclosed)
3491 cave->amoeba_2_state = GD_AM_ENCLOSED;
3494 /* amoeba 2 can also be turned into diamond by magic wall */
3495 if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3496 cave->amoeba_2_state = GD_AM_ENCLOSED;
3498 /* now check times. --------------------------- */
3499 /* decrement time if a voodoo was killed. */
3500 cave->time -= time_decrement_sec * cave->timing_factor;
3504 /* only decrement time when player is already born. */
3507 int secondsbefore, secondsafter;
3509 secondsbefore = cave->time / cave->timing_factor;
3510 cave->time -= cave->speed;
3511 if (cave->time <= 0)
3514 secondsafter = cave->time / cave->timing_factor;
3515 if (cave->time / cave->timing_factor < 10)
3516 /* if less than 10 seconds, no walking sound, but play explosion sound */
3517 gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3519 if (secondsbefore != secondsafter)
3520 gd_cave_set_seconds_sound(cave);
3523 /* a gravity switch was activated; seconds counting down */
3524 if (cave->gravity_will_change > 0)
3526 cave->gravity_will_change -= cave->speed;
3527 if (cave->gravity_will_change < 0)
3528 cave->gravity_will_change = 0;
3530 if (cave->gravity_will_change == 0)
3532 cave->gravity = cave->gravity_next_direction;
3533 gd_sound_play(cave, GD_S_GRAVITY_CHANGING, O_GRAVITY_SWITCH, -1, -1); /* takes precedence over amoeba and magic wall sound */
3537 /* creatures direction automatically change */
3538 if (cave->creatures_direction_will_change > 0)
3540 cave->creatures_direction_will_change -= cave->speed;
3541 if (cave->creatures_direction_will_change < 0)
3542 cave->creatures_direction_will_change = 0;
3544 if (cave->creatures_direction_will_change == 0)
3546 gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3548 cave->creatures_backwards = !cave->creatures_backwards;
3549 cave->creatures_direction_will_change =
3550 cave->creatures_direction_auto_change_time * cave->timing_factor;
3554 /* magic wall; if active&wait or not wait for hatching */
3555 if (cave->magic_wall_state == GD_MW_ACTIVE &&
3556 (cave->hatched || !cave->magic_timer_wait_for_hatching))
3558 cave->magic_wall_time -= cave->speed;
3559 if (cave->magic_wall_time < 0)
3560 cave->magic_wall_time = 0;
3561 if (cave->magic_wall_time == 0)
3562 cave->magic_wall_state = GD_MW_EXPIRED;
3565 /* we may wait for hatching, when starting amoeba */
3566 if (cave->amoeba_timer_started_immediately ||
3567 (cave->amoeba_state == GD_AM_AWAKE &&
3568 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3570 cave->amoeba_time -= cave->speed;
3571 if (cave->amoeba_time < 0)
3572 cave->amoeba_time = 0;
3573 if (cave->amoeba_time == 0)
3574 cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3577 /* we may wait for hatching, when starting amoeba */
3578 if (cave->amoeba_timer_started_immediately ||
3579 (cave->amoeba_2_state == GD_AM_AWAKE &&
3580 (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3582 cave->amoeba_2_time -= cave->speed;
3583 if (cave->amoeba_2_time < 0)
3584 cave->amoeba_2_time = 0;
3585 if (cave->amoeba_2_time == 0)
3586 cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3589 /* check for player hatching. */
3590 start_signal = FALSE;
3592 /* if not the c64 scheduling, but the correct frametime is used,
3593 hatching delay should always be decremented. */
3594 /* otherwise, the if (millisecs...) condition below will set this. */
3595 if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3597 /* NON-C64 scheduling */
3598 if (cave->hatching_delay_frame > 0)
3600 /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3601 cave->hatching_delay_frame--;
3602 if (cave->hatching_delay_frame == 0)
3603 start_signal = TRUE;
3608 /* C64 scheduling */
3609 if (cave->hatching_delay_time > 0)
3611 /* for c64 schedulings, hatching delay means milliseconds. */
3612 cave->hatching_delay_time -= cave->speed;
3613 if (cave->hatching_delay_time <= 0)
3615 cave->hatching_delay_time = 0;
3616 start_signal = TRUE;
3621 /* if decremented hatching, and it became zero: */
3624 /* THIS IS THE CAVE START SIGNAL */
3626 /* record that now the cave is in its normal state */
3627 cave->hatched = TRUE;
3629 /* if diamonds needed is below zero, we count the available diamonds now. */
3630 gd_cave_count_diamonds(cave);
3632 /* setup direction auto change */
3633 if (cave->creatures_direction_auto_change_time)
3635 cave->creatures_direction_will_change =
3636 cave->creatures_direction_auto_change_time * cave->timing_factor;
3638 if (cave->creatures_direction_auto_change_on_start)
3639 cave->creatures_backwards = !cave->creatures_backwards;
3642 gd_sound_play(cave, GD_S_CRACKING, O_INBOX, -1, -1);
3646 if (cave->biters_wait_frame == 0)
3647 cave->biters_wait_frame = cave->biter_delay_frame;
3649 cave->biters_wait_frame--;
3651 /* replicators delay */
3652 if (cave->replicators_wait_frame == 0)
3653 cave->replicators_wait_frame = cave->replicator_delay_frame;
3655 cave->replicators_wait_frame--;
3660 /* check if cave failed by timeout is done in main game engine */
3662 /* check if cave failed by timeout */
3663 if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3665 gd_cave_clear_sounds(cave);
3666 cave->player_state = GD_PL_TIMEOUT;
3667 gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3671 /* set these for drawing. */
3672 cave->last_direction = player_move;
3673 /* here we remember last movements for animation. this is needed here,
3674 as animation is in sync with the game, not the keyboard directly.
3675 (for example, after exiting the cave, the player was "running" in the
3676 original, till bonus points were counted for remaining time and so on. */
3677 if (player_move == GD_MV_LEFT ||
3678 player_move == GD_MV_UP_LEFT ||
3679 player_move == GD_MV_DOWN_LEFT)
3680 cave->last_horizontal_direction = GD_MV_LEFT;
3682 if (player_move == GD_MV_RIGHT ||
3683 player_move == GD_MV_UP_RIGHT ||
3684 player_move == GD_MV_DOWN_RIGHT)
3685 cave->last_horizontal_direction = GD_MV_RIGHT;
3687 cave->frame++; /* XXX */
3690 void set_initial_cave_speed(GdCave *cave)
3695 /* set cave get function; to implement perfect or lineshifting borders */
3696 if (cave->lineshift)
3697 cave->getp = getp_shift;
3699 cave->getp = getp_perfect;
3701 /* check whether to scan the first and last line */
3702 if (cave->border_scan_first_and_last)
3713 for (y = ymin; y <= ymax; y++)
3715 for (x = 0; x < cave->w; x++)
3717 /* add the ckdelay correction value for every element seen. */
3718 cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3722 /* update timing calculated by iterating and counting elements */
3723 update_cave_speed(cave);