5788933bfcdb041661eb6c41c0cddf352349fb51
[rocksndiamonds.git] / src / game_bd / bd_caveengine.c
1 /*
2  * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
3  *
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.
7  *
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.
15  */
16
17 /* IMPORTANT NOTES */
18
19 /*
20  * LAVA.
21  *
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.
26  *
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.
31  */
32
33 #include <glib.h>
34
35 #include "main_bd.h"
36
37
38 /* for gravity */
39 static const GdDirection ccw_eighth[] =
40 {
41   GD_MV_STILL,
42   GD_MV_UP_LEFT,
43   GD_MV_UP,
44   GD_MV_UP_RIGHT,
45   GD_MV_RIGHT,
46   GD_MV_DOWN_RIGHT,
47   GD_MV_DOWN,
48   GD_MV_DOWN_LEFT
49 };
50
51 static const GdDirection ccw_fourth[] =
52 {
53   GD_MV_STILL,
54   GD_MV_LEFT,
55   GD_MV_UP_LEFT,
56   GD_MV_UP,
57   GD_MV_UP_RIGHT,
58   GD_MV_RIGHT,
59   GD_MV_DOWN_RIGHT,
60   GD_MV_DOWN,
61   GD_MV_DOWN_LEFT,
62   GD_MV_LEFT
63 };
64
65 static const GdDirection cw_eighth[] =
66 {
67   GD_MV_STILL,
68   GD_MV_UP_RIGHT,
69   GD_MV_RIGHT,
70   GD_MV_DOWN_RIGHT,
71   GD_MV_DOWN,
72   GD_MV_DOWN_LEFT,
73   GD_MV_LEFT,
74   GD_MV_UP_LEFT,
75   GD_MV_UP
76 };
77
78 static const GdDirection cw_fourth[] =
79 {
80   GD_MV_STILL,
81   GD_MV_RIGHT,
82   GD_MV_DOWN_RIGHT,
83   GD_MV_DOWN,
84   GD_MV_DOWN_LEFT,
85   GD_MV_LEFT,
86   GD_MV_UP_LEFT,
87   GD_MV_UP,
88   GD_MV_UP_RIGHT
89 };
90
91 static const GdDirection opposite[] =
92 {
93   GD_MV_STILL,
94   GD_MV_DOWN,
95   GD_MV_DOWN_LEFT,
96   GD_MV_LEFT,
97   GD_MV_UP_LEFT,
98   GD_MV_UP,
99   GD_MV_UP_RIGHT,
100   GD_MV_RIGHT,
101   GD_MV_DOWN_RIGHT
102 };
103
104 /* sets timeout sound. */
105 void gd_cave_set_seconds_sound(GdCave *cave)
106 {
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)
111     return;
112
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)
117   {
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;
128   }
129 }
130
131 /* play diamond or stone sound of given element. */
132 static void play_sound_of_element(GdCave *cave, GdElement element, int x, int y)
133 {
134   /* stone and diamond fall sounds. */
135   switch (element)
136   {
137     case O_NUT:
138     case O_NUT_F:
139       if (cave->nut_sound)
140         gd_sound_play(cave, GD_S_NUT, element, x, y);
141       break;
142
143     case O_STONE:
144     case O_STONE_F:
145     case O_FLYING_STONE:
146     case O_FLYING_STONE_F:
147     case O_MEGA_STONE:
148     case O_MEGA_STONE_F:
149     case O_WAITING_STONE:
150     case O_CHASING_STONE:
151       if (cave->stone_sound)
152         gd_sound_play(cave, GD_S_STONE, element, x, y);
153       break;
154
155     case O_NITRO_PACK:
156     case O_NITRO_PACK_F:
157       if (cave->nitro_sound)
158         gd_sound_play(cave, GD_S_NITRO, element, x, y);
159       break;
160
161     case O_FALLING_WALL:
162     case O_FALLING_WALL_F:
163       if (cave->falling_wall_sound)
164         gd_sound_play(cave, GD_S_FALLING_WALL, element, x, y);
165       break;
166
167     case O_H_EXPANDING_WALL:
168     case O_V_EXPANDING_WALL:
169     case O_EXPANDING_WALL:
170     case O_H_EXPANDING_STEEL_WALL:
171     case O_V_EXPANDING_STEEL_WALL:
172     case O_EXPANDING_STEEL_WALL:
173       if (cave->expanding_wall_sound)
174         gd_sound_play(cave, GD_S_EXPANDING_WALL, element, x, y);
175       break;
176
177     case O_DIAMOND:
178     case O_DIAMOND_F:
179     case O_FLYING_DIAMOND:
180     case O_FLYING_DIAMOND_F:
181       if (cave->diamond_sound)
182         gd_sound_play(cave, GD_S_DIAMOND_RANDOM, element, x, y);
183       break;
184
185     case O_BLADDER_SPENDER:
186       if (cave->bladder_spender_sound)
187         gd_sound_play(cave, GD_S_BLADDER_SPENDER, element, x, y);
188       break;
189
190     case O_PRE_CLOCK_1:
191       if (cave->bladder_convert_sound)
192         gd_sound_play(cave, GD_S_BLADDER_CONVERT, element, x, y);
193       break;
194
195     case O_SLIME:
196       if (cave->slime_sound)
197         gd_sound_play(cave, GD_S_SLIME, element, x, y);
198       break;
199
200     case O_LAVA:
201       if (cave->lava_sound)
202         gd_sound_play(cave, GD_S_LAVA, element, x, y);
203       break;
204
205     case O_ACID:
206       if (cave->acid_spread_sound)
207         gd_sound_play(cave, GD_S_ACID_SPREAD, element, x, y);
208       break;
209
210     case O_BLADDER:
211       if (cave->bladder_sound)
212         gd_sound_play(cave, GD_S_BLADDER_MOVE, element, x, y);
213       break;
214
215     case O_BITER_1:
216     case O_BITER_2:
217     case O_BITER_3:
218     case O_BITER_4:
219       if (cave->biter_sound)
220         gd_sound_play(cave, GD_S_BITER_EAT, element, x, y);
221       break;
222
223     case O_DIRT_BALL:
224     case O_DIRT_BALL_F:
225     case O_DIRT_LOOSE:
226     case O_DIRT_LOOSE_F:
227       gd_sound_play(cave, GD_S_DIRT_BALL, element, x, y);
228       break;
229
230     default:
231       /* do nothing. */
232       break;
233   }
234 }
235
236 static inline GdElement *getp(const GdCave *cave, const int x, const int y)
237 {
238   return cave->getp(cave, x, y);
239 }
240
241 /*
242   perfect (non-lineshifting) GET function.
243   returns a pointer to a selected cave element by its coordinates.
244 */
245 static inline GdElement *getp_perfect(const GdCave *cave, const int x, const int y)
246 {
247   /* (x + n) mod n: this works also for x >= n and -n + 1 < x < 0 */
248   return &(cave->map[(y + cave->h) % cave->h][(x + cave->w) % cave->w]);
249 }
250
251 /*
252   line shifting GET function; returns a pointer to the selected cave element.
253   this is used to emulate the line-shifting behaviour of original games, so that
254   the player entering one side will appear one row above or below on the other.
255 */
256 static inline GdElement *getp_shift(const GdCave *cave, int x, int y)
257 {
258   if (x >= cave->w)
259   {
260     y++;
261     x -= cave->w;
262   }
263   else if (x < 0)
264   {
265     y--;
266     x += cave->w;
267   }
268
269   y = (y + cave->h) % cave->h;
270
271   return &(cave->map[y][x]);
272 }
273
274 static inline GdElement get(const GdCave *cave, const int x, const int y)
275 {
276   return *getp(cave, x, y);
277 }
278
279 /* returns an element which is somewhere near x,y */
280 static inline GdElement get_dir(const GdCave *cave, const int x, const int y,
281                                 const GdDirection dir)
282 {
283   return get(cave, x + gd_dx[dir], y + gd_dy[dir]);
284 }
285
286 static inline boolean explodes_by_hit_dir(const GdCave *cave, const int x,
287                                           const int y, GdDirection dir)
288 {
289   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_EXPLODES_BY_HIT) != 0;
290 }
291
292 /* returns true if the element is not explodable, for example the steel wall */
293 static inline boolean non_explodable(const GdCave *cave, const int x, const int y)
294 {
295   return (gd_elements[get(cave, x,y) & O_MASK].properties & P_NON_EXPLODABLE) != 0;
296 }
297
298 /* returns true if the element can be eaten by the amoeba, eg. space and dirt. */
299 static inline boolean amoeba_eats_dir(const GdCave *cave, const int x, const int y,
300                                       const GdDirection dir)
301 {
302   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_AMOEBA_CONSUMES) != 0;
303 }
304
305 /* returns true if the element is sloped, so stones and diamonds roll down on it.
306    for example a stone or brick wall */
307 static inline boolean sloped_dir(const GdCave *cave, const int x, const int y,
308                                  const GdDirection dir, const GdDirection slop)
309 {
310   switch (slop)
311   {
312     case GD_MV_LEFT:
313       return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_LEFT) != 0;
314
315     case GD_MV_RIGHT:
316       return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_RIGHT) != 0;
317
318     case GD_MV_UP:
319       return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_UP) != 0;
320
321     case GD_MV_DOWN:
322       return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_SLOPED_DOWN) != 0;
323
324     default:
325       break;
326   }
327
328   return FALSE;
329 }
330
331 /* returns true if the element is sloped for bladder movement
332    (brick = yes, diamond = no, for example) */
333 static inline boolean sloped_for_bladder_dir (const GdCave *cave, const int x, const int y,
334                                               const GdDirection dir)
335 {
336   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLADDER_SLOPED) != 0;
337 }
338
339 static inline boolean blows_up_flies_dir(const GdCave *cave, const int x, const int y,
340                                          const GdDirection dir)
341 {
342     return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_BLOWS_UP_FLIES) != 0;
343 }
344
345 /* returns true if the element is a counter-clockwise creature */
346 static inline boolean rotates_ccw (const GdCave *cave, const int x, const int y)
347 {
348   return (gd_elements[get(cave, x, y) & O_MASK].properties & P_CCW) != 0;
349 }
350
351 /* returns true if the element is a player */
352 static inline boolean is_player(const GdCave *cave, const int x, const int y)
353 {
354   return (gd_elements[get(cave, x, y) & O_MASK].properties & P_PLAYER) != 0;
355 }
356
357 /* returns true if the element is a player */
358 static inline boolean is_player_dir(const GdCave *cave, const int x, const int y,
359                                     const GdDirection dir)
360 {
361   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_PLAYER) != 0;
362 }
363
364 static inline boolean can_be_hammered_dir(const GdCave *cave, const int x, const int y,
365                                           const GdDirection dir)
366 {
367   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_CAN_BE_HAMMERED) != 0;
368 }
369
370 /* returns true if the element is explodable and explodes to space, for example the player */
371 static inline boolean is_first_stage_of_explosion(const GdCave *cave, const int x, const int y)
372 {
373   return (gd_elements[get(cave, x, y) & O_MASK].properties & P_EXPLOSION_FIRST_STAGE) != 0;
374 }
375
376 /* returns true if the element is moved by the conveyor belt */
377 static inline boolean moved_by_conveyor_top_dir(const GdCave *cave, const int x, const int y,
378                                                 const GdDirection dir)
379 {
380   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_TOP) != 0;
381 }
382
383 /* returns true if the element is moved by the conveyor belt */
384 static inline boolean moved_by_conveyor_bottom_dir(const GdCave *cave, const int x, const int y,
385                                                    const GdDirection dir)
386 {
387   return (gd_elements[get_dir(cave, x, y, dir) & O_MASK].properties & P_MOVED_BY_CONVEYOR_BOTTOM) != 0;
388 }
389
390 static inline boolean is_scanned_dir(const GdCave *cave, const int x, const int y,
391                                      const GdDirection dir)
392 {
393   return (get_dir(cave, x, y, dir) & SCANNED) != 0;
394 }
395
396 /* returns true if neighbouring element is "e" */
397 /* treats dirt specially */
398 /* treats lava specially */
399 static inline boolean is_element_dir(const GdCave *cave, const int x, const int y,
400                                      const GdDirection dir, GdElement e)
401 {
402   GdElement examined = get_dir(cave, x, y, dir);
403
404   /* if it is a dirt-like, change to dirt, so equality will evaluate to true */
405   if (gd_elements[examined & O_MASK].properties & P_DIRT)
406     examined = O_DIRT;
407
408   if (gd_elements[e & O_MASK].properties & P_DIRT)
409     e = O_DIRT;
410
411   /* if the element on the map is a lava, it should be like space */
412   if (examined == O_LAVA)
413     examined = O_SPACE;
414
415   return (e == examined);
416 }
417
418 /* returns true if neighbouring element is space */
419 static inline boolean is_space_dir(const GdCave *cave, const int x, const int y,
420                                    const GdDirection dir)
421 {
422   GdElement e = get_dir(cave, x, y, dir)&O_MASK;
423
424   return (e == O_SPACE || e == O_LAVA);
425 }
426
427 /* store an element at the given position */
428 static inline void store(GdCave *cave, const int x, const int y, const GdElement element)
429 {
430   GdElement *e = getp(cave, x, y);
431
432   if (*e == O_LAVA)
433   {
434     play_sound_of_element(cave, O_LAVA, x, y);
435
436     return;
437   }
438
439   *e = element;
440 }
441
442 /* store an element with SCANNED flag turned on */
443 static inline void store_sc(GdCave *cave, const int x, const int y, const GdElement element)
444 {
445   store(cave, x, y, element | SCANNED);
446 }
447
448 /* store an element to a neighbouring cell */
449 static inline void store_dir(GdCave *cave, const int x, const int y,
450                              const GdDirection dir, const GdElement element)
451 {
452   store(cave, x + gd_dx[dir], y + gd_dy[dir], element|SCANNED);
453 }
454
455 /* store an element to a neighbouring cell */
456 static inline void store_dir_no_scanned(GdCave *cave, const int x, const int y,
457                                         const GdDirection dir, const GdElement element)
458 {
459   store(cave, x + gd_dx[dir], y + gd_dy[dir], element);
460 }
461
462 /* move element to direction; then place space at x, y */
463 static inline void move(GdCave *cave, const int x, const int y,
464                         const GdDirection dir, const GdElement e)
465 {
466   store_dir(cave, x, y, dir, e);
467   store(cave, x, y, O_SPACE);
468 }
469
470 /* increment a cave element; can be used for elements which are one after
471    the other, for example bladder1, bladder2, bladder3... */
472 static inline void next(GdCave *cave, const int x, const int y)
473 {
474   (*getp(cave, x, y))++;
475 }
476
477 static void cell_explode(GdCave *cave, int x, int y, GdElement explode_to)
478 {
479   if (non_explodable (cave, x, y))
480     return;
481
482   if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
483     cave->voodoo_touched = TRUE;
484
485   if (get(cave, x, y) == O_VOODOO && !cave->voodoo_disappear_in_explosion)
486     /* voodoo turns into a time penalty */
487     store_sc(cave, x, y, O_TIME_PENALTY);
488   else if (get(cave, x, y) == O_NITRO_PACK ||
489            get(cave, x, y) == O_NITRO_PACK_F)
490     /* nitro pack inside an explosion - it is now triggered */
491     store_sc(cave, x, y, O_NITRO_PACK_EXPLODE);
492   else
493     /* for everything else */
494     store_sc(cave, x, y, explode_to);
495 }
496
497 /* a creature explodes to a 3x3 something. */
498 static void creature_explode(GdCave *cave, int x, int y, GdElement explode_to)
499 {
500   int xx, yy;
501
502   /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
503   cave->ckdelay += 1200;
504   gd_sound_play(cave, GD_S_EXPLOSION, get(cave, x, y), x, y);
505
506   for (yy = y - 1; yy <= y + 1; yy++)
507     for (xx = x - 1; xx <= x + 1; xx++)
508       cell_explode(cave, xx, yy, explode_to);
509 }
510
511 static void nitro_explode(GdCave *cave, int x, int y)
512 {
513   int xx, yy;
514
515   /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
516   cave->ckdelay += 1200;
517   gd_sound_play(cave, GD_S_NITRO_EXPLOSION, get(cave, x, y), x, y);
518
519   for (yy = y - 1; yy <= y + 1; yy++)
520     for (xx = x - 1; xx <= x + 1; xx++)
521       cell_explode(cave, xx, yy, O_NITRO_EXPL_1);
522
523   /* the current cell is explicitly changed into a nitro expl,
524      as cell_explode changes it to a triggered nitro pack */
525   store_sc(cave, x, y, O_NITRO_EXPL_1);
526 }
527
528 /* a voodoo explodes, leaving a 3x3 steel and a time penalty behind. */
529 static void voodoo_explode(GdCave *cave, int x, int y)
530 {
531   int xx, yy;
532
533   /* the processing of an explosion took pretty much time: processing 3x3 = 9 elements */
534   cave->ckdelay += 1000;
535
536   gd_sound_play(cave, GD_S_VOODOO_EXPLOSION, get(cave, x, y), x, y);
537   if (cave->voodoo_any_hurt_kills_player)
538     cave->voodoo_touched = TRUE;
539
540   /* voodoo explodes to 3x3 steel */
541   for (yy = y - 1; yy <= y + 1; yy++)
542     for (xx = x - 1; xx <= x + 1; xx++)
543       store_sc(cave, xx, yy, O_PRE_STEEL_1);
544
545   /* middle is a time penalty (which will be turned into a gravestone) */
546   store_sc(cave, x, y, O_TIME_PENALTY);
547 }
548
549 /* a bomb does not explode the voodoo, neither does the ghost.
550    this function check this, and stores the new element or not.
551    destroying the voodoo is also controlled by the
552    voodoo_disappear_in_explosion flag. */
553 static void explode_try_skip_voodoo(GdCave *cave, const int x, const int y, const GdElement expl)
554 {
555   if (non_explodable (cave, x, y))
556     return;
557
558   /* bomb does not explode voodoo */
559   if (!cave->voodoo_disappear_in_explosion && get(cave, x, y) == O_VOODOO)
560     return;
561
562   if (cave->voodoo_any_hurt_kills_player && get(cave, x, y) == O_VOODOO)
563     cave->voodoo_touched = TRUE;
564
565   store_sc (cave, x, y, expl);
566 }
567
568 /* X shaped ghost explosion; does not touch voodoo! */
569 static void ghost_explode(GdCave *cave, const int x, const int y)
570 {
571   gd_sound_play(cave, GD_S_GHOST_EXPLOSION, get(cave, x, y), x, y);
572
573   /* the processing of an explosion took pretty much time: processing 5 elements */
574   cave->ckdelay += 650;
575
576   explode_try_skip_voodoo(cave, x,     y,     O_GHOST_EXPL_1);
577   explode_try_skip_voodoo(cave, x - 1, y - 1, O_GHOST_EXPL_1);
578   explode_try_skip_voodoo(cave, x + 1, y + 1, O_GHOST_EXPL_1);
579   explode_try_skip_voodoo(cave, x - 1, y + 1, O_GHOST_EXPL_1);
580   explode_try_skip_voodoo(cave, x + 1, y - 1, O_GHOST_EXPL_1);
581 }
582
583 /* +shaped bomb explosion; does not touch voodoo! */
584 static void bomb_explode(GdCave *cave, const int x, const int y)
585 {
586   gd_sound_play(cave, GD_S_BOMB_EXPLOSION, get(cave, x, y), x, y);
587
588   /* the processing of an explosion took pretty much time: processing 5 elements */
589   cave->ckdelay += 650;
590
591   explode_try_skip_voodoo(cave, x,     y,     O_BOMB_EXPL_1);
592   explode_try_skip_voodoo(cave, x - 1, y,     O_BOMB_EXPL_1);
593   explode_try_skip_voodoo(cave, x + 1, y,     O_BOMB_EXPL_1);
594   explode_try_skip_voodoo(cave, x,     y + 1, O_BOMB_EXPL_1);
595   explode_try_skip_voodoo(cave, x,     y - 1, O_BOMB_EXPL_1);
596 }
597
598 /*
599   explode an element with the appropriate type of exlposion.
600  */
601 static void explode(GdCave *cave, int x, int y)
602 {
603   GdElement e = get(cave, x, y) & O_MASK;
604
605   switch (e)
606   {
607     case O_GHOST:
608       ghost_explode(cave, x, y);
609       break;
610
611     case O_BOMB_TICK_7:
612       bomb_explode(cave, x, y);
613       break;
614
615     case O_VOODOO:
616       voodoo_explode(cave, x, y);
617       break;
618
619     case O_NITRO_PACK:
620     case O_NITRO_PACK_F:
621     case O_NITRO_PACK_EXPLODE:
622       nitro_explode(cave, x, y);
623       break;
624
625     case O_AMOEBA_2:
626       creature_explode(cave, x, y, O_AMOEBA_2_EXPL_1);
627       break;
628
629     case O_FALLING_WALL_F:
630       creature_explode(cave, x, y, O_EXPLODE_1);
631       break;
632
633     case O_BUTTER_1:
634     case O_BUTTER_2:
635     case O_BUTTER_3:
636     case O_BUTTER_4:
637       creature_explode(cave, x, y, cave->butterfly_explode_to);
638       break;
639
640     case O_ALT_BUTTER_1:
641     case O_ALT_BUTTER_2:
642     case O_ALT_BUTTER_3:
643     case O_ALT_BUTTER_4:
644       creature_explode(cave, x, y, cave->alt_butterfly_explode_to);
645       break;
646
647     case O_FIREFLY_1:
648     case O_FIREFLY_2:
649     case O_FIREFLY_3:
650     case O_FIREFLY_4:
651       creature_explode(cave, x, y, cave->firefly_explode_to);
652       break;
653
654     case O_ALT_FIREFLY_1:
655     case O_ALT_FIREFLY_2:
656     case O_ALT_FIREFLY_3:
657     case O_ALT_FIREFLY_4:
658       creature_explode(cave, x, y, cave->alt_firefly_explode_to);
659       break;
660
661     case O_PLAYER:
662     case O_PLAYER_BOMB:
663     case O_PLAYER_GLUED:
664     case O_PLAYER_STIRRING:
665     case O_PLAYER_PNEUMATIC_LEFT:
666     case O_PLAYER_PNEUMATIC_RIGHT:
667       creature_explode(cave, x, y, O_EXPLODE_1);
668       break;
669
670     case O_STONEFLY_1:
671     case O_STONEFLY_2:
672     case O_STONEFLY_3:
673     case O_STONEFLY_4:
674       creature_explode(cave, x, y, cave->stonefly_explode_to);
675       break;
676
677     case O_DRAGONFLY_1:
678     case O_DRAGONFLY_2:
679     case O_DRAGONFLY_3:
680     case O_DRAGONFLY_4:
681       creature_explode(cave, x, y, cave->dragonfly_explode_to);
682       break;
683
684     default:
685       break;
686   }
687 }
688
689 static void inline
690 explode_dir(GdCave *cave, const int x, const int y, GdDirection dir)
691 {
692   explode(cave, x + gd_dx[dir], y + gd_dy[dir]);
693 }
694
695 /*
696   player eats specified object.
697   returns O_SPACE if he eats it (diamond, dirt, space, outbox)
698   returns other element if something other appears there and he can't move.
699   cave pointer is needed to know the diamond values.
700 */
701 static GdElement player_get_element (GdCave* cave, const GdElement object, int x, int y)
702 {
703   int i;
704
705   switch (object)
706   {
707     case O_DIAMOND_KEY:
708       cave->diamond_key_collected = TRUE;
709       gd_sound_play(cave, GD_S_DIAMOND_KEY_COLLECT, object, x, y);
710       return O_SPACE;
711
712     /* KEYS AND DOORS */
713     case O_KEY_1:
714       gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
715       cave->key1++;
716       return O_SPACE;
717
718     case O_KEY_2:
719       gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
720       cave->key2++;
721       return O_SPACE;
722
723     case O_KEY_3:
724       gd_sound_play(cave, GD_S_KEY_COLLECT, object, x, y);
725       cave->key3++;
726       return O_SPACE;
727
728     case O_DOOR_1:
729       if (cave->key1 == 0)
730         return object;
731       gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
732       cave->key1--;
733       return O_SPACE;
734
735     case O_DOOR_2:
736       if (cave->key2 == 0)
737         return object;
738       gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
739       cave->key2--;
740       return O_SPACE;
741
742     case O_DOOR_3:
743       if (cave->key3 == 0)
744         return object;
745       gd_sound_play(cave, GD_S_DOOR_OPEN, object, x, y);
746       cave->key3--;
747       return O_SPACE;
748
749     /* SWITCHES */
750     case O_CREATURE_SWITCH:        /* creatures change direction. */
751       gd_sound_play(cave, GD_S_SWITCH_CREATURES, object, x, y);
752       cave->creatures_backwards = !cave->creatures_backwards;
753       return object;
754
755     case O_EXPANDING_WALL_SWITCH:        /* expanding wall change direction. */
756       gd_sound_play(cave, GD_S_SWITCH_EXPANDING, object, x, y);
757       cave->expanding_wall_changed = !cave->expanding_wall_changed;
758       return object;
759
760     case O_BITER_SWITCH:        /* biter change delay */
761       gd_sound_play(cave, GD_S_SWITCH_BITER, object, x, y);
762       cave->biter_delay_frame++;
763       if (cave->biter_delay_frame == 4)
764         cave->biter_delay_frame = 0;
765       return object;
766
767     case O_REPLICATOR_SWITCH:    /* replicator on/off switch */
768       gd_sound_play(cave, GD_S_SWITCH_REPLICATOR, object, x, y);
769       cave->replicators_active = !cave->replicators_active;
770       return object;
771
772     case O_CONVEYOR_SWITCH:    /* conveyor belts on/off */
773       gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
774       cave->conveyor_belts_active = !cave->conveyor_belts_active;
775       return object;
776
777     case O_CONVEYOR_DIR_SWITCH: /* conveyor belts switch direction */
778       gd_sound_play(cave, GD_S_SWITCH_CONVEYOR, object, x, y);
779       cave->conveyor_belts_direction_changed = !cave->conveyor_belts_direction_changed;
780       return object;
781
782     /* USUAL STUFF */
783     case O_DIRT:
784     case O_DIRT2:
785     case O_STEEL_EATABLE:
786     case O_BRICK_EATABLE:
787     case O_DIRT_SLOPED_UP_RIGHT:
788     case O_DIRT_SLOPED_UP_LEFT:
789     case O_DIRT_SLOPED_DOWN_LEFT:
790     case O_DIRT_SLOPED_DOWN_RIGHT:
791     case O_DIRT_BALL:
792     case O_DIRT_LOOSE:
793       gd_sound_play(cave, GD_S_WALK_EARTH, object, x, y);
794       return O_SPACE;
795
796     case O_SWEET:
797       gd_sound_play(cave, GD_S_SWEET_COLLECT, object, x, y);
798       cave->sweet_eaten = TRUE;
799       return O_SPACE;
800
801     case O_PNEUMATIC_HAMMER:
802       gd_sound_play(cave, GD_S_PNEUMATIC_COLLECT, object, x, y);
803       cave->got_pneumatic_hammer = TRUE;
804       return O_SPACE;
805
806     case O_CLOCK:
807       /* bonus time */
808       gd_sound_play(cave, GD_S_CLOCK_COLLECT, object, x, y);
809       cave->time += cave->time_bonus * cave->timing_factor;
810       if (cave->time > cave->max_time * cave->timing_factor)
811         cave->time -= cave->max_time * cave->timing_factor;
812       /* no space, rather a dirt remains there... */
813       return O_DIRT;
814
815     case O_DIAMOND:
816     case O_FLYING_DIAMOND:
817       // prevent diamond sounds for O_SKELETON (see below)
818       if (x != -1 && y != -1)
819         gd_sound_play(cave, GD_S_DIAMOND_COLLECT, object, x, y);
820
821       cave->score += cave->diamond_value;
822       cave->diamonds_collected++;
823
824       if (cave->diamonds_needed == cave->diamonds_collected)
825       {
826         cave->gate_open = TRUE;
827
828         /* extra is worth more points. */
829         cave->diamond_value = cave->extra_diamond_value;
830
831         cave->gate_open_flash = 1;
832         cave->sound3 = GD_S_CRACK;
833         gd_sound_play(cave, GD_S_CRACK, O_OUTBOX, x, y);
834       }
835       return O_SPACE;
836
837     case O_SKELETON:
838       cave->skeletons_collected++;
839
840       /* as if player got a diamond */
841       for (i = 0; i < cave->skeletons_worth_diamonds; i++)
842         player_get_element(cave, O_DIAMOND, -1, -1);
843
844       /* _after_ calling get_element for the fake diamonds, so we overwrite its sounds */
845       gd_sound_play(cave, GD_S_SKELETON_COLLECT, object, x, y);
846       return O_SPACE;
847
848     case O_OUTBOX:
849     case O_INVIS_OUTBOX:
850       cave->player_state = GD_PL_EXITED;    /* player now exits the cave! */
851       return O_SPACE;
852
853     case O_SPACE:
854     case O_LAVA:    /* player goes into lava, as if it was space */
855       gd_sound_play(cave, GD_S_WALK_EMPTY, object, x, y);
856       return O_SPACE;
857
858     default:
859       /* the object will remain there. */
860       return object;
861   }
862 }
863
864 /*
865   process a crazy dream-style teleporter.
866   called from gd_cave_iterate, for a player or a player_bomb.
867   player is standing at px, py, and trying to move in the direction player_move,
868   where there is a teleporter.
869   we check the whole cave, from px+1,py, till we get back to px,py (by wrapping
870   around). the first teleporter we find, and which is suitable, will be the destination.
871   return TRUE if teleporter worked, FALSE if cound not find any suitable teleporter.
872  */
873 static boolean do_teleporter(GdCave *cave, int px, int py, GdDirection player_move)
874 {
875   int tx, ty;
876
877   tx = px;
878   ty = py;
879
880   do
881   {
882     /* jump to next element; wrap around columns and rows. */
883     tx++;
884
885     if (tx >= cave->w)
886     {
887       tx = 0;
888       ty++;
889
890       if (ty >= cave->h)
891         ty = 0;
892     }
893
894     /* if we found a teleporter... */
895     if (get(cave, tx, ty) == O_TELEPORTER &&
896         is_space_dir(cave, tx, ty, player_move))
897     {
898       /* new player appears near teleporter found */
899       store_dir(cave, tx, ty, player_move, get(cave, px, py));
900
901       /* current player disappears */
902       store(cave, px, py, O_SPACE);
903
904       gd_sound_play(cave, GD_S_TELEPORTER, O_TELEPORTER, tx, ty);
905
906       return TRUE;    /* return true as teleporter worked */
907     }
908   }
909   /* loop until we get back to original coordinates */
910   while (tx != px || ty != py);
911
912   /* return false as we did not find any usable teleporter */
913   return FALSE;
914 }
915
916 /*
917   try to push an element.
918   returns true if the push is possible; also does move the specified _element_.
919   up to the caller to move the _player_itself_.
920 */
921 static boolean do_push(GdCave *cave, int x, int y, GdDirection player_move, boolean player_fire)
922 {
923   boolean result;
924   GdElement what = get_dir(cave, x, y, player_move);
925
926   /* gravity for falling wall, bladder, ... */
927   GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
928
929   result = FALSE;
930
931   switch (what)
932   {
933     case O_WAITING_STONE:
934     case O_STONE:
935     case O_NITRO_PACK:
936     case O_CHASING_STONE:
937     case O_MEGA_STONE:
938     case O_FLYING_STONE:
939     case O_NUT:
940       /* pushing some kind of stone or nut */
941       /* directions possible: 90degrees cw or ccw to current gravity. */
942       /* only push if player dir is orthogonal to gravity,
943          ie. gravity down, pushing left & right possible */
944       if (player_move == ccw_fourth[cave->gravity] ||
945           player_move == cw_fourth[cave->gravity])
946       {
947         int prob;
948
949         prob = 0;
950
951         /* different probabilities for different elements. */
952         switch (what)
953         {
954           case O_WAITING_STONE:
955             /* waiting stones are light, can always push */
956             prob = 1000000;
957             break;
958
959           case O_CHASING_STONE:
960             /* chasing can be pushed if player is turbo */
961             if (cave->sweet_eaten)
962               prob = 1000000;
963             break;
964
965           case O_MEGA_STONE:
966             /* mega may(!) be pushed if player is turbo */
967             if (cave->mega_stones_pushable_with_sweet && cave->sweet_eaten)
968               prob = 1000000;
969             break;
970
971           case O_STONE:
972           case O_NUT:
973           case O_FLYING_STONE:
974           case O_NITRO_PACK:
975             if (cave->sweet_eaten)
976               prob = cave->pushing_stone_prob_sweet; /* probability with sweet */
977             else
978               prob = cave->pushing_stone_prob; /* probability without sweet. */
979             break;
980
981           default:
982             break;
983         }
984
985         if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move) &&
986             g_rand_int_range(cave->random, 0, 1000000) < prob)
987         {
988           /* if decided that he will be able to push, */
989           store_dir(cave, x, y, GD_MV_TWICE + player_move, what);
990           play_sound_of_element(cave, what, x, y);
991           result = TRUE;
992         }
993       }
994       break;
995
996     case O_BLADDER:
997     case O_BLADDER_1:
998     case O_BLADDER_2:
999     case O_BLADDER_3:
1000     case O_BLADDER_4:
1001     case O_BLADDER_5:
1002     case O_BLADDER_6:
1003     case O_BLADDER_7:
1004     case O_BLADDER_8:
1005       /* pushing a bladder. keep in mind that after pushing, we always get an O_BLADDER,
1006        * not an O_BLADDER_x. */
1007       /* there is no "delayed" state of a bladder, so we use store_dir_no_scanned! */
1008
1009       /* first check: we cannot push a bladder "up" */
1010       if (player_move != opposite[grav_compat])
1011       {
1012         /* pushing a bladder "down". p = player, o = bladder, 1, 2, 3 = directions to check. */
1013         /* player moving in the direction of gravity. */
1014         /*  p   p  g  */
1015         /* 2o3  |  |  */
1016         /*  1   v  v  */
1017         if (player_move == grav_compat)
1018         {
1019           /* pushing bladder down */
1020           if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))
1021             store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1022           /* if no space to push down, maybe left (down-left to player) */
1023           else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))
1024
1025             /* left is "down, turned right (cw)" */
1026             store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1027           /* if not, maybe right (down-right to player) */
1028           else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
1029             store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1030         }
1031
1032         /* pushing a bladder "left". p = player, o = bladder, 1, 2, 3 = directions to check. */
1033         /*  3        g */
1034         /* 1op  <-p  | */
1035         /*  2        v */
1036         else if (player_move == cw_fourth[grav_compat])
1037         {
1038           if (is_space_dir(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat]))    /* pushing it left */
1039             store_dir_no_scanned(cave, x, y, GD_MV_TWICE + cw_fourth[grav_compat], O_BLADDER), result = TRUE;
1040           else if (is_space_dir(cave, x, y, cw_eighth[grav_compat]))    /* maybe down, and player will move left */
1041             store_dir_no_scanned(cave, x, y, cw_eighth[grav_compat], O_BLADDER), result = TRUE;
1042           else if (is_space_dir(cave, x, y, cw_eighth[player_move]))    /* maybe up, and player will move left */
1043             store_dir_no_scanned(cave, x, y, cw_eighth[player_move], O_BLADDER), result = TRUE;
1044         }
1045
1046         /* pushing a bladder "right". p = player, o = bladder, 1, 2, 3 = directions to check. */
1047         /*  3        g */
1048         /* po1  p-<  | */
1049         /*  2        v */
1050         else if (player_move == ccw_fourth[grav_compat])
1051         {
1052           if (is_space_dir(cave, x, y, GD_MV_TWICE + player_move))    /* pushing it right */
1053             store_dir_no_scanned(cave, x, y, GD_MV_TWICE + player_move, O_BLADDER), result = TRUE;
1054           else if (is_space_dir(cave, x, y, ccw_eighth[grav_compat]))    /* maybe down, and player will move right */
1055             store_dir_no_scanned(cave, x, y, ccw_eighth[grav_compat], O_BLADDER), result = TRUE;
1056           else if (is_space_dir(cave, x, y, ccw_eighth[player_move]))    /* maybe up, and player will move right */
1057             store_dir_no_scanned(cave, x, y, ccw_eighth[player_move], O_BLADDER), result = TRUE;
1058         }
1059
1060         if (result)
1061           play_sound_of_element(cave, O_BLADDER, x, y);
1062       }
1063       break;
1064
1065     case O_BOX:
1066       /* a box is only pushed with the fire pressed */
1067       if (player_fire)
1068       {
1069         /* but always with 100% probability */
1070         switch (player_move)
1071         {
1072           case GD_MV_LEFT:
1073           case GD_MV_RIGHT:
1074           case GD_MV_UP:
1075           case GD_MV_DOWN:
1076             /* pushing in some dir, two steps in that dir - is there space? */
1077             if (is_space_dir(cave, x, y, player_move + GD_MV_TWICE))
1078             {
1079               /* yes, so push. */
1080               store_dir(cave, x, y, player_move + GD_MV_TWICE, O_BOX);
1081               result = TRUE;
1082               gd_sound_play(cave, GD_S_BOX_PUSH, what, x, y);
1083             }
1084             break;
1085
1086           default:
1087             /* push in no other directions possible */
1088             break;
1089         }
1090       }
1091       break;
1092
1093       /* pushing of other elements not possible */
1094     default:
1095       break;
1096   }
1097
1098   return result;
1099 }
1100
1101 /* from the key press booleans, create a direction */
1102 GdDirection gd_direction_from_keypress(boolean up, boolean down, boolean left, boolean right)
1103 {
1104   GdDirection player_move;
1105
1106   /* from the key press booleans, create a direction */
1107   if (up && right)
1108     player_move = GD_MV_UP_RIGHT;
1109   else if (down && right)
1110     player_move = GD_MV_DOWN_RIGHT;
1111   else if (down && left)
1112     player_move = GD_MV_DOWN_LEFT;
1113   else if (up && left)
1114     player_move = GD_MV_UP_LEFT;
1115   else if (up)
1116     player_move = GD_MV_UP;
1117   else if (down)
1118     player_move = GD_MV_DOWN;
1119   else if (left)
1120     player_move = GD_MV_LEFT;
1121   else if (right)
1122     player_move = GD_MV_RIGHT;
1123   else
1124     player_move = GD_MV_STILL;
1125
1126   return player_move;
1127 }
1128
1129 /* clear these to no sound; and they will be set during iteration. */
1130 void gd_cave_clear_sounds(GdCave *cave)
1131 {
1132   cave->sound1 = GD_S_NONE;
1133   cave->sound2 = GD_S_NONE;
1134   cave->sound3 = GD_S_NONE;
1135 }
1136
1137 static void do_start_fall(GdCave *cave, int x, int y, GdDirection falling_direction,
1138                           GdElement falling_element)
1139 {
1140   if (cave->gravity_disabled)
1141     return;
1142
1143   if (is_space_dir(cave, x, y, falling_direction))
1144   {
1145     /* beginning to fall */
1146     play_sound_of_element(cave, get(cave, x, y), x, y);
1147     move(cave, x, y, falling_direction, falling_element);
1148   }
1149
1150   /* check if it is on a sloped element, and it can roll. */
1151   /* for example, sloped wall looks like: */
1152   /*  /| */
1153   /* /_| */
1154   /* this is tagged as sloped up&left. */
1155   /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1156   /* then check the direction to roll (left or right) */
1157   /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1158   else if (sloped_dir(cave, x, y, falling_direction, opposite[falling_direction]))
1159   {
1160     /* rolling down, if sitting on a sloped object  */
1161     if (sloped_dir(cave, x, y, falling_direction, cw_fourth[falling_direction]) &&
1162         is_space_dir(cave, x, y, cw_fourth[falling_direction]) &&
1163         is_space_dir(cave, x, y, cw_eighth[falling_direction]))
1164     {
1165       /* rolling left? - keep in mind that ccw_fourth rotates gravity ccw,
1166          so here we use cw_fourth */
1167       play_sound_of_element(cave, get(cave, x, y), x, y);
1168       move(cave, x, y, cw_fourth[falling_direction], falling_element);
1169     }
1170     else if (sloped_dir(cave, x, y, falling_direction, ccw_fourth[falling_direction]) &&
1171              is_space_dir(cave, x, y, ccw_fourth[falling_direction]) &&
1172              is_space_dir(cave, x, y, ccw_eighth[falling_direction]))
1173     {
1174       /* rolling right? */
1175       play_sound_of_element(cave, get(cave, x, y), x, y);
1176       move(cave, x, y, ccw_fourth[falling_direction], falling_element);
1177     }
1178   }
1179 }
1180
1181 static boolean do_fall_try_crush_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1182 {
1183   if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1184       cave->voodoo_dies_by_stone)
1185   {
1186     /* this is a 1stB-style vodo. explodes by stone, collects diamonds */
1187     explode_dir(cave, x, y, fall_dir);
1188     return TRUE;
1189   }
1190   else
1191     return FALSE;
1192 }
1193
1194 static boolean do_fall_try_eat_voodoo(GdCave *cave, int x, int y, GdDirection fall_dir)
1195 {
1196   if (get_dir(cave, x, y, fall_dir) == O_VOODOO &&
1197       cave->voodoo_collects_diamonds)
1198   {
1199     /* this is a 1stB-style voodoo. explodes by stone, collects diamonds */
1200     player_get_element(cave, O_DIAMOND, x, y);   /* as if player got diamond */
1201     store(cave, x, y, O_SPACE);    /* diamond disappears */
1202     return TRUE;
1203   }
1204   else
1205     return FALSE;
1206 }
1207
1208 static boolean do_fall_try_crack_nut(GdCave *cave, int x, int y,
1209                                      GdDirection fall_dir, GdElement bouncing)
1210 {
1211   if (get_dir(cave, x, y, fall_dir) == O_NUT ||
1212       get_dir(cave, x, y, fall_dir) == O_NUT_F)
1213   {
1214     /* stones */
1215     store(cave, x, y, bouncing);
1216     store_dir(cave, x, y, fall_dir, cave->nut_turns_to_when_crushed);
1217
1218     if (cave->nut_sound)
1219       gd_sound_play(cave, GD_S_NUT_CRACK, O_NUT, x, y);
1220
1221     return TRUE;
1222   }
1223   else
1224     return FALSE;
1225 }
1226
1227 static boolean do_fall_try_magic(GdCave *cave, int x, int y,
1228                                  GdDirection fall_dir, GdElement magic)
1229 {
1230   if (get_dir(cave, x, y, fall_dir) == O_MAGIC_WALL)
1231   {
1232     play_sound_of_element(cave, O_DIAMOND, x, y);    /* always play diamond sound */
1233
1234     if (cave->magic_wall_state==GD_MW_DORMANT)
1235       cave->magic_wall_state = GD_MW_ACTIVE;
1236
1237     if (cave->magic_wall_state==GD_MW_ACTIVE &&
1238         is_space_dir(cave, x, y, GD_MV_TWICE+fall_dir))
1239     {
1240       /* if magic wall active and place underneath, it turns element
1241          into anything the effect says to do. */
1242       store_dir(cave, x, y, GD_MV_TWICE+fall_dir, magic);
1243     }
1244
1245     /* active or non-active or anything, element falling in will always disappear */
1246     store(cave, x, y, O_SPACE);
1247
1248     return TRUE;
1249   }
1250   else
1251     return FALSE;
1252 }
1253
1254 static boolean do_fall_try_crush(GdCave *cave, int x, int y, GdDirection fall_dir)
1255 {
1256   if (explodes_by_hit_dir(cave, x, y, fall_dir))
1257   {
1258     explode_dir(cave, x, y, fall_dir);
1259     return TRUE;
1260   }
1261   else
1262     return FALSE;
1263 }
1264
1265 static boolean do_fall_roll_or_stop(GdCave *cave, int x, int y,
1266                                     GdDirection fall_dir, GdElement bouncing)
1267 {
1268   if (is_space_dir(cave, x, y, fall_dir))
1269   {
1270     /* falling further */
1271     move(cave, x, y, fall_dir, get(cave, x, y));
1272
1273     return TRUE;
1274   }
1275
1276   /* check if it is on a sloped element, and it can roll. */
1277   /* for example, sloped wall looks like: */
1278   /*  /| */
1279   /* /_| */
1280   /* this is tagged as sloped up&left. */
1281   /* first check if the stone or diamond is coming from "up" (ie. opposite of gravity) */
1282   /* then check the direction to roll (left or right) */
1283   /* this way, gravity can also be pointing right, and the above slope will work as one would expect */
1284
1285   if (sloped_dir(cave, x, y, fall_dir, opposite[fall_dir]))
1286   {
1287     /* sloped element, falling to left or right */
1288     if (sloped_dir(cave, x, y, fall_dir, cw_fourth[fall_dir]) &&
1289         is_space_dir(cave, x, y, cw_eighth[fall_dir]) &&
1290         is_space_dir(cave, x, y, cw_fourth[fall_dir]))
1291     {
1292       play_sound_of_element(cave, get(cave, x, y), x, y);
1293
1294       /* try to roll left first - see O_STONE to understand why cw_fourth */
1295       move(cave, x, y, cw_fourth[fall_dir], get(cave, x, y));
1296     }
1297     else if (sloped_dir(cave, x, y, fall_dir, ccw_fourth[fall_dir]) &&
1298              is_space_dir(cave, x, y, ccw_eighth[fall_dir]) &&
1299              is_space_dir(cave, x, y, ccw_fourth[fall_dir]))
1300     {
1301       play_sound_of_element(cave, get(cave, x, y), x, y);
1302
1303       /* if not, try to roll right */
1304       move(cave, x, y, ccw_fourth[fall_dir], get(cave, x, y));
1305     }
1306     else
1307     {
1308       /* cannot roll in any direction, so it stops */
1309       play_sound_of_element(cave, get(cave, x, y), x, y);
1310       store(cave, x, y, bouncing);
1311     }
1312
1313     return TRUE;
1314   }
1315
1316   /* any other element, stops */
1317   play_sound_of_element(cave, get(cave, x, y), x, y);
1318   store(cave, x, y, bouncing);
1319   return TRUE;
1320 }
1321
1322 static void update_cave_speed(GdCave *cave)
1323 {
1324   /* update timing calculated by iterating and counting elements which were slow to process on c64 */
1325   switch (cave->scheduling)
1326   {
1327     case GD_SCHEDULING_MILLISECONDS:
1328       /* cave->speed already contains the milliseconds value, do not touch it */
1329       break;
1330
1331     case GD_SCHEDULING_BD1:
1332       if (!cave->intermission)
1333         /* non-intermissions */
1334         cave->speed = (88 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1335       else
1336         /* intermissions were quicker, as only lines 1-12 were processed by the engine. */
1337         cave->speed = (60 + 3.66 * cave->c64_timing + (cave->ckdelay + cave->ckdelay_extra_for_animation) / 1000);
1338       break;
1339
1340     case GD_SCHEDULING_BD1_ATARI:
1341       /* about 20ms/frame faster than c64 version */
1342       if (!cave->intermission)
1343         cave->speed = (74 + 3.2 * cave->c64_timing + (cave->ckdelay) / 1000);            /* non-intermissions */
1344       else
1345         cave->speed = (65 + 2.88 * cave->c64_timing + (cave->ckdelay) / 1000);        /* for intermissions */
1346       break;
1347
1348     case GD_SCHEDULING_BD2:
1349       /* 60 is a guess. */
1350       cave->speed = MAX(60 + (cave->ckdelay + cave->ckdelay_extra_for_animation)/1000, cave->c64_timing * 20);
1351       break;
1352
1353     case GD_SCHEDULING_PLCK:
1354       /* 65 is totally empty cave in construction kit, with delay = 0) */
1355       cave->speed = MAX(65 + cave->ckdelay / 1000, cave->c64_timing * 20);
1356       break;
1357
1358     case GD_SCHEDULING_BD2_PLCK_ATARI:
1359       /* a really fast engine; timing works like c64 plck. */
1360       /* 40 ms was measured in the construction kit, with delay = 0 */
1361       cave->speed = MAX(40 + cave->ckdelay / 1000, cave->c64_timing * 20);
1362       break;
1363
1364     case GD_SCHEDULING_CRDR:
1365       if (cave->hammered_walls_reappear) /* this made the engine very slow. */
1366         cave->ckdelay += 60000;
1367       cave->speed = MAX(130 + cave->ckdelay / 1000, cave->c64_timing * 20);
1368       break;
1369
1370     case GD_SCHEDULING_MAX:
1371       break;
1372   }
1373 }
1374
1375 /* process a cave. */
1376 void gd_cave_iterate(GdCave *cave, GdDirection player_move, boolean player_fire, boolean suicide)
1377 {
1378   int x, y, i;
1379
1380   /* for border scan */
1381   int ymin, ymax;
1382
1383   /* amoeba found to be enclosed. if not, this is cleared */
1384   boolean amoeba_found_enclosed, amoeba_2_found_enclosed;
1385
1386   /* counting the number of amoebas. after scan, check if too much */
1387   int amoeba_count, amoeba_2_count;
1388
1389   /* cave scan found water - for sound */
1390   boolean found_water;
1391
1392   boolean inbox_toggle;
1393   boolean start_signal;
1394
1395   /* gravity for falling wall, bladder, ... */
1396   GdDirection grav_compat = cave->gravity_affects_all ? cave->gravity : GD_MV_DOWN;
1397
1398   /* directions for o_something_1, 2, 3 and 4 (creatures) */
1399   static const GdDirection creature_dir[] =
1400   {
1401     GD_MV_LEFT,
1402     GD_MV_UP,
1403     GD_MV_RIGHT,
1404     GD_MV_DOWN
1405   };
1406   static const GdDirection creature_chdir[] =
1407   {
1408     GD_MV_RIGHT,
1409     GD_MV_DOWN,
1410     GD_MV_LEFT,
1411     GD_MV_UP
1412   };
1413   int time_decrement_sec;
1414
1415   /* biters eating elements preference, they try to go in this order */
1416   GdElement biter_try[] =
1417   {
1418     O_DIRT,
1419     cave->biter_eat,
1420     O_SPACE, O_STONE
1421   };
1422
1423   boolean amoeba_sound, magic_sound;
1424
1425   gd_cave_clear_sounds(cave);
1426
1427   /* if diagonal movements not allowed, */
1428   /* horizontal movements have precedence. [BROADRIBB] */
1429   if (!cave->diagonal_movements)
1430   {
1431     switch (player_move)
1432     {
1433       case GD_MV_UP_RIGHT:
1434       case GD_MV_DOWN_RIGHT:
1435         player_move = GD_MV_RIGHT;
1436         break;
1437
1438       case GD_MV_UP_LEFT:
1439       case GD_MV_DOWN_LEFT:
1440         player_move = GD_MV_LEFT;
1441         break;
1442
1443       default:
1444         /* no correction needed */
1445         break;
1446     }
1447   }
1448
1449   /* set cave get function; to implement perfect or lineshifting borders */
1450   if (cave->lineshift)
1451     cave->getp = getp_shift;
1452   else
1453     cave->getp = getp_perfect;
1454
1455   /* increment this. if the scan routine comes across player, clears it (sets to zero). */
1456   if (cave->player_seen_ago < 100)
1457     cave->player_seen_ago++;
1458
1459   if (cave->pneumatic_hammer_active_delay > 0)
1460     cave->pneumatic_hammer_active_delay--;
1461
1462   /* inboxes and outboxes flash with the rhythm of the game, not the display.
1463    * also, a player can be born only from an open, not from a steel-wall-like inbox. */
1464   cave->inbox_flash_toggle = !cave->inbox_flash_toggle;
1465   inbox_toggle = cave->inbox_flash_toggle;
1466
1467   if (cave->gate_open_flash > 0)
1468     cave->gate_open_flash--;
1469
1470   /* score collected this frame */
1471   cave->score = 0;
1472
1473   /* suicide only kills the active player */
1474   /* player_x, player_y was set by the previous iterate routine, or the cave setup. */
1475   /* we must check if there is a player or not - he may have exploded or something like that */
1476   if (suicide && cave->player_state == GD_PL_LIVING &&
1477       is_player(cave, cave->player_x, cave->player_y))
1478     store(cave, cave->player_x, cave->player_y, O_EXPLODE_1);
1479
1480   /* check for walls reappearing */
1481   if (cave->hammered_reappear)
1482   {
1483     for (y = 0; y < cave->h; y++)
1484     {
1485       for (x = 0; x < cave->w; x++)
1486       {
1487         /* timer for the cell > 0? */
1488         if (cave->hammered_reappear[y][x]>0)
1489         {
1490           /* decrease timer */
1491           cave->hammered_reappear[y][x]--;
1492
1493           /* check if it became zero */
1494           if (cave->hammered_reappear[y][x] == 0)
1495           {
1496             store(cave, x, y, O_BRICK);
1497             gd_sound_play(cave, GD_S_WALL_REAPPEAR, O_BRICK, x, y);
1498           }
1499         }
1500       }
1501     }
1502   }
1503
1504   /* variables to check during the scan */
1505
1506   /* will be set to false if any of the amoeba is found free. */
1507   amoeba_found_enclosed = TRUE;
1508   amoeba_2_found_enclosed = TRUE;
1509   amoeba_count = 0;
1510   amoeba_2_count = 0;
1511   found_water = FALSE;
1512   cave->ckdelay = 0;
1513   time_decrement_sec = 0;
1514
1515   /* check whether to scan the first and last line */
1516   if (cave->border_scan_first_and_last)
1517   {
1518     ymin = 0;
1519     ymax = cave->h - 1;
1520   }
1521   else
1522   {
1523     ymin = 1;
1524     ymax = cave->h - 2;
1525   }
1526
1527   /* the cave scan routine */
1528   for (y = ymin; y <= ymax; y++)
1529   {
1530     for (x = 0; x < cave->w; x++)
1531     {
1532       /* if we find a scanned element, change it to the normal one, and that's all. */
1533       /* this is required, for example for chasing stones, which have moved, always passing slime! */
1534       if (get(cave, x, y)&SCANNED)
1535       {
1536         store(cave, x, y, get(cave, x, y) & ~SCANNED);
1537
1538         continue;
1539       }
1540
1541       /* add the ckdelay correction value for every element seen. */
1542       cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
1543
1544       switch (get(cave, x, y))
1545       {
1546         /*
1547          *     P L A Y E R S
1548          */
1549         case O_PLAYER:
1550           if (cave->kill_player)
1551           {
1552             explode (cave, x, y);
1553             break;
1554           }
1555
1556           cave->player_seen_ago = 0;
1557           /* bd4 intermission caves have many players. so if one of them has exited,
1558            * do not change the flag anymore. so this if () is needed */
1559           if (cave->player_state!=GD_PL_EXITED)
1560             cave->player_state = GD_PL_LIVING;
1561
1562           /* check for pneumatic hammer things */
1563           /* 1) press fire, 2) have pneumatic hammer 4) space on left or right
1564              for hammer 5) stand on something */
1565           if (player_fire && cave->got_pneumatic_hammer &&
1566               is_space_dir(cave, x, y, player_move) &&
1567               !is_space_dir(cave, x, y, GD_MV_DOWN))
1568           {
1569             if (player_move == GD_MV_LEFT &&
1570                 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_LEFT))
1571             {
1572               cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1573               store_dir(cave, x, y, GD_MV_LEFT, O_PNEUMATIC_ACTIVE_LEFT);
1574               store(cave, x, y, O_PLAYER_PNEUMATIC_LEFT);
1575               break;    /* finished. */
1576             }
1577
1578             if (player_move == GD_MV_RIGHT &&
1579                 can_be_hammered_dir(cave, x, y, GD_MV_DOWN_RIGHT))
1580             {
1581               cave->pneumatic_hammer_active_delay = cave->pneumatic_hammer_frame;
1582               store_dir(cave, x, y, GD_MV_RIGHT, O_PNEUMATIC_ACTIVE_RIGHT);
1583               store(cave, x, y, O_PLAYER_PNEUMATIC_RIGHT);
1584               break;    /* finished. */
1585             }
1586           }
1587
1588           if (player_move != GD_MV_STILL)
1589           {
1590             /* only do every check if he is not moving */
1591             GdElement what = get_dir(cave, x, y, player_move);
1592             GdElement remains = what;
1593             boolean push;
1594
1595             /* if we are 'eating' a teleporter, and the function returns true
1596                (teleporting worked), break here */
1597             if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1598               break;
1599
1600             /* try to push element; if successful, break  */
1601             push = do_push(cave, x, y, player_move, player_fire);
1602             if (push)
1603               remains = O_SPACE;
1604             else
1605               switch (what)
1606               {
1607                 case O_BOMB:
1608                   /* if its a bomb, remember he now has one. */
1609                   /* we do not change the "remains" and "what" variables,
1610                      so that part of the code will be ineffective */
1611                   gd_sound_play(cave, GD_S_BOMB_COLLECT, what, x, y);
1612                   store_dir(cave, x, y, player_move, O_SPACE);
1613
1614                   if (player_fire)
1615                     store(cave, x, y, O_PLAYER_BOMB);
1616                   else
1617                     move(cave, x, y, player_move, O_PLAYER_BOMB);
1618                   break;
1619
1620                 case O_POT:
1621                   /* we do not change the "remains" and "what" variables,
1622                      so that part of the code will be ineffective */
1623                   if (!player_fire && !cave->gravity_switch_active &&
1624                       cave->skeletons_collected >= cave->skeletons_needed_for_pot)
1625                   {
1626                     cave->skeletons_collected -= cave->skeletons_needed_for_pot;
1627                     move(cave, x, y, player_move, O_PLAYER_STIRRING);
1628                     cave->gravity_disabled = TRUE;
1629                   }
1630                   break;
1631
1632                 case O_GRAVITY_SWITCH:
1633                   /* (we cannot use player_get for this as it does not have player_move parameter) */
1634                   /* only allow changing direction if the new dir is not diagonal */
1635                   if (cave->gravity_switch_active &&
1636                       (player_move == GD_MV_LEFT ||
1637                        player_move == GD_MV_RIGHT ||
1638                        player_move == GD_MV_UP ||
1639                        player_move == GD_MV_DOWN))
1640                   {
1641                     gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1642                     cave->gravity_will_change =
1643                       cave->gravity_change_time * cave->timing_factor;
1644                     cave->gravity_next_direction = player_move;
1645                     cave->gravity_switch_active = FALSE;
1646                   }
1647                   break;
1648
1649                 default:
1650                   /* get element - process others.
1651                      if cannot get, player_get_element will return the same */
1652                   remains = player_get_element (cave, what, x, y);
1653                   break;
1654               }
1655
1656             if (remains != what || remains == O_SPACE)
1657             {
1658               /* if anything changed, apply the change. */
1659
1660               /* if snapping anything and we have snapping explosions set.
1661                  but these is not true for pushing. */
1662               if (remains == O_SPACE && player_fire && !push)
1663                 remains = cave->snap_element;
1664
1665               if (remains != O_SPACE || player_fire)
1666                 /* if any other element than space, player cannot move.
1667                    also if pressing fire, will not move. */
1668                 store_dir(cave, x, y, player_move, remains);
1669               else
1670                 /* if space remains there, the player moves. */
1671                 move(cave, x, y, player_move, O_PLAYER);
1672             }
1673           }
1674           break;
1675
1676         case O_PLAYER_BOMB:
1677           /* much simpler; cannot steal stones */
1678           if (cave->kill_player)
1679           {
1680             explode(cave, x, y);
1681             break;
1682           }
1683
1684           cave->player_seen_ago = 0;
1685           /* bd4 intermission caves have many players. so if one of them has exited,
1686            * do not change the flag anymore. so this if () is needed */
1687           if (cave->player_state != GD_PL_EXITED)
1688             cave->player_state = GD_PL_LIVING;
1689
1690           if (player_move != GD_MV_STILL)
1691           {
1692             /* if the player does not move, nothing to do */
1693             GdElement what = get_dir(cave, x, y, player_move);
1694             GdElement remains = what;
1695
1696             if (player_fire)
1697             {
1698               /* placing a bomb into empty space or dirt */
1699               if (is_space_dir(cave, x, y, player_move) ||
1700                   is_element_dir(cave, x, y, player_move, O_DIRT))
1701               {
1702                 store_dir(cave, x, y, player_move, O_BOMB_TICK_1);
1703
1704                 /* placed bomb, he is normal player again */
1705                 store(cave, x, y, O_PLAYER);
1706                 gd_sound_play(cave, GD_S_BOMB_PLACE, O_BOMB, x, y);
1707               }
1708               break;
1709             }
1710
1711             /* pushing and collecting */
1712             /* if we are 'eating' a teleporter, and the function returns true
1713                (teleporting worked), break here */
1714             if (what == O_TELEPORTER && do_teleporter(cave, x, y, player_move))
1715               break;
1716
1717             /* player fire is false... */
1718             if (do_push(cave, x, y, player_move, FALSE))
1719               remains = O_SPACE;
1720             else
1721             {
1722               switch (what)
1723               {
1724                 case O_GRAVITY_SWITCH:
1725                   /* (we cannot use player_get for this as it does not have
1726                      player_move parameter) */
1727                   /* only allow changing direction if the new dir is not diagonal */
1728                   if (cave->gravity_switch_active &&
1729                       (player_move==GD_MV_LEFT ||
1730                        player_move==GD_MV_RIGHT ||
1731                        player_move==GD_MV_UP ||
1732                        player_move==GD_MV_DOWN))
1733                   {
1734                     gd_sound_play(cave, GD_S_SWITCH_GRAVITY, what, x, y);
1735                     cave->gravity_will_change =
1736                       cave->gravity_change_time * cave->timing_factor;
1737                     cave->gravity_next_direction = player_move;
1738                     cave->gravity_switch_active = FALSE;
1739                   }
1740                   break;
1741
1742                 default:
1743                   /* get element. if cannot get, player_get_element will return the same */
1744                   remains = player_get_element (cave, what, x, y);
1745                   break;
1746               }
1747             }
1748
1749             /* if element changed, OR there is space, move. */
1750             if (remains != what || remains == O_SPACE)
1751             {
1752               /* if anything changed, apply the change. */
1753               move(cave, x, y, player_move, O_PLAYER_BOMB);
1754             }
1755           }
1756           break;
1757
1758         case O_PLAYER_STIRRING:
1759           if (cave->kill_player)
1760           {
1761             explode(cave, x, y);
1762             break;
1763           }
1764
1765           /* stirring sound, if no other walking sound or explosion */
1766           gd_sound_play(cave, GD_S_STIRRING, O_PLAYER_STIRRING, x, y);
1767
1768           cave->player_seen_ago = 0;
1769           /* bd4 intermission caves have many players. so if one of them has exited,
1770            * do not change the flag anymore. so this if () is needed */
1771           if (cave->player_state!=GD_PL_EXITED)
1772             cave->player_state = GD_PL_LIVING;
1773
1774           if (player_fire)
1775           {
1776             /* player "exits" stirring the pot by pressing fire */
1777             cave->gravity_disabled = FALSE;
1778             store(cave, x, y, O_PLAYER);
1779             cave->gravity_switch_active = TRUE;
1780           }
1781           break;
1782
1783           /* player holding pneumatic hammer */
1784         case O_PLAYER_PNEUMATIC_LEFT:
1785         case O_PLAYER_PNEUMATIC_RIGHT:
1786           /* usual player stuff */
1787           if (cave->kill_player)
1788           {
1789             explode(cave, x, y);
1790             break;
1791           }
1792
1793           cave->player_seen_ago = 0;
1794           if (cave->player_state!=GD_PL_EXITED)
1795             cave->player_state = GD_PL_LIVING;
1796
1797           /* if hammering time is up, becomes a normal player again. */
1798           if (cave->pneumatic_hammer_active_delay == 0)
1799             store(cave, x, y, O_PLAYER);
1800           break;
1801
1802           /* the active pneumatic hammer itself */
1803         case O_PNEUMATIC_ACTIVE_RIGHT:
1804         case O_PNEUMATIC_ACTIVE_LEFT:
1805           if (cave->pneumatic_hammer_active_delay == 0)
1806           {
1807             GdElement new_elem;
1808
1809             /* pneumatic hammer element disappears */
1810             store(cave, x, y, O_SPACE);
1811
1812             /* which is the new element which appears after that one is hammered? */
1813             new_elem = gd_element_get_hammered(get_dir(cave, x, y, GD_MV_DOWN));
1814
1815             /* if there is a new element, display it */
1816             /* O_NONE might be returned, for example if the element being
1817                hammered explodes during hammering (by a nearby explosion) */
1818             if (new_elem != O_NONE)
1819             {
1820               store_dir(cave, x, y, GD_MV_DOWN, new_elem);
1821
1822               /* and if walls reappear, remember it in array */
1823               if (cave->hammered_walls_reappear)
1824               {
1825                 int wall_y;
1826
1827                 wall_y = (y + 1) % cave->h;
1828                 cave->hammered_reappear[wall_y][x] = cave->hammered_wall_reappear_frame;
1829               }
1830             }
1831           }
1832           break;
1833
1834           /*
1835            *     S T O N E S,   D I A M O N D S
1836            */
1837         case O_STONE:    /* standing stone */
1838           do_start_fall(cave, x, y, cave->gravity, cave->stone_falling_effect);
1839           break;
1840
1841         case O_MEGA_STONE:    /* standing mega_stone */
1842           do_start_fall(cave, x, y, cave->gravity, O_MEGA_STONE_F);
1843           break;
1844
1845         case O_DIAMOND:    /* standing diamond */
1846           do_start_fall(cave, x, y, cave->gravity, cave->diamond_falling_effect);
1847           break;
1848
1849         case O_NUT:    /* standing nut */
1850           do_start_fall(cave, x, y, cave->gravity, O_NUT_F);
1851           break;
1852
1853         case O_DIRT_BALL:    /* standing dirt ball */
1854           do_start_fall(cave, x, y, cave->gravity, O_DIRT_BALL_F);
1855           break;
1856
1857         case O_DIRT_LOOSE:    /* standing loose dirt */
1858           do_start_fall(cave, x, y, cave->gravity, O_DIRT_LOOSE_F);
1859           break;
1860
1861         case O_FLYING_STONE:    /* standing stone */
1862           do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_STONE_F);
1863           break;
1864
1865         case O_FLYING_DIAMOND:    /* standing diamond */
1866           do_start_fall(cave, x, y, opposite[cave->gravity], O_FLYING_DIAMOND_F);
1867           break;
1868
1869           /*
1870            *     F A L L I N G    E L E M E N T S,    F L Y I N G   S T O N E S,   D I A M O N D S
1871            */
1872         case O_DIRT_BALL_F:    /* falling dirt ball */
1873           if (!cave->gravity_disabled)
1874             do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_BALL);
1875           break;
1876
1877         case O_DIRT_LOOSE_F:    /* falling loose dirt */
1878           if (!cave->gravity_disabled)
1879             do_fall_roll_or_stop(cave, x, y, cave->gravity, O_DIRT_LOOSE);
1880           break;
1881
1882         case O_STONE_F:    /* falling stone */
1883           if (!cave->gravity_disabled)
1884           {
1885             if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1886               break;
1887
1888             if (do_fall_try_crack_nut(cave, x, y, cave->gravity, cave->stone_bouncing_effect))
1889               break;
1890
1891             if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_stone_to))
1892               break;
1893
1894             if (do_fall_try_crush(cave, x, y, cave->gravity))
1895               break;
1896
1897             do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->stone_bouncing_effect);
1898           }
1899           break;
1900
1901         case O_MEGA_STONE_F:    /* falling mega */
1902           if (!cave->gravity_disabled)
1903           {
1904             if (do_fall_try_crush_voodoo(cave, x, y, cave->gravity))
1905               break;
1906
1907             if (do_fall_try_crack_nut(cave, x, y, cave->gravity, O_MEGA_STONE))
1908               break;
1909
1910             if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_mega_stone_to))
1911               break;
1912
1913             if (do_fall_try_crush(cave, x, y, cave->gravity))
1914               break;
1915
1916             do_fall_roll_or_stop(cave, x, y, cave->gravity, O_MEGA_STONE);
1917           }
1918           break;
1919
1920         case O_DIAMOND_F:    /* falling diamond */
1921           if (!cave->gravity_disabled)
1922           {
1923             if (do_fall_try_eat_voodoo(cave, x, y, cave->gravity))
1924               break;
1925
1926             if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_diamond_to))
1927               break;
1928
1929             if (do_fall_try_crush(cave, x, y, cave->gravity))
1930               break;
1931
1932             do_fall_roll_or_stop(cave, x, y, cave->gravity, cave->diamond_bouncing_effect);
1933           }
1934           break;
1935
1936         case O_NUT_F:    /* falling nut */
1937           if (!cave->gravity_disabled)
1938           {
1939             if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nut_to))
1940               break;
1941
1942             if (do_fall_try_crush(cave, x, y, cave->gravity))
1943               break;
1944
1945             do_fall_roll_or_stop(cave, x, y, cave->gravity, O_NUT);
1946           }
1947           break;
1948
1949         case O_FLYING_STONE_F:    /* falling stone */
1950           if (!cave->gravity_disabled)
1951           {
1952             GdDirection fall_dir = opposite[cave->gravity];
1953
1954             if (do_fall_try_crush_voodoo(cave, x, y, fall_dir))
1955               break;
1956
1957             if (do_fall_try_crack_nut(cave, x, y, fall_dir, O_FLYING_STONE))
1958               break;
1959
1960             if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_stone_to))
1961               break;
1962
1963             if (do_fall_try_crush(cave, x, y, fall_dir))
1964               break;
1965
1966             do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_STONE);
1967           }
1968           break;
1969
1970         case O_FLYING_DIAMOND_F:    /* falling diamond */
1971           if (!cave->gravity_disabled)
1972           {
1973             GdDirection fall_dir = opposite[cave->gravity];
1974
1975             if (do_fall_try_eat_voodoo(cave, x, y, fall_dir))
1976               break;
1977
1978             if (do_fall_try_magic(cave, x, y, fall_dir, cave->magic_flying_diamond_to))
1979               break;
1980
1981             if (do_fall_try_crush(cave, x, y, fall_dir))
1982               break;
1983
1984             do_fall_roll_or_stop(cave, x, y, fall_dir, O_FLYING_DIAMOND);
1985           }
1986           break;
1987
1988           /*
1989            * N I T R O    P A C K
1990            */
1991         case O_NITRO_PACK:    /* standing nitro pack */
1992           do_start_fall(cave, x, y, cave->gravity, O_NITRO_PACK_F);
1993           break;
1994
1995         case O_NITRO_PACK_F:    /* falling nitro pack */
1996           if (!cave->gravity_disabled)
1997           {
1998             if (is_space_dir(cave, x, y, cave->gravity))    /* if space, falling further */
1999               move(cave, x, y, cave->gravity, get(cave, x, y));
2000             else if (do_fall_try_magic(cave, x, y, cave->gravity, cave->magic_nitro_pack_to))
2001             {
2002               /* try magic wall; if true, function did the work */
2003             }
2004             else if (is_element_dir(cave, x, y, cave->gravity, O_DIRT))
2005             {
2006               /* falling on a dirt, it does NOT explode - just stops at its place. */
2007               play_sound_of_element(cave, O_NITRO_PACK, x, y);
2008               store(cave, x, y, O_NITRO_PACK);
2009             }
2010             else
2011               /* falling on any other element it explodes */
2012               explode(cave, x, y);
2013           }
2014           break;
2015
2016         case O_NITRO_PACK_EXPLODE:    /* a triggered nitro pack */
2017           explode(cave, x, y);
2018           break;
2019
2020           /*
2021            *     C R E A T U R E S
2022            */
2023
2024         case O_COW_1:
2025         case O_COW_2:
2026         case O_COW_3:
2027         case O_COW_4:
2028           /* if cannot move in any direction, becomes an enclosed cow */
2029           if (!is_space_dir(cave, x, y, GD_MV_UP) && !is_space_dir(cave, x, y, GD_MV_DOWN) &&
2030               !is_space_dir(cave, x, y, GD_MV_LEFT) && !is_space_dir(cave, x, y, GD_MV_RIGHT))
2031             store(cave, x, y, O_COW_ENCLOSED_1);
2032           else
2033           {
2034             /* THIS IS THE CREATURE MOVE thing copied. */
2035             const GdDirection *creature_move;
2036             boolean ccw = rotates_ccw(cave, x, y);    /* check if default is counterclockwise */
2037             GdElement base;    /* base element number (which is like O_***_1) */
2038             int dir, dirn, dirp;    /* direction */
2039
2040             base = O_COW_1;
2041
2042             dir = get(cave, x, y)-base;    /* facing where */
2043             creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2044
2045             /* now change direction if backwards */
2046             if (cave->creatures_backwards)
2047               ccw = !ccw;
2048
2049             if (ccw)
2050             {
2051               dirn = (dir + 3) & 3;    /* fast turn */
2052               dirp = (dir + 1) & 3;    /* slow turn */
2053             }
2054             else
2055             {
2056               dirn = (dir + 1) & 3;    /* fast turn */
2057               dirp = (dir + 3) & 3;    /* slow turn */
2058             }
2059
2060             if (is_space_dir(cave, x, y, creature_move[dirn]))
2061               move(cave, x, y, creature_move[dirn], base + dirn);    /* turn and move to preferred dir */
2062             else if (is_space_dir(cave, x, y, creature_move[dir]))
2063               move(cave, x, y, creature_move[dir], base + dir);    /* go on */
2064             else
2065               store(cave, x, y, base + dirp);    /* turn in place if nothing else possible */
2066           }
2067           break;
2068
2069           /* enclosed cows wait some time before turning to a skeleton */
2070         case O_COW_ENCLOSED_1:
2071         case O_COW_ENCLOSED_2:
2072         case O_COW_ENCLOSED_3:
2073         case O_COW_ENCLOSED_4:
2074         case O_COW_ENCLOSED_5:
2075         case O_COW_ENCLOSED_6:
2076           if (is_space_dir(cave, x, y, GD_MV_UP) ||
2077               is_space_dir(cave, x, y, GD_MV_LEFT) ||
2078               is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2079               is_space_dir(cave, x, y, GD_MV_DOWN))
2080             store(cave, x, y, O_COW_1);
2081           else
2082             next(cave, x, y);
2083           break;
2084
2085         case O_COW_ENCLOSED_7:
2086           if (is_space_dir(cave, x, y, GD_MV_UP) ||
2087               is_space_dir(cave, x, y, GD_MV_LEFT) ||
2088               is_space_dir(cave, x, y, GD_MV_RIGHT) ||
2089               is_space_dir(cave, x, y, GD_MV_DOWN))
2090             store(cave, x, y, O_COW_1);
2091           else
2092             store(cave, x, y, O_SKELETON);
2093           break;
2094
2095         case O_FIREFLY_1:
2096         case O_FIREFLY_2:
2097         case O_FIREFLY_3:
2098         case O_FIREFLY_4:
2099         case O_ALT_FIREFLY_1:
2100         case O_ALT_FIREFLY_2:
2101         case O_ALT_FIREFLY_3:
2102         case O_ALT_FIREFLY_4:
2103         case O_BUTTER_1:
2104         case O_BUTTER_2:
2105         case O_BUTTER_3:
2106         case O_BUTTER_4:
2107         case O_ALT_BUTTER_1:
2108         case O_ALT_BUTTER_2:
2109         case O_ALT_BUTTER_3:
2110         case O_ALT_BUTTER_4:
2111         case O_STONEFLY_1:
2112         case O_STONEFLY_2:
2113         case O_STONEFLY_3:
2114         case O_STONEFLY_4:
2115           /* check if touches a voodoo */
2116           if (get_dir(cave, x, y, GD_MV_LEFT)  == O_VOODOO ||
2117               get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2118               get_dir(cave, x, y, GD_MV_UP)    == O_VOODOO ||
2119               get_dir(cave, x, y, GD_MV_DOWN)  == O_VOODOO)
2120             cave->voodoo_touched = TRUE;
2121
2122           /* check if touches something bad and should explode (includes voodoo by the flags) */
2123           if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2124               blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2125               blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2126               blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2127             explode (cave, x, y);
2128           /* otherwise move */
2129           else
2130           {
2131             const GdDirection *creature_move;
2132             boolean ccw = rotates_ccw(cave, x, y);    /* check if default is counterclockwise */
2133             GdElement base;    /* base element number (which is like O_***_1) */
2134             int dir, dirn, dirp;    /* direction */
2135
2136             if (get(cave, x, y) >= O_FIREFLY_1 &&
2137                 get(cave, x, y) <= O_FIREFLY_4)
2138               base = O_FIREFLY_1;
2139             else if (get(cave, x, y) >= O_BUTTER_1 &&
2140                      get(cave, x, y) <= O_BUTTER_4)
2141               base = O_BUTTER_1;
2142             else if (get(cave, x, y) >= O_STONEFLY_1 &&
2143                      get(cave, x, y) <= O_STONEFLY_4)
2144               base = O_STONEFLY_1;
2145             else if (get(cave, x, y) >= O_ALT_FIREFLY_1 &&
2146                      get(cave, x, y) <= O_ALT_FIREFLY_4)
2147               base = O_ALT_FIREFLY_1;
2148             else if (get(cave, x, y) >= O_ALT_BUTTER_1 &&
2149                      get(cave, x, y) <= O_ALT_BUTTER_4)
2150               base = O_ALT_BUTTER_1;
2151
2152             dir = get(cave, x, y)-base;    /* facing where */
2153             creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2154
2155             /* now change direction if backwards */
2156             if (cave->creatures_backwards)
2157               ccw = !ccw;
2158
2159             if (ccw)
2160             {
2161               dirn = (dir + 3) & 3;    /* fast turn */
2162               dirp = (dir + 1) & 3;    /* slow turn */
2163             }
2164             else
2165             {
2166               dirn = (dir + 1) & 3;    /* fast turn */
2167               dirp = (dir + 3) & 3;    /* slow turn */
2168             }
2169
2170             if (is_space_dir(cave, x, y, creature_move[dirn]))
2171               move(cave, x, y, creature_move[dirn], base + dirn);    /* turn and move to preferred dir */
2172             else if (is_space_dir(cave, x, y, creature_move[dir]))
2173               move(cave, x, y, creature_move[dir], base + dir);    /* go on */
2174             else
2175               store(cave, x, y, base + dirp);    /* turn in place if nothing else possible */
2176           }
2177           break;
2178
2179         case O_WAITING_STONE:
2180           if (is_space_dir(cave, x, y, grav_compat))
2181           {
2182             /* beginning to fall */
2183             /* it wakes up. */
2184             move(cave, x, y, grav_compat, O_CHASING_STONE);
2185           }
2186           else if (sloped_dir(cave, x, y, grav_compat, opposite[grav_compat]))
2187           {
2188             /* rolling down a brick wall or a stone */
2189             if (sloped_dir(cave, x, y, grav_compat, cw_fourth[grav_compat]) &&
2190                 is_space_dir(cave, x, y, cw_fourth[grav_compat]) &&
2191                 is_space_dir(cave, x, y, cw_eighth[grav_compat]))
2192             {
2193               /* maybe rolling left - see case O_STONE to understand why we use cw_fourth here */
2194               move(cave, x, y, cw_fourth[grav_compat], O_WAITING_STONE);
2195             }
2196             else if (sloped_dir(cave, x, y, grav_compat, ccw_fourth[grav_compat]) &&
2197                      is_space_dir(cave, x, y, ccw_fourth[grav_compat]) &&
2198                      is_space_dir(cave, x, y, ccw_eighth[grav_compat]))
2199             {
2200               /* or maybe right */
2201               move(cave, x, y, ccw_fourth[grav_compat], O_WAITING_STONE);
2202             }
2203           }
2204           break;
2205
2206         case O_CHASING_STONE:
2207           {
2208             int px = cave->px[0];
2209             int py = cave->py[0];
2210             boolean horizontal = g_rand_boolean(cave->random);
2211             boolean dont_move = FALSE;
2212             int i = 3;
2213
2214             /* try to move... */
2215             while (1)
2216             {
2217               if (horizontal)
2218               {
2219                 /*********************************/
2220                 /* check for a horizontal movement */
2221                 if (px == x)
2222                 {
2223                   /* if coordinates are the same */
2224                   i -= 1;
2225                   horizontal = !horizontal;
2226
2227                   if (i == 2)
2228                     continue;
2229                 }
2230                 else
2231                 {
2232                   if (px > x && is_space_dir(cave, x, y, GD_MV_RIGHT))
2233                   {
2234                     move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2235                     dont_move = TRUE;
2236                     break;
2237                   }
2238                   else if (px < x && is_space_dir(cave, x, y, GD_MV_LEFT))
2239                   {
2240                     move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2241                     dont_move = TRUE;
2242                     break;
2243                   }
2244                   else
2245                   {
2246                     i -= 2;
2247                     if (i == 1)
2248                     {
2249                       horizontal = !horizontal;
2250                       continue;
2251                     }
2252                   }
2253                 }
2254               }
2255               else
2256               {
2257                 /********************************/
2258                 /* check for a vertical movement */
2259                 if (py == y)
2260                 {
2261                   /* if coordinates are the same */
2262                   i -= 1;
2263                   horizontal = !horizontal;
2264                   if (i == 2)
2265                     continue;
2266                 }
2267                 else
2268                 {
2269                   if (py > y && is_space_dir(cave, x, y, GD_MV_DOWN))
2270                   {
2271                     move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2272                     dont_move = TRUE;
2273                     break;
2274                   }
2275                   else if (py < y && is_space_dir(cave, x, y, GD_MV_UP))
2276                   {
2277                     move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2278                     dont_move = TRUE;
2279                     break;
2280                   }
2281                   else
2282                   {
2283                     i -= 2;
2284                     if (i == 1)
2285                     {
2286                       horizontal = !horizontal;
2287                       continue;
2288                     }
2289                   }
2290                 }
2291               }
2292
2293               if (i != 0)
2294                 dont_move = TRUE;
2295
2296               break;
2297             }
2298
2299             /* if we should move in both directions, but can not move in any, stop. */
2300             if (!dont_move)
2301             {
2302               if (horizontal)
2303               {
2304                 /* check for horizontal */
2305                 if (x >= px)
2306                 {
2307                   if (is_space_dir(cave, x, y, GD_MV_UP) &&
2308                       is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2309                     move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2310                   else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2311                            is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2312                     move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2313                 }
2314                 else
2315                 {
2316                   if (is_space_dir(cave, x, y, GD_MV_UP) &&
2317                       is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2318                     move(cave, x, y, GD_MV_UP, O_CHASING_STONE);
2319                   else if (is_space_dir(cave, x, y, GD_MV_DOWN) &&
2320                            is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2321                     move(cave, x, y, GD_MV_DOWN, O_CHASING_STONE);
2322                 }
2323               }
2324               else
2325               {
2326                 /* check for vertical */
2327                 if (y >= py)
2328                 {
2329                   if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2330                       is_space_dir(cave, x, y, GD_MV_UP_LEFT))
2331                     move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2332                   else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2333                            is_space_dir(cave, x, y, GD_MV_UP_RIGHT))
2334                     move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2335                 }
2336                 else
2337                 {
2338                   if (is_space_dir(cave, x, y, GD_MV_LEFT) &&
2339                       is_space_dir(cave, x, y, GD_MV_DOWN_LEFT))
2340                     move(cave, x, y, GD_MV_LEFT, O_CHASING_STONE);
2341                   else if (is_space_dir(cave, x, y, GD_MV_RIGHT) &&
2342                            is_space_dir(cave, x, y, GD_MV_DOWN_RIGHT))
2343                     move(cave, x, y, GD_MV_RIGHT, O_CHASING_STONE);
2344                 }
2345               }
2346             }
2347           }
2348           break;
2349
2350         case O_REPLICATOR:
2351           if (cave->replicators_wait_frame == 0 &&
2352               cave->replicators_active &&
2353               !cave->gravity_disabled)
2354           {
2355             /* only replicate, if space is under it. */
2356             /* do not replicate players! */
2357             /* also obeys gravity settings. */
2358             /* only replicate element if it is not a scanned one */
2359             /* do not replicate space... that condition looks like it
2360                makes no sense, but otherwise it generates SCANNED spaces,
2361                which cannot be "collected" by the player, so he cannot run
2362                under a replicator */
2363             if (is_space_dir(cave, x, y, cave->gravity) &&
2364                 !is_player_dir(cave, x, y, opposite[cave->gravity]) &&
2365                 !is_space_dir(cave, x, y, opposite[cave->gravity]))
2366             {
2367               store_dir(cave, x, y, cave->gravity, get_dir(cave, x, y, opposite[cave->gravity]));
2368               gd_sound_play(cave, GD_S_REPLICATOR, O_REPLICATOR, x, y);
2369             }
2370           }
2371           break;
2372
2373         case O_BITER_1:
2374         case O_BITER_2:
2375         case O_BITER_3:
2376         case O_BITER_4:
2377           if (cave->biters_wait_frame == 0)
2378           {
2379             static GdDirection biter_move[] =
2380             {
2381               GD_MV_UP,
2382               GD_MV_RIGHT,
2383               GD_MV_DOWN,
2384               GD_MV_LEFT
2385             };
2386
2387             /* direction, last two bits 0..3 */
2388             int dir = get(cave, x, y) - O_BITER_1;
2389             int dirn = (dir + 3) & 3;
2390             int dirp = (dir + 1) & 3;
2391             int i;
2392             GdElement made_sound_of = O_NONE;
2393
2394             for (i = 0; i < G_N_ELEMENTS (biter_try); i++)
2395             {
2396               if (is_element_dir(cave, x, y, biter_move[dir], biter_try[i]))
2397               {
2398                 move(cave, x, y, biter_move[dir], O_BITER_1 + dir);
2399                 if (biter_try[i] != O_SPACE)
2400                   made_sound_of = O_BITER_1;    /* sound of a biter eating */
2401                 break;
2402               }
2403               else if (is_element_dir(cave, x, y, biter_move[dirn], biter_try[i]))
2404               {
2405                 move(cave, x, y, biter_move[dirn], O_BITER_1 + dirn);
2406                 if (biter_try[i] != O_SPACE)
2407                   made_sound_of = O_BITER_1;    /* sound of a biter eating */
2408                 break;
2409               }
2410               else if (is_element_dir(cave, x, y, biter_move[dirp], biter_try[i]))
2411               {
2412                 move(cave, x, y, biter_move[dirp], O_BITER_1 + dirp);
2413                 if (biter_try[i] != O_SPACE)
2414                   made_sound_of = O_BITER_1;    /* sound of a biter eating */
2415                 break;
2416               }
2417             }
2418
2419             if (i == G_N_ELEMENTS(biter_try))
2420               /* i = number of elements in array: could not move, so just turn */
2421               store(cave, x, y, O_BITER_1 + dirp);
2422             else if (biter_try[i] == O_STONE)
2423             {
2424               /* if there was a stone there, where we moved...
2425                  do not eat stones, just throw them back */
2426               store(cave, x, y, O_STONE);
2427               made_sound_of = O_STONE;
2428             }
2429
2430             /* if biter did move, we had sound. play it. */
2431             if (made_sound_of!=O_NONE)
2432               play_sound_of_element(cave, made_sound_of, x, y);
2433           }
2434           break;
2435
2436         case O_DRAGONFLY_1:
2437         case O_DRAGONFLY_2:
2438         case O_DRAGONFLY_3:
2439         case O_DRAGONFLY_4:
2440           /* check if touches a voodoo */
2441           if (get_dir(cave, x, y, GD_MV_LEFT)  == O_VOODOO ||
2442               get_dir(cave, x, y, GD_MV_RIGHT) == O_VOODOO ||
2443               get_dir(cave, x, y, GD_MV_UP)    == O_VOODOO ||
2444               get_dir(cave, x, y, GD_MV_DOWN)  == O_VOODOO)
2445             cave->voodoo_touched = TRUE;
2446
2447           /* check if touches something bad and should explode (includes voodoo by the flags) */
2448           if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2449               blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2450               blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2451               blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2452             explode (cave, x, y);
2453           /* otherwise move */
2454           else
2455           {
2456             const GdDirection *creature_move;
2457             boolean ccw = rotates_ccw(cave, x, y);    /* check if default is counterclockwise */
2458             GdElement base = O_DRAGONFLY_1;    /* base element number (which is like O_***_1) */
2459             int dir, dirn;    /* direction */
2460
2461             dir = get(cave, x, y)-base;    /* facing where */
2462             creature_move = cave->creatures_backwards ? creature_chdir : creature_dir;
2463
2464             /* now change direction if backwards */
2465             if (cave->creatures_backwards)
2466               ccw = !ccw;
2467
2468             if (ccw)
2469               dirn = (dir + 3) & 3;    /* fast turn */
2470             else
2471               dirn = (dir + 1) & 3;    /* fast turn */
2472
2473             /* if can move forward, does so. */
2474             if (is_space_dir(cave, x, y, creature_move[dir]))
2475               move(cave, x, y, creature_move[dir], base + dir);
2476             else
2477               /* otherwise turns 90 degrees in place. */
2478               store(cave, x, y, base + dirn);
2479           }
2480           break;
2481
2482         case O_BLADDER:
2483           store(cave, x, y, O_BLADDER_1);
2484           break;
2485
2486         case O_BLADDER_1:
2487         case O_BLADDER_2:
2488         case O_BLADDER_3:
2489         case O_BLADDER_4:
2490         case O_BLADDER_5:
2491         case O_BLADDER_6:
2492         case O_BLADDER_7:
2493         case O_BLADDER_8:
2494           /* bladder with any delay state: try to convert to clock. */
2495           if (is_element_dir(cave, x, y, opposite[grav_compat], cave->bladder_converts_by) ||
2496               is_element_dir(cave, x, y, cw_fourth[grav_compat], cave->bladder_converts_by) || is_element_dir(cave, x, y, ccw_fourth[grav_compat], cave->bladder_converts_by))
2497           {
2498             /* if touches the specified element, let it be a clock */
2499             store(cave, x, y, O_PRE_CLOCK_1);
2500
2501             /* plays the bladder convert sound */
2502             play_sound_of_element(cave, O_PRE_CLOCK_1, x, y);
2503           }
2504           else
2505           {
2506             /* is space over the bladder? */
2507             if (is_space_dir(cave, x, y, opposite[grav_compat]))
2508             {
2509               if (get(cave, x, y)==O_BLADDER_8)
2510               {
2511                 /* if it is a bladder 8, really move up */
2512                 move(cave, x, y, opposite[grav_compat], O_BLADDER_1);
2513                 play_sound_of_element(cave, O_BLADDER, x, y);
2514               }
2515               else
2516                 /* if smaller delay, just increase delay. */
2517                 next(cave, x, y);
2518             }
2519             else
2520               /* if not space, is something sloped over the bladder? */
2521               if (sloped_for_bladder_dir(cave, x, y, opposite[grav_compat]) &&
2522                   sloped_dir(cave, x, y, opposite[grav_compat], opposite[grav_compat]))
2523               {
2524                 if (sloped_dir(cave, x, y, opposite[grav_compat], ccw_fourth[opposite[grav_compat]]) &&
2525                     is_space_dir(cave, x, y, ccw_fourth[opposite[grav_compat]]) &&
2526                     is_space_dir(cave, x, y, ccw_eighth[opposite[grav_compat]]))
2527                 {
2528                   /* rolling up, to left */
2529                   if (get(cave, x, y) == O_BLADDER_8)
2530                   {
2531                     /* if it is a bladder 8, really roll */
2532                     move(cave, x, y, ccw_fourth[opposite[grav_compat]], O_BLADDER_8);
2533                     play_sound_of_element(cave, O_BLADDER, x, y);
2534                   }
2535                   else
2536                     /* if smaller delay, just increase delay. */
2537                     next(cave, x, y);
2538                 }
2539                 else if (sloped_dir(cave, x, y, opposite[grav_compat], cw_fourth[opposite[grav_compat]]) &&
2540                          is_space_dir(cave, x, y, cw_fourth[opposite[grav_compat]]) &&
2541                          is_space_dir(cave, x, y, cw_eighth[opposite[grav_compat]]))
2542                 {
2543                   /* rolling up, to left */
2544                   if (get(cave, x, y) == O_BLADDER_8)
2545                   {
2546                     /* if it is a bladder 8, really roll */
2547                     move(cave, x, y, cw_fourth[opposite[grav_compat]], O_BLADDER_8);
2548                     play_sound_of_element(cave, O_BLADDER, x, y);
2549                   }
2550                   else
2551                     /* if smaller delay, just increase delay. */
2552                     next(cave, x, y);
2553                 }
2554               }
2555
2556             /* no space, no sloped thing over it - store bladder 1 and that is for now. */
2557               else
2558                 store(cave, x, y, O_BLADDER_1);
2559           }
2560           break;
2561
2562         case O_GHOST:
2563           if (blows_up_flies_dir(cave, x, y, GD_MV_DOWN) ||
2564               blows_up_flies_dir(cave, x, y, GD_MV_UP) ||
2565               blows_up_flies_dir(cave, x, y, GD_MV_LEFT) ||
2566               blows_up_flies_dir(cave, x, y, GD_MV_RIGHT))
2567             explode (cave, x, y);
2568           else
2569           {
2570             int i;
2571
2572             /* the ghost is given four possibilities to move. */
2573             for (i = 0; i < 4; i++)
2574             {
2575               static GdDirection dirs[] =
2576               {
2577                 GD_MV_UP,
2578                 GD_MV_DOWN,
2579                 GD_MV_LEFT,
2580                 GD_MV_RIGHT
2581               };
2582               GdDirection random_dir;
2583
2584               random_dir = dirs[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(dirs))];
2585               if (is_space_dir(cave, x, y, random_dir))
2586               {
2587                 move(cave, x, y, random_dir, O_GHOST);
2588                 break;    /* ghost did move -> exit loop */
2589               }
2590             }
2591           }
2592           break;
2593
2594           /*
2595            *     A C T I V E    E L E M E N T S
2596            */
2597
2598         case O_AMOEBA:
2599           amoeba_count++;
2600           switch (cave->amoeba_state)
2601           {
2602             case GD_AM_TOO_BIG:
2603               store(cave, x, y, cave->amoeba_too_big_effect);
2604               break;
2605
2606             case GD_AM_ENCLOSED:
2607               store(cave, x, y, cave->amoeba_enclosed_effect);
2608               break;
2609
2610             case GD_AM_SLEEPING:
2611             case GD_AM_AWAKE:
2612               /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2613               if (amoeba_found_enclosed)
2614                 /* if still found enclosed, check all four directions,
2615                    if this one is able to grow. */
2616                 if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2617                     amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2618                     amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2619                     amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2620                 {
2621                   /* not enclosed. this is a local (per scan) flag! */
2622                   amoeba_found_enclosed = FALSE;
2623                   cave->amoeba_state = GD_AM_AWAKE;
2624                 }
2625
2626               /* if alive, check in which dir to grow (or not) */
2627               if (cave->amoeba_state==GD_AM_AWAKE)
2628               {
2629                 if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_growth_prob)
2630                 {
2631                   switch (g_rand_int_range(cave->random, 0, 4))
2632                   {
2633                     /* decided to grow, choose a random direction. */
2634                     case 0:    /* let this be up. numbers indifferent. */
2635                       if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2636                         store_dir(cave, x, y, GD_MV_UP, O_AMOEBA);
2637                       break;
2638
2639                     case 1:    /* down */
2640                       if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2641                         store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA);
2642                       break;
2643
2644                     case 2:    /* left */
2645                       if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2646                         store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA);
2647                       break;
2648
2649                     case 3:    /* right */
2650                       if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2651                         store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA);
2652                       break;
2653                   }
2654                 }
2655               }
2656               break;
2657
2658           }
2659           break;
2660
2661         case O_AMOEBA_2:
2662           amoeba_2_count++;
2663           /* check if it is touching an amoeba, and explosion is enabled */
2664           if (cave->amoeba_2_explodes_by_amoeba &&
2665               (is_element_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA) ||
2666                is_element_dir(cave, x, y, GD_MV_UP, O_AMOEBA) ||
2667                is_element_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA) ||
2668                is_element_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA)))
2669             explode (cave, x, y);
2670           else
2671             switch (cave->amoeba_2_state)
2672             {
2673               case GD_AM_TOO_BIG:
2674                 store(cave, x, y, cave->amoeba_2_too_big_effect);
2675                 break;
2676
2677               case GD_AM_ENCLOSED:
2678                 store(cave, x, y, cave->amoeba_2_enclosed_effect);
2679                 break;
2680
2681               case GD_AM_SLEEPING:
2682               case GD_AM_AWAKE:
2683                 /* if no amoeba found during THIS SCAN yet, which was able to grow, check this one. */
2684                 if (amoeba_2_found_enclosed)
2685                   if (amoeba_eats_dir(cave, x, y, GD_MV_UP) ||
2686                       amoeba_eats_dir(cave, x, y, GD_MV_DOWN) ||
2687                       amoeba_eats_dir(cave, x, y, GD_MV_LEFT) ||
2688                       amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2689                   {
2690                     /* not enclosed. this is a local (per scan) flag! */
2691                     amoeba_2_found_enclosed = FALSE;
2692                     cave->amoeba_2_state = GD_AM_AWAKE;
2693                   }
2694
2695                 /* if it is alive, decide if it attempts to grow */
2696                 if (cave->amoeba_2_state == GD_AM_AWAKE)
2697                   if (g_rand_int_range(cave->random, 0, 1000000) < cave->amoeba_2_growth_prob)
2698                   {
2699                     switch (g_rand_int_range(cave->random, 0, 4))
2700                     {
2701                       /* decided to grow, choose a random direction. */
2702                       case 0:    /* let this be up. numbers indifferent. */
2703                         if (amoeba_eats_dir(cave, x, y, GD_MV_UP))
2704                           store_dir(cave, x, y, GD_MV_UP, O_AMOEBA_2);
2705                         break;
2706
2707                       case 1:    /* down */
2708                         if (amoeba_eats_dir(cave, x, y, GD_MV_DOWN))
2709                           store_dir(cave, x, y, GD_MV_DOWN, O_AMOEBA_2);
2710                         break;
2711
2712                       case 2:    /* left */
2713                         if (amoeba_eats_dir(cave, x, y, GD_MV_LEFT))
2714                           store_dir(cave, x, y, GD_MV_LEFT, O_AMOEBA_2);
2715                         break;
2716
2717                       case 3:    /* right */
2718                         if (amoeba_eats_dir(cave, x, y, GD_MV_RIGHT))
2719                           store_dir(cave, x, y, GD_MV_RIGHT, O_AMOEBA_2);
2720                         break;
2721                     }
2722                   }
2723                 break;
2724
2725             }
2726           break;
2727
2728         case O_ACID:
2729           /* choose randomly, if it spreads */
2730           if (g_rand_int_range(cave->random, 0, 1000000) <= cave->acid_spread_ratio)
2731           {
2732             /* the current one explodes */
2733             store(cave, x, y, cave->acid_turns_to);
2734
2735             /* and if neighbours are eaten, put acid there. */
2736             if (is_element_dir(cave, x, y, GD_MV_UP, cave->acid_eats_this))
2737             {
2738               play_sound_of_element(cave, O_ACID, x, y);
2739               store_dir(cave, x, y, GD_MV_UP, O_ACID);
2740             }
2741
2742             if (is_element_dir(cave, x, y, GD_MV_DOWN, cave->acid_eats_this))
2743             {
2744               play_sound_of_element(cave, O_ACID, x, y);
2745               store_dir(cave, x, y, GD_MV_DOWN, O_ACID);
2746             }
2747
2748             if (is_element_dir(cave, x, y, GD_MV_LEFT, cave->acid_eats_this))
2749             {
2750               play_sound_of_element(cave, O_ACID, x, y);
2751               store_dir(cave, x, y, GD_MV_LEFT, O_ACID);
2752             }
2753
2754             if (is_element_dir(cave, x, y, GD_MV_RIGHT, cave->acid_eats_this))
2755             {
2756               play_sound_of_element(cave, O_ACID, x, y);
2757               store_dir(cave, x, y, GD_MV_RIGHT, O_ACID);
2758             }
2759           }
2760           break;
2761
2762         case O_WATER:
2763           found_water = TRUE;
2764           if (!cave->water_does_not_flow_down &&
2765               is_space_dir(cave, x, y, GD_MV_DOWN))
2766             /* emulating the odd behaviour in crdr */
2767             store_dir(cave, x, y, GD_MV_DOWN, O_WATER_1);
2768
2769           if (is_space_dir(cave, x, y, GD_MV_UP))
2770             store_dir(cave, x, y, GD_MV_UP, O_WATER_1);
2771
2772           if (is_space_dir(cave, x, y, GD_MV_LEFT))
2773             store_dir(cave, x, y, GD_MV_LEFT, O_WATER_1);
2774
2775           if (is_space_dir(cave, x, y, GD_MV_RIGHT))
2776             store_dir(cave, x, y, GD_MV_RIGHT, O_WATER_1);
2777           break;
2778
2779         case O_WATER_16:
2780           store(cave, x, y, O_WATER);
2781           break;
2782
2783         case O_H_EXPANDING_WALL:
2784         case O_V_EXPANDING_WALL:
2785         case O_H_EXPANDING_STEEL_WALL:
2786         case O_V_EXPANDING_STEEL_WALL:
2787           /* checks first if direction is changed. */
2788           if (((get(cave, x, y) == O_H_EXPANDING_WALL ||
2789                 get(cave, x, y) == O_H_EXPANDING_STEEL_WALL) &&
2790                !cave->expanding_wall_changed) ||
2791               ((get(cave, x, y)==O_V_EXPANDING_WALL ||
2792                 get(cave, x, y)==O_V_EXPANDING_STEEL_WALL) &&
2793                cave->expanding_wall_changed))
2794           {
2795             if (is_space_dir(cave, x, y, GD_MV_LEFT))
2796             {
2797               store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2798               play_sound_of_element(cave, get(cave, x, y), x, y);
2799             }
2800
2801             if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2802               store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2803               play_sound_of_element(cave, get(cave, x, y), x, y);
2804             }
2805           }
2806           else
2807           {
2808             if (is_space_dir(cave, x, y, GD_MV_UP)) {
2809               store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2810               play_sound_of_element(cave, get(cave, x, y), x, y);
2811             }
2812
2813             if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2814               store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2815               play_sound_of_element(cave, get(cave, x, y), x, y);
2816             }
2817           }
2818           break;
2819
2820         case O_EXPANDING_WALL:
2821         case O_EXPANDING_STEEL_WALL:
2822           /* the wall which grows in all four directions. */
2823           if (is_space_dir(cave, x, y, GD_MV_LEFT))
2824           {
2825             store_dir(cave, x, y, GD_MV_LEFT, get(cave, x, y));
2826             play_sound_of_element(cave, get(cave, x, y), x, y);
2827           }
2828
2829           if (is_space_dir(cave, x, y, GD_MV_RIGHT)) {
2830             store_dir(cave, x, y, GD_MV_RIGHT, get(cave, x, y));
2831             play_sound_of_element(cave, get(cave, x, y), x, y);
2832           }
2833
2834           if (is_space_dir(cave, x, y, GD_MV_UP)) {
2835             store_dir(cave, x, y, GD_MV_UP, get(cave, x, y));
2836             play_sound_of_element(cave, get(cave, x, y), x, y);
2837           }
2838
2839           if (is_space_dir(cave, x, y, GD_MV_DOWN)) {
2840             store_dir(cave, x, y, GD_MV_DOWN, get(cave, x, y));
2841             play_sound_of_element(cave, get(cave, x, y), x, y);
2842           }
2843           break;
2844
2845         case O_SLIME:
2846 #if 1
2847           ; // to make compilers happy ...
2848 #else
2849           g_print("Step[%03d]", cave->frame); /* XXX */
2850 #endif
2851           int rrr = gd_cave_c64_random(cave);
2852 #if 1
2853 #else
2854           g_print(".Rand[%03d].Perm[%03d].Result[%d]\n", rrr, cave->slime_permeability_c64,
2855                   (rrr & cave->slime_permeability_c64) == 0);
2856 #endif
2857           /*
2858            * unpredictable: g_rand_int
2859            * predictable: c64 predictable random generator.
2860            *    for predictable, a random number is generated,
2861            *    whether or not it is even possible that the stone will be able to pass.
2862            */
2863           if (cave->slime_predictable ? ((rrr /* XXX */ & cave->slime_permeability_c64) == 0) : g_rand_int_range(cave->random, 0, 1000000) < cave->slime_permeability)
2864           {
2865             GdDirection grav = cave->gravity;
2866             GdDirection oppos = opposite[cave->gravity];
2867
2868             /* space under the slime? elements may pass from top to bottom then. */
2869             if (is_space_dir(cave, x, y, grav))
2870             {
2871               if (get_dir(cave, x, y, oppos) == cave->slime_eats_1)
2872               {
2873                 /* output a falling xy under */
2874                 store_dir(cave, x, y, grav, cave->slime_converts_1);
2875
2876                 store_dir(cave, x, y, oppos, O_SPACE);
2877                 play_sound_of_element(cave, O_SLIME, x, y);
2878               }
2879               else if (get_dir(cave, x, y, oppos) == cave->slime_eats_2)
2880               {
2881                 store_dir(cave, x, y, grav, cave->slime_converts_2);
2882                 store_dir(cave, x, y, oppos, O_SPACE);
2883                 play_sound_of_element(cave, O_SLIME, x, y);
2884               }
2885               else if (get_dir(cave, x, y, oppos) == cave->slime_eats_3)
2886               {
2887                 store_dir(cave, x, y, grav, cave->slime_converts_3);
2888                 store_dir(cave, x, y, oppos, O_SPACE);
2889                 play_sound_of_element(cave, O_SLIME, x, y);
2890               }
2891               else if (get_dir(cave, x, y, oppos) == O_WAITING_STONE)
2892               {
2893                 /* waiting stones pass without awakening */
2894                 store_dir(cave, x, y, grav, O_WAITING_STONE);
2895                 store_dir(cave, x, y, oppos, O_SPACE);
2896                 play_sound_of_element(cave, O_SLIME, x, y);
2897               }
2898               else if (get_dir(cave, x, y, oppos) == O_CHASING_STONE)
2899               {
2900                 /* chasing stones pass */
2901                 store_dir(cave, x, y, grav, O_CHASING_STONE);
2902                 store_dir(cave, x, y, oppos, O_SPACE);
2903                 play_sound_of_element(cave, O_SLIME, x, y);
2904               }
2905             }
2906             else
2907               /* or space over the slime? elements may pass from bottom to up then. */
2908               if (is_space_dir(cave, x, y, oppos))
2909               {
2910                 if (get_dir(cave, x, y, grav) == O_BLADDER)
2911                 {
2912                   /* bladders move UP the slime */
2913                   store_dir(cave, x, y, grav, O_SPACE);
2914                   store_dir(cave, x, y, oppos, O_BLADDER_1);
2915                   play_sound_of_element(cave, O_SLIME, x, y);
2916                 }
2917                 else if (get_dir(cave, x, y, grav)==O_FLYING_STONE)
2918                 {
2919                   store_dir(cave, x, y, grav, O_SPACE);
2920                   store_dir(cave, x, y, oppos, O_FLYING_STONE_F);
2921                   play_sound_of_element(cave, O_SLIME, x, y);
2922                 }
2923                 else if (get_dir(cave, x, y, grav)==O_FLYING_DIAMOND)
2924                 {
2925                   store_dir(cave, x, y, grav, O_SPACE);
2926                   store_dir(cave, x, y, oppos, O_FLYING_DIAMOND_F);
2927                   play_sound_of_element(cave, O_SLIME, x, y);
2928                 }
2929               }
2930           }
2931           break;
2932
2933         case O_FALLING_WALL:
2934           if (is_space_dir(cave, x, y, grav_compat))
2935           {
2936             /* try falling if space under. */
2937             int yy;
2938
2939             for (yy = y + 1; yy < y + cave->h; yy++)
2940               /* yy < y + cave->h is to check everything OVER the wall - since caves wrap around !! */
2941               if (get(cave, x, yy) != O_SPACE)
2942                 /* stop cycle when other than space */
2943                 break;
2944
2945             /* if scanning stopped by a player... start falling! */
2946             if (get(cave, x, yy) == O_PLAYER ||
2947                 get(cave, x, yy) == O_PLAYER_GLUED ||
2948                 get(cave, x, yy) == O_PLAYER_BOMB)
2949             {
2950               move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2951               /* no sound when the falling wall starts falling! */
2952             }
2953           }
2954           break;
2955
2956         case O_FALLING_WALL_F:
2957           switch (get_dir(cave, x, y, grav_compat))
2958           {
2959             case O_PLAYER:
2960             case O_PLAYER_GLUED:
2961             case O_PLAYER_BOMB:
2962               /* if player under, it explodes - the falling wall, not the player! */
2963               explode(cave, x, y);
2964               break;
2965
2966             case O_SPACE:
2967               /* continue falling */
2968               move(cave, x, y, grav_compat, O_FALLING_WALL_F);
2969               break;
2970
2971             default:
2972               /* stop */
2973               play_sound_of_element(cave, get(cave, x, y), x, y);
2974               store(cave, x, y, O_FALLING_WALL);
2975               break;
2976           }
2977           break;
2978
2979           /*
2980            * C O N V E Y O R    B E L T S
2981            */
2982
2983         case O_CONVEYOR_RIGHT:
2984         case O_CONVEYOR_LEFT:
2985           /* only works if gravity is up or down!!! */
2986           /* first, check for gravity and running belts. */
2987           if (!cave->gravity_disabled && cave->conveyor_belts_active)
2988           {
2989             const GdDirection *dir;
2990             boolean left;
2991
2992             /* decide direction */
2993             left = get(cave, x, y) != O_CONVEYOR_RIGHT;
2994             if (cave->conveyor_belts_direction_changed)
2995               left = !left;
2996             dir = left ? ccw_eighth : cw_eighth;
2997
2998             /* CHECK IF IT CONVEYS THE ELEMENT ABOVE IT */
2999             /* if gravity is normal, and the conveyor belt has something
3000                ABOVE which can be moved
3001                OR
3002                the gravity is up, so anything that should float now goes
3003                DOWN and touches the conveyor */
3004             if ((cave->gravity == GD_MV_DOWN &&
3005                  moved_by_conveyor_top_dir(cave, x, y, GD_MV_UP)) ||
3006                 (cave->gravity == GD_MV_UP &&
3007                  moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_UP)))
3008             {
3009               if (!is_scanned_dir(cave, x, y, GD_MV_UP) &&
3010                   is_space_dir(cave, x, y, dir[GD_MV_UP]))
3011               {
3012                 store_dir(cave, x, y, dir[GD_MV_UP], get_dir(cave, x, y, GD_MV_UP));    /* move */
3013                 store_dir(cave, x, y, GD_MV_UP, O_SPACE);    /* and place a space. */
3014               }
3015             }
3016
3017             /* CHECK IF IT CONVEYS THE ELEMENT BELOW IT */
3018             if ((cave->gravity == GD_MV_UP &&
3019                  moved_by_conveyor_top_dir(cave, x, y, GD_MV_DOWN)) ||
3020                 (cave->gravity == GD_MV_DOWN &&
3021                  moved_by_conveyor_bottom_dir(cave, x, y, GD_MV_DOWN)))
3022             {
3023               if (!is_scanned_dir(cave, x, y, GD_MV_DOWN) &&
3024                   is_space_dir(cave, x, y, dir[GD_MV_DOWN]))
3025               {
3026                 store_dir(cave, x, y, dir[GD_MV_DOWN], get_dir(cave, x, y, GD_MV_DOWN));    /* move */
3027                 store_dir(cave, x, y, GD_MV_DOWN, O_SPACE);    /* and clear. */
3028               }
3029             }
3030           }
3031           break;
3032
3033           /*
3034            * S I M P L E   C H A N G I N G;   E X P L O S I O N S
3035            */
3036
3037         case O_EXPLODE_5:
3038           store(cave, x, y, cave->explosion_effect);
3039           break;
3040
3041         case O_NUT_EXPL_4:
3042           store(cave, x, y, O_DIAMOND);
3043           break;
3044
3045         case O_PRE_DIA_5:
3046           store(cave, x, y, cave->diamond_birth_effect);
3047           break;
3048
3049         case O_PRE_STONE_4:
3050           store(cave, x, y, O_STONE);
3051           break;
3052
3053         case O_NITRO_EXPL_4:
3054           store(cave, x, y, cave->nitro_explosion_effect);
3055           break;
3056
3057         case O_BOMB_EXPL_4:
3058           store(cave, x, y, cave->bomb_explosion_effect);
3059           break;
3060
3061         case O_AMOEBA_2_EXPL_4:
3062           store(cave, x, y, cave->amoeba_2_explosion_effect);
3063           break;
3064
3065         case O_GHOST_EXPL_4:
3066           {
3067             static GdElement ghost_explode[] =
3068             {
3069               O_SPACE, O_SPACE, O_DIRT, O_DIRT, O_CLOCK, O_CLOCK, O_PRE_OUTBOX,
3070               O_BOMB, O_BOMB, O_PLAYER, O_GHOST, O_BLADDER, O_DIAMOND, O_SWEET,
3071               O_WAITING_STONE, O_BITER_1
3072             };
3073
3074             store(cave, x, y, ghost_explode[g_rand_int_range(cave->random, 0, G_N_ELEMENTS(ghost_explode))]);
3075           }
3076           break;
3077
3078         case O_PRE_STEEL_4:
3079           store(cave, x, y, O_STEEL);
3080           break;
3081
3082         case O_PRE_CLOCK_4:
3083           store(cave, x, y, O_CLOCK);
3084           break;
3085
3086         case O_BOMB_TICK_7:
3087           explode(cave, x, y);
3088           break;
3089
3090         case O_TRAPPED_DIAMOND:
3091           if (cave->diamond_key_collected)
3092             store(cave, x, y, O_DIAMOND);
3093           break;
3094
3095         case O_PRE_OUTBOX:
3096           if (cave->gate_open) /* if no more diamonds needed */
3097             store(cave, x, y, O_OUTBOX);    /* open outbox */
3098           break;
3099
3100         case O_PRE_INVIS_OUTBOX:
3101           if (cave->gate_open)    /* if no more diamonds needed */
3102             store(cave, x, y, O_INVIS_OUTBOX);    /* open outbox. invisible one :P */
3103           break;
3104
3105         case O_INBOX:
3106           if (cave->hatched && !inbox_toggle)    /* if it is time of birth */
3107             store(cave, x, y, O_PRE_PL_1);
3108           inbox_toggle = !inbox_toggle;
3109           break;
3110
3111         case O_PRE_PL_3:
3112           store(cave, x, y, O_PLAYER);
3113           break;
3114
3115         case O_PRE_DIA_1:
3116         case O_PRE_DIA_2:
3117         case O_PRE_DIA_3:
3118         case O_PRE_DIA_4:
3119         case O_PRE_STONE_1:
3120         case O_PRE_STONE_2:
3121         case O_PRE_STONE_3:
3122         case O_BOMB_TICK_1:
3123         case O_BOMB_TICK_2:
3124         case O_BOMB_TICK_3:
3125         case O_BOMB_TICK_4:
3126         case O_BOMB_TICK_5:
3127         case O_BOMB_TICK_6:
3128         case O_PRE_STEEL_1:
3129         case O_PRE_STEEL_2:
3130         case O_PRE_STEEL_3:
3131         case O_BOMB_EXPL_1:
3132         case O_BOMB_EXPL_2:
3133         case O_BOMB_EXPL_3:
3134         case O_NUT_EXPL_1:
3135         case O_NUT_EXPL_2:
3136         case O_NUT_EXPL_3:
3137         case O_GHOST_EXPL_1:
3138         case O_GHOST_EXPL_2:
3139         case O_GHOST_EXPL_3:
3140         case O_EXPLODE_1:
3141         case O_EXPLODE_2:
3142         case O_EXPLODE_3:
3143         case O_EXPLODE_4:
3144         case O_PRE_PL_1:
3145         case O_PRE_PL_2:
3146         case O_PRE_CLOCK_1:
3147         case O_PRE_CLOCK_2:
3148         case O_PRE_CLOCK_3:
3149         case O_NITRO_EXPL_1:
3150         case O_NITRO_EXPL_2:
3151         case O_NITRO_EXPL_3:
3152         case O_AMOEBA_2_EXPL_1:
3153         case O_AMOEBA_2_EXPL_2:
3154         case O_AMOEBA_2_EXPL_3:
3155           /* simply the next identifier */
3156           next(cave, x, y);
3157           break;
3158
3159         case O_WATER_1:
3160         case O_WATER_2:
3161         case O_WATER_3:
3162         case O_WATER_4:
3163         case O_WATER_5:
3164         case O_WATER_6:
3165         case O_WATER_7:
3166         case O_WATER_8:
3167         case O_WATER_9:
3168         case O_WATER_10:
3169         case O_WATER_11:
3170         case O_WATER_12:
3171         case O_WATER_13:
3172         case O_WATER_14:
3173         case O_WATER_15:
3174           found_water = TRUE;    /* for sound */
3175           /* simply the next identifier */
3176           next(cave, x, y);
3177           break;
3178
3179         case O_BLADDER_SPENDER:
3180           if (is_space_dir(cave, x, y, opposite[grav_compat]))
3181           {
3182             store_dir(cave, x, y, opposite[grav_compat], O_BLADDER);
3183             store(cave, x, y, O_PRE_STEEL_1);
3184             play_sound_of_element(cave, O_BLADDER_SPENDER, x, y);
3185           }
3186           break;
3187
3188         default:
3189           /* other inanimate elements that do nothing */
3190           break;
3191       }
3192     }
3193   }
3194
3195   /* POSTPROCESSING */
3196
3197   /* another scan-like routine: */
3198   /* short explosions (for example, in bd1) started with explode_2. */
3199   /* internally we use explode_1; and change it to explode_2 if needed. */
3200   if (cave->short_explosions)
3201   {
3202     for (y = 0; y < cave->h; y++)
3203     {
3204       for (x = 0; x < cave->w; x++)
3205       {
3206         if (is_first_stage_of_explosion(cave, x, y))
3207         {
3208           next(cave, x, y);    /* select next frame of explosion */
3209
3210           /* forget scanned flag immediately */
3211           store(cave, x, y, get(cave, x, y) & ~SCANNED);
3212         }
3213       }
3214     }
3215   }
3216
3217   /* finally: forget "scanned" flags for objects. */
3218   /* also, check for time penalties. */
3219   /* these is something like an effect table, but we do not really use one. */
3220   for (y = 0; y < cave->h; y++)
3221   {
3222     for (x = 0; x < cave->w; x++)
3223     {
3224       if (get(cave, x, y) & SCANNED)
3225         store(cave, x, y, get(cave, x, y) & ~SCANNED);
3226
3227       if (get(cave, x, y) == O_TIME_PENALTY)
3228       {
3229         store(cave, x, y, O_GRAVESTONE);
3230
3231         /* there is time penalty for destroying the voodoo */
3232         time_decrement_sec += cave->time_penalty;
3233       }
3234     }
3235   }
3236
3237   /* this loop finds the coordinates of the player. needed for scrolling and chasing stone.*/
3238   /* but we only do this, if a living player was found. if not yet, the setup
3239      routine coordinates are used */
3240   if (cave->player_state==GD_PL_LIVING)
3241   {
3242     if (cave->active_is_first_found)
3243     {
3244       /* to be 1stb compatible, we do everything backwards. */
3245       for (y = cave->h - 1; y >= 0; y--)
3246       {
3247         for (x = cave->w - 1; x >= 0; x--)
3248         {
3249           if (is_player(cave, x, y))
3250           {
3251             /* here we remember the coordinates. */
3252             cave->player_x = x;
3253             cave->player_y = y;
3254           }
3255         }
3256       }
3257     }
3258     else
3259     {
3260       /* as in the original: look for the last one */
3261       for (y = 0; y < cave->h; y++)
3262       {
3263         for (x = 0; x < cave->w; x++)
3264         {
3265           if (is_player(cave, x, y))
3266           {
3267             /* here we remember the coordinates. */
3268             cave->player_x = x;
3269             cave->player_y = y;
3270           }
3271         }
3272       }
3273     }
3274   }
3275
3276   /* record coordinates of player for chasing stone */
3277   for (i = 0; i < G_N_ELEMENTS(cave->px) - 1; i++)
3278   {
3279     cave->px[i] = cave->px[i + 1];
3280     cave->py[i] = cave->py[i + 1];
3281   }
3282
3283   cave->px[G_N_ELEMENTS(cave->px) - 1] = cave->player_x;
3284   cave->py[G_N_ELEMENTS(cave->py) - 1] = cave->player_y;
3285
3286   /* SCHEDULING */
3287
3288   /* update timing calculated by iterating and counting elements */
3289   update_cave_speed(cave);
3290
3291   /* cave 3 sounds. precedence is controlled by the sound_play function. */
3292   /* but we have to check amoeba&magic together as they had a different gritty sound when mixed */
3293   if (found_water && cave->water_sound)
3294     gd_sound_play(cave, GD_S_WATER, O_WATER, -1, -1);
3295
3296   magic_sound = (cave->magic_wall_state == GD_MW_ACTIVE &&
3297                  cave->magic_wall_sound);
3298
3299   amoeba_sound = (cave->hatched && cave->amoeba_sound &&
3300                   ((amoeba_count > 0 && cave->amoeba_state == GD_AM_AWAKE) ||
3301                    (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE)));
3302
3303   if (amoeba_sound && magic_sound)
3304   {
3305     gd_sound_play(cave, GD_S_AMOEBA_MAGIC, O_AMOEBA, -1, -1);
3306   }
3307   else
3308   {
3309     if (amoeba_sound)
3310       gd_sound_play(cave, GD_S_AMOEBA, O_AMOEBA, -1, -1);
3311     else if (magic_sound)
3312       gd_sound_play(cave, GD_S_MAGIC_WALL, O_MAGIC_WALL, -1, -1);
3313   }
3314
3315   if (cave->hatched)
3316   {
3317     if ((amoeba_count   > 0 && cave->amoeba_state   == GD_AM_AWAKE) ||
3318         (amoeba_2_count > 0 && cave->amoeba_2_state == GD_AM_AWAKE))
3319       play_sound_of_element(cave, O_AMOEBA, x, y);
3320   }
3321
3322   /* pneumatic hammer sound - overrides everything. */
3323   if (cave->pneumatic_hammer_active_delay > 0 && cave->pneumatic_hammer_sound)
3324     gd_sound_play(cave, GD_S_PNEUMATIC_HAMMER, O_PNEUMATIC_HAMMER, -1, -1);
3325
3326   /* CAVE VARIABLES */
3327
3328   /* PLAYER */
3329
3330   /* check if player is alive. */
3331   if ((cave->player_state == GD_PL_LIVING && cave->player_seen_ago > 15) || cave->kill_player)
3332     cave->player_state = GD_PL_DIED;
3333
3334   /* check if any voodoo exploded, and kill players the next scan if that happended. */
3335   if (cave->voodoo_touched)
3336     cave->kill_player = TRUE;
3337
3338   /* AMOEBA */
3339
3340   /* check flags after evaluating. */
3341   if (cave->amoeba_state == GD_AM_AWAKE)
3342   {
3343     if (amoeba_count >= cave->amoeba_max_count)
3344       cave->amoeba_state = GD_AM_TOO_BIG;
3345     if (amoeba_found_enclosed)
3346       cave->amoeba_state = GD_AM_ENCLOSED;
3347     }
3348
3349   /* amoeba can also be turned into diamond by magic wall */
3350   if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3351     cave->amoeba_state = GD_AM_ENCLOSED;
3352
3353   /* AMOEBA 2 */
3354   if (cave->amoeba_2_state == GD_AM_AWAKE)
3355   {
3356     /* check flags after evaluating. */
3357     if (amoeba_2_count >= cave->amoeba_2_max_count)
3358       cave->amoeba_2_state = GD_AM_TOO_BIG;
3359
3360     if (amoeba_2_found_enclosed)
3361       cave->amoeba_2_state = GD_AM_ENCLOSED;
3362   }
3363
3364   /* amoeba 2 can also be turned into diamond by magic wall */
3365   if (cave->magic_wall_stops_amoeba && cave->magic_wall_state == GD_MW_ACTIVE)
3366     cave->amoeba_2_state = GD_AM_ENCLOSED;
3367
3368   /* now check times. --------------------------- */
3369   /* decrement time if a voodoo was killed. */
3370   cave->time -= time_decrement_sec * cave->timing_factor;
3371   if (cave->time < 0)
3372     cave->time = 0;
3373
3374   /* only decrement time when player is already born. */
3375   if (cave->hatched)
3376   {
3377     int secondsbefore, secondsafter;
3378
3379     secondsbefore = cave->time / cave->timing_factor;
3380     cave->time -= cave->speed;
3381     if (cave->time <= 0)
3382       cave->time = 0;
3383
3384     secondsafter = cave->time / cave->timing_factor;
3385     if (cave->time / cave->timing_factor < 10)
3386       /* if less than 10 seconds, no walking sound, but play explosion sound */
3387       gd_sound_play(cave, GD_S_NONE, O_NONE, -1, -1);
3388
3389     if (secondsbefore != secondsafter)
3390       gd_cave_set_seconds_sound(cave);
3391   }
3392
3393   /* a gravity switch was activated; seconds counting down */
3394   if (cave->gravity_will_change > 0)
3395   {
3396     cave->gravity_will_change -= cave->speed;
3397     if (cave->gravity_will_change < 0)
3398       cave->gravity_will_change = 0;
3399
3400     if (cave->gravity_will_change == 0)
3401     {
3402       cave->gravity = cave->gravity_next_direction;
3403       if (cave->gravity_change_sound)
3404         gd_sound_play(cave, GD_S_GRAVITY_CHANGE, O_GRAVITY_SWITCH, -1, -1);    /* takes precedence over amoeba and magic wall sound */
3405     }
3406   }
3407
3408   /* creatures direction automatically change */
3409   if (cave->creatures_direction_will_change > 0)
3410   {
3411     cave->creatures_direction_will_change -= cave->speed;
3412     if (cave->creatures_direction_will_change < 0)
3413       cave->creatures_direction_will_change = 0;
3414
3415     if (cave->creatures_direction_will_change == 0)
3416     {
3417       if (cave->creature_direction_auto_change_sound)
3418         gd_sound_play(cave, GD_S_SWITCH_CREATURES, O_CREATURE_SWITCH, -1, -1);
3419
3420       cave->creatures_backwards = !cave->creatures_backwards;
3421       cave->creatures_direction_will_change =
3422         cave->creatures_direction_auto_change_time * cave->timing_factor;
3423     }
3424   }
3425
3426   /* magic wall; if active&wait or not wait for hatching */
3427   if (cave->magic_wall_state == GD_MW_ACTIVE &&
3428       (cave->hatched || !cave->magic_timer_wait_for_hatching))
3429   {
3430     cave->magic_wall_time -= cave->speed;
3431     if (cave->magic_wall_time < 0)
3432       cave->magic_wall_time = 0;
3433     if (cave->magic_wall_time == 0)
3434       cave->magic_wall_state = GD_MW_EXPIRED;
3435   }
3436
3437   /* we may wait for hatching, when starting amoeba */
3438   if (cave->amoeba_timer_started_immediately ||
3439       (cave->amoeba_state == GD_AM_AWAKE &&
3440        (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3441   {
3442     cave->amoeba_time -= cave->speed;
3443     if (cave->amoeba_time < 0)
3444       cave->amoeba_time = 0;
3445     if (cave->amoeba_time == 0)
3446       cave->amoeba_growth_prob = cave->amoeba_fast_growth_prob;
3447   }
3448
3449   /* we may wait for hatching, when starting amoeba */
3450   if (cave->amoeba_timer_started_immediately ||
3451       (cave->amoeba_2_state == GD_AM_AWAKE &&
3452        (cave->hatched || !cave->amoeba_timer_wait_for_hatching)))
3453   {
3454     cave->amoeba_2_time -= cave->speed;
3455     if (cave->amoeba_2_time < 0)
3456       cave->amoeba_2_time = 0;
3457     if (cave->amoeba_2_time == 0)
3458       cave->amoeba_2_growth_prob = cave->amoeba_2_fast_growth_prob;
3459   }
3460
3461   /* check for player hatching. */
3462   start_signal = FALSE;
3463
3464   /* if not the c64 scheduling, but the correct frametime is used,
3465      hatching delay should always be decremented. */
3466   /* otherwise, the if (millisecs...) condition below will set this. */
3467   if (cave->scheduling == GD_SCHEDULING_MILLISECONDS)
3468   {
3469     /* NON-C64 scheduling */
3470     if (cave->hatching_delay_frame > 0)
3471     {
3472       /* for milliseconds-based, non-c64 schedulings, hatching delay means frames. */
3473       cave->hatching_delay_frame--;
3474       if (cave->hatching_delay_frame == 0)
3475         start_signal = TRUE;
3476     }
3477   }
3478   else
3479   {
3480     /* C64 scheduling */
3481     if (cave->hatching_delay_time > 0)
3482     {
3483       /* for c64 schedulings, hatching delay means milliseconds. */
3484       cave->hatching_delay_time -= cave->speed;
3485       if (cave->hatching_delay_time <= 0)
3486       {
3487         cave->hatching_delay_time = 0;
3488         start_signal = TRUE;
3489       }
3490     }
3491   }
3492
3493   /* if decremented hatching, and it became zero: */
3494   if (start_signal)
3495   {
3496     /* THIS IS THE CAVE START SIGNAL */
3497
3498     /* record that now the cave is in its normal state */
3499     cave->hatched = TRUE;
3500
3501     /* if diamonds needed is below zero, we count the available diamonds now. */
3502     gd_cave_count_diamonds(cave);
3503
3504     /* setup direction auto change */
3505     if (cave->creatures_direction_auto_change_time)
3506     {
3507       cave->creatures_direction_will_change =
3508         cave->creatures_direction_auto_change_time * cave->timing_factor;
3509
3510       if (cave->creatures_direction_auto_change_on_start)
3511         cave->creatures_backwards = !cave->creatures_backwards;
3512     }
3513
3514     gd_sound_play(cave, GD_S_CRACK, O_INBOX, -1, -1);
3515   }
3516
3517   /* for biters */
3518   if (cave->biters_wait_frame == 0)
3519     cave->biters_wait_frame = cave->biter_delay_frame;
3520   else
3521     cave->biters_wait_frame--;
3522
3523   /* replicators delay */
3524   if (cave->replicators_wait_frame == 0)
3525     cave->replicators_wait_frame = cave->replicator_delay_frame;
3526   else
3527     cave->replicators_wait_frame--;
3528
3529   /* LAST THOUGTS */
3530
3531 #if 1
3532   /* check if cave failed by timeout is done in main game engine */
3533 #else
3534   /* check if cave failed by timeout */
3535   if (cave->player_state == GD_PL_LIVING && cave->time == 0)
3536   {
3537     gd_cave_clear_sounds(cave);
3538     cave->player_state = GD_PL_TIMEOUT;
3539     gd_sound_play(cave, GD_S_TIMEOUT_0, O_NONE, -1, -1);
3540   }
3541 #endif
3542
3543   /* set these for drawing. */
3544   cave->last_direction = player_move;
3545   /* here we remember last movements for animation. this is needed here,
3546      as animation is in sync with the game, not the keyboard directly.
3547      (for example, after exiting the cave, the player was "running" in the
3548      original, till bonus points were counted for remaining time and so on. */
3549   if (player_move == GD_MV_LEFT ||
3550       player_move == GD_MV_UP_LEFT ||
3551       player_move == GD_MV_DOWN_LEFT)
3552     cave->last_horizontal_direction = GD_MV_LEFT;
3553
3554   if (player_move == GD_MV_RIGHT ||
3555       player_move == GD_MV_UP_RIGHT ||
3556       player_move == GD_MV_DOWN_RIGHT)
3557     cave->last_horizontal_direction = GD_MV_RIGHT;
3558
3559   cave->frame++;  /* XXX */
3560 }
3561
3562 void set_initial_cave_speed(GdCave *cave)
3563 {
3564   int ymin, ymax;
3565   int x, y;
3566
3567   /* set cave get function; to implement perfect or lineshifting borders */
3568   if (cave->lineshift)
3569     cave->getp = getp_shift;
3570   else
3571     cave->getp = getp_perfect;
3572
3573   /* check whether to scan the first and last line */
3574   if (cave->border_scan_first_and_last)
3575   {
3576     ymin = 0;
3577     ymax = cave->h - 1;
3578   }
3579   else
3580   {
3581     ymin = 1;
3582     ymax = cave->h - 2;
3583   }
3584
3585   for (y = ymin; y <= ymax; y++)
3586   {
3587     for (x = 0; x < cave->w; x++)
3588     {
3589       /* add the ckdelay correction value for every element seen. */
3590       cave->ckdelay += gd_elements[get(cave, x, y)].ckdelay;
3591     }
3592   }
3593
3594   /* update timing calculated by iterating and counting elements */
3595   update_cave_speed(cave);
3596 }