rnd-20040309-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MovePlayer() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollPlayer() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_NO_EXPLOSION         0
47 #define EX_NORMAL               1
48 #define EX_CENTER               2
49 #define EX_BORDER               3
50
51 /* special positions in the game control window (relative to control window) */
52 #define XX_LEVEL                37
53 #define YY_LEVEL                20
54 #define XX_EMERALDS             29
55 #define YY_EMERALDS             54
56 #define XX_DYNAMITE             29
57 #define YY_DYNAMITE             89
58 #define XX_KEYS                 18
59 #define YY_KEYS                 123
60 #define XX_SCORE                15
61 #define YY_SCORE                159
62 #define XX_TIME1                29
63 #define XX_TIME2                30
64 #define YY_TIME                 194
65
66 /* special positions in the game control window (relative to main window) */
67 #define DX_LEVEL                (DX + XX_LEVEL)
68 #define DY_LEVEL                (DY + YY_LEVEL)
69 #define DX_EMERALDS             (DX + XX_EMERALDS)
70 #define DY_EMERALDS             (DY + YY_EMERALDS)
71 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
72 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
73 #define DX_KEYS                 (DX + XX_KEYS)
74 #define DY_KEYS                 (DY + YY_KEYS)
75 #define DX_SCORE                (DX + XX_SCORE)
76 #define DY_SCORE                (DY + YY_SCORE)
77 #define DX_TIME1                (DX + XX_TIME1)
78 #define DX_TIME2                (DX + XX_TIME2)
79 #define DY_TIME                 (DY + YY_TIME)
80
81 /* values for initial player move delay (initial delay counter value) */
82 #define INITIAL_MOVE_DELAY_OFF  -1
83 #define INITIAL_MOVE_DELAY_ON   0
84
85 /* values for player movement speed (which is in fact a delay value) */
86 #define MOVE_DELAY_NORMAL_SPEED 8
87 #define MOVE_DELAY_HIGH_SPEED   4
88
89 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
90 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
91 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
92 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
93
94 /* values for other actions */
95 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
96
97 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
98
99 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
100                                  RND(element_info[e].push_delay_random))
101 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
102                                  RND(element_info[e].move_delay_random))
103 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
104                                     (element_info[e].move_delay_random))
105
106 #define ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, condition)                \
107                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
108                                         (CAN_MOVE_INTO_ACID(e) &&       \
109                                          Feld[x][y] == EL_ACID) ||      \
110                                         (condition)))
111
112 #if 0
113 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
114                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
115                                         (condition) ||                  \
116                                         (DONT_COLLIDE_WITH(e) &&        \
117                                          IS_PLAYER(x, y) &&             \
118                                          !PLAYER_ENEMY_PROTECTED(x, y))))
119 #else
120 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
121                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
122                                         (condition) ||                  \
123                                         (CAN_MOVE_INTO_ACID(e) &&       \
124                                          Feld[x][y] == EL_ACID) ||      \
125                                         (DONT_COLLIDE_WITH(e) &&        \
126                                          IS_PLAYER(x, y) &&             \
127                                          !PLAYER_ENEMY_PROTECTED(x, y))))
128 #endif
129
130 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)              \
131                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
132                                         (condition)))
133
134 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
135         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
136
137 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                        \
138         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
139
140 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                         \
141         ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
142
143 #if 0
144 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
145 #else
146 #define ENEMY_CAN_ENTER_FIELD(e, x, y) ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, 0)
147 #endif
148
149 #define YAMYAM_CAN_ENTER_FIELD(x, y)                                    \
150                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
151                                         (CAN_MOVE_INTO_ACID(EL_YAMYAM) && \
152                                          Feld[x][y] == EL_ACID) ||      \
153                                         Feld[x][y] == EL_DIAMOND))
154
155 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                               \
156                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
157                                         (CAN_MOVE_INTO_ACID(EL_DARK_YAMYAM) &&\
158                                          Feld[x][y] == EL_ACID) ||      \
159                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
160
161 #define PACMAN_CAN_ENTER_FIELD(x, y)                                    \
162                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
163                                         (CAN_MOVE_INTO_ACID(EL_PACMAN) && \
164                                          Feld[x][y] == EL_ACID) ||      \
165                                         IS_AMOEBOID(Feld[x][y])))
166
167 #define PIG_CAN_ENTER_FIELD(x, y)                                       \
168                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
169                                         (CAN_MOVE_INTO_ACID(EL_PIG) &&  \
170                                          Feld[x][y] == EL_ACID) ||      \
171                                         IS_FOOD_PIG(Feld[x][y])))
172
173 #define PENGUIN_CAN_ENTER_FIELD(x, y)                                   \
174                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
175                                         (CAN_MOVE_INTO_ACID(EL_PENGUIN) && \
176                                          Feld[x][y] == EL_ACID) ||      \
177                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
178                                         Feld[x][y] == EL_EXIT_OPEN))
179
180 #define DRAGON_CAN_ENTER_FIELD(x, y)                                    \
181                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
182                                         (CAN_MOVE_INTO_ACID(EL_DRAGON) && \
183                                          Feld[x][y] == EL_ACID)))
184
185 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                           \
186                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
187                                         (CAN_MOVE_INTO_ACID(EL_MOLE) && \
188                                          Feld[x][y] == EL_ACID) ||      \
189                                         (condition)))
190
191 #define SPRING_CAN_ENTER_FIELD(x, y)                                    \
192                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
193                                         (CAN_MOVE_INTO_ACID(EL_SPRING) && \
194                                          Feld[x][y] == EL_ACID)))
195
196 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
197 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
198 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
199 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
200
201 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
202         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
203
204 #if 0
205 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
206                 (!IS_PLAYER(x, y) &&                                    \
207                  (Feld[x][y] == EL_ACID ||                              \
208                   IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
209 #else
210 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
211                 (!IS_PLAYER(x, y) &&                                    \
212                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
213 #endif
214
215 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
216         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
217
218 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
219 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
220
221 /* game button identifiers */
222 #define GAME_CTRL_ID_STOP               0
223 #define GAME_CTRL_ID_PAUSE              1
224 #define GAME_CTRL_ID_PLAY               2
225 #define SOUND_CTRL_ID_MUSIC             3
226 #define SOUND_CTRL_ID_LOOPS             4
227 #define SOUND_CTRL_ID_SIMPLE            5
228
229 #define NUM_GAME_BUTTONS                6
230
231
232 /* forward declaration for internal use */
233
234 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
235 static boolean MovePlayer(struct PlayerInfo *, int, int);
236 static void ScrollPlayer(struct PlayerInfo *, int);
237 static void ScrollScreen(struct PlayerInfo *, int);
238
239 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
240
241 static void InitBeltMovement(void);
242 static void CloseAllOpenTimegates(void);
243 static void CheckGravityMovement(struct PlayerInfo *);
244 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
245 static void KillHeroUnlessEnemyProtected(int, int);
246 static void KillHeroUnlessExplosionProtected(int, int);
247
248 static void TestIfPlayerTouchesCustomElement(int, int);
249 static void TestIfElementTouchesCustomElement(int, int);
250 static void TestIfElementHitsCustomElement(int, int, int);
251 #if 0
252 static void TestIfElementSmashesCustomElement(int, int, int);
253 #endif
254
255 static void ChangeElement(int, int, int);
256
257 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
258 #define CheckTriggeredElementChange(x, y, e, ev)                        \
259         CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
260 #define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s)            \
261         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
262 #define CheckTriggeredElementChangeSide(x, y, e, ev, s)                 \
263         CheckTriggeredElementChangeExt(x, y, e, ev, -1, s, -1)
264 #define CheckTriggeredElementChangePage(x, y, e, ev, p)                 \
265         CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
266
267 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
268 #define CheckElementChange(x, y, e, ev)                                 \
269         CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
270 #define CheckElementChangePlayer(x, y, e, ev, p, s)                     \
271         CheckElementChangeExt(x, y, e, ev, p, s, -1)
272 #define CheckElementChangeSide(x, y, e, ev, s)                          \
273         CheckElementChangeExt(x, y, e, ev, -1, s, -1)
274 #define CheckElementChangePage(x, y, e, ev, p)                          \
275         CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
276
277 static void PlayLevelSound(int, int, int);
278 static void PlayLevelSoundNearest(int, int, int);
279 static void PlayLevelSoundAction(int, int, int);
280 static void PlayLevelSoundElementAction(int, int, int, int);
281 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
282 static void PlayLevelSoundActionIfLoop(int, int, int);
283 static void StopLevelSoundActionIfLoop(int, int, int);
284 static void PlayLevelMusic();
285
286 static void MapGameButtons();
287 static void HandleGameButtons(struct GadgetInfo *);
288
289 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
290
291
292 /* ------------------------------------------------------------------------- */
293 /* definition of elements that automatically change to other elements after  */
294 /* a specified time, eventually calling a function when changing             */
295 /* ------------------------------------------------------------------------- */
296
297 /* forward declaration for changer functions */
298 static void InitBuggyBase(int x, int y);
299 static void WarnBuggyBase(int x, int y);
300
301 static void InitTrap(int x, int y);
302 static void ActivateTrap(int x, int y);
303 static void ChangeActiveTrap(int x, int y);
304
305 static void InitRobotWheel(int x, int y);
306 static void RunRobotWheel(int x, int y);
307 static void StopRobotWheel(int x, int y);
308
309 static void InitTimegateWheel(int x, int y);
310 static void RunTimegateWheel(int x, int y);
311
312 struct ChangingElementInfo
313 {
314   int element;
315   int target_element;
316   int change_delay;
317   void (*pre_change_function)(int x, int y);
318   void (*change_function)(int x, int y);
319   void (*post_change_function)(int x, int y);
320 };
321
322 static struct ChangingElementInfo change_delay_list[] =
323 {
324   {
325     EL_NUT_BREAKING,
326     EL_EMERALD,
327     6,
328     NULL,
329     NULL,
330     NULL
331   },
332   {
333     EL_PEARL_BREAKING,
334     EL_EMPTY,
335     8,
336     NULL,
337     NULL,
338     NULL
339   },
340   {
341     EL_EXIT_OPENING,
342     EL_EXIT_OPEN,
343     29,
344     NULL,
345     NULL,
346     NULL
347   },
348   {
349     EL_EXIT_CLOSING,
350     EL_EXIT_CLOSED,
351     29,
352     NULL,
353     NULL,
354     NULL
355   },
356   {
357     EL_SP_EXIT_OPENING,
358     EL_SP_EXIT_OPEN,
359     29,
360     NULL,
361     NULL,
362     NULL
363   },
364   {
365     EL_SP_EXIT_CLOSING,
366     EL_SP_EXIT_CLOSED,
367     29,
368     NULL,
369     NULL,
370     NULL
371   },
372   {
373     EL_SWITCHGATE_OPENING,
374     EL_SWITCHGATE_OPEN,
375     29,
376     NULL,
377     NULL,
378     NULL
379   },
380   {
381     EL_SWITCHGATE_CLOSING,
382     EL_SWITCHGATE_CLOSED,
383     29,
384     NULL,
385     NULL,
386     NULL
387   },
388   {
389     EL_TIMEGATE_OPENING,
390     EL_TIMEGATE_OPEN,
391     29,
392     NULL,
393     NULL,
394     NULL
395   },
396   {
397     EL_TIMEGATE_CLOSING,
398     EL_TIMEGATE_CLOSED,
399     29,
400     NULL,
401     NULL,
402     NULL
403   },
404
405   {
406     EL_ACID_SPLASH_LEFT,
407     EL_EMPTY,
408     8,
409     NULL,
410     NULL,
411     NULL
412   },
413   {
414     EL_ACID_SPLASH_RIGHT,
415     EL_EMPTY,
416     8,
417     NULL,
418     NULL,
419     NULL
420   },
421   {
422     EL_SP_BUGGY_BASE,
423     EL_SP_BUGGY_BASE_ACTIVATING,
424     0,
425     InitBuggyBase,
426     NULL,
427     NULL
428   },
429   {
430     EL_SP_BUGGY_BASE_ACTIVATING,
431     EL_SP_BUGGY_BASE_ACTIVE,
432     0,
433     InitBuggyBase,
434     NULL,
435     NULL
436   },
437   {
438     EL_SP_BUGGY_BASE_ACTIVE,
439     EL_SP_BUGGY_BASE,
440     0,
441     InitBuggyBase,
442     WarnBuggyBase,
443     NULL
444   },
445   {
446     EL_TRAP,
447     EL_TRAP_ACTIVE,
448     0,
449     InitTrap,
450     NULL,
451     ActivateTrap
452   },
453   {
454     EL_TRAP_ACTIVE,
455     EL_TRAP,
456     31,
457     NULL,
458     ChangeActiveTrap,
459     NULL
460   },
461   {
462     EL_ROBOT_WHEEL_ACTIVE,
463     EL_ROBOT_WHEEL,
464     0,
465     InitRobotWheel,
466     RunRobotWheel,
467     StopRobotWheel
468   },
469   {
470     EL_TIMEGATE_SWITCH_ACTIVE,
471     EL_TIMEGATE_SWITCH,
472     0,
473     InitTimegateWheel,
474     RunTimegateWheel,
475     NULL
476   },
477
478   {
479     EL_UNDEFINED,
480     EL_UNDEFINED,
481     -1,
482     NULL,
483     NULL,
484     NULL
485   }
486 };
487
488 struct
489 {
490   int element;
491   int push_delay_fixed, push_delay_random;
492 }
493 push_delay_list[] =
494 {
495   { EL_SPRING,                  0, 0 },
496   { EL_BALLOON,                 0, 0 },
497
498   { EL_SOKOBAN_OBJECT,          2, 0 },
499   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
500   { EL_SATELLITE,               2, 0 },
501   { EL_SP_DISK_YELLOW,          2, 0 },
502
503   { EL_UNDEFINED,               0, 0 },
504 };
505
506 struct
507 {
508   int element;
509   int move_stepsize;
510 }
511 move_stepsize_list[] =
512 {
513   { EL_AMOEBA_DROP,             2 },
514   { EL_AMOEBA_DROPPING,         2 },
515   { EL_QUICKSAND_FILLING,       1 },
516   { EL_QUICKSAND_EMPTYING,      1 },
517   { EL_MAGIC_WALL_FILLING,      2 },
518   { EL_BD_MAGIC_WALL_FILLING,   2 },
519   { EL_MAGIC_WALL_EMPTYING,     2 },
520   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
521
522   { EL_UNDEFINED,               0 },
523 };
524
525 struct
526 {
527   int element;
528   int count;
529 }
530 collect_count_list[] =
531 {
532   { EL_EMERALD,                 1 },
533   { EL_BD_DIAMOND,              1 },
534   { EL_EMERALD_YELLOW,          1 },
535   { EL_EMERALD_RED,             1 },
536   { EL_EMERALD_PURPLE,          1 },
537   { EL_DIAMOND,                 3 },
538   { EL_SP_INFOTRON,             1 },
539   { EL_PEARL,                   5 },
540   { EL_CRYSTAL,                 8 },
541
542   { EL_UNDEFINED,               0 },
543 };
544
545 struct
546 {
547   int element;
548   int direction;
549 }
550 tube_access[] =
551 {
552   { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
553   { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
554   { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
555   { EL_TUBE_VERTICAL_LEFT,      MV_LEFT |            MV_UP | MV_DOWN },
556   { EL_TUBE_VERTICAL_RIGHT,               MV_RIGHT | MV_UP | MV_DOWN },
557   { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT | MV_UP           },
558   { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT |         MV_DOWN },
559   { EL_TUBE_LEFT_UP,            MV_LEFT |            MV_UP           },
560   { EL_TUBE_LEFT_DOWN,          MV_LEFT |                    MV_DOWN },
561   { EL_TUBE_RIGHT_UP,                     MV_RIGHT | MV_UP           },
562   { EL_TUBE_RIGHT_DOWN,                   MV_RIGHT |         MV_DOWN },
563
564   { EL_UNDEFINED,               0                                    }
565 };
566
567 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
568
569 #define IS_AUTO_CHANGING(e)     (element_info[e].change_events & \
570                                  CH_EVENT_BIT(CE_DELAY))
571 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
572 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
573                                  IS_JUST_CHANGING(x, y))
574
575 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
576
577
578 void GetPlayerConfig()
579 {
580   if (!audio.sound_available)
581     setup.sound_simple = FALSE;
582
583   if (!audio.loops_available)
584     setup.sound_loops = FALSE;
585
586   if (!audio.music_available)
587     setup.sound_music = FALSE;
588
589   if (!video.fullscreen_available)
590     setup.fullscreen = FALSE;
591
592   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
593
594   SetAudioMode(setup.sound);
595   InitJoysticks();
596 }
597
598 static int getBeltNrFromBeltElement(int element)
599 {
600   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
601           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
602           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
603 }
604
605 static int getBeltNrFromBeltActiveElement(int element)
606 {
607   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
608           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
609           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
610 }
611
612 static int getBeltNrFromBeltSwitchElement(int element)
613 {
614   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
615           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
616           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
617 }
618
619 static int getBeltDirNrFromBeltSwitchElement(int element)
620 {
621   static int belt_base_element[4] =
622   {
623     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
624     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
625     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
626     EL_CONVEYOR_BELT_4_SWITCH_LEFT
627   };
628
629   int belt_nr = getBeltNrFromBeltSwitchElement(element);
630   int belt_dir_nr = element - belt_base_element[belt_nr];
631
632   return (belt_dir_nr % 3);
633 }
634
635 static int getBeltDirFromBeltSwitchElement(int element)
636 {
637   static int belt_move_dir[3] =
638   {
639     MV_LEFT,
640     MV_NO_MOVING,
641     MV_RIGHT
642   };
643
644   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
645
646   return belt_move_dir[belt_dir_nr];
647 }
648
649 static void InitPlayerField(int x, int y, int element, boolean init_game)
650 {
651   if (element == EL_SP_MURPHY)
652   {
653     if (init_game)
654     {
655       if (stored_player[0].present)
656       {
657         Feld[x][y] = EL_SP_MURPHY_CLONE;
658
659         return;
660       }
661       else
662       {
663         stored_player[0].use_murphy_graphic = TRUE;
664       }
665
666       Feld[x][y] = EL_PLAYER_1;
667     }
668   }
669
670   if (init_game)
671   {
672     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
673     int jx = player->jx, jy = player->jy;
674
675     player->present = TRUE;
676
677     player->block_last_field = (element == EL_SP_MURPHY ?
678                                 level.sp_block_last_field :
679                                 level.block_last_field);
680
681     if (!options.network || player->connected)
682     {
683       player->active = TRUE;
684
685       /* remove potentially duplicate players */
686       if (StorePlayer[jx][jy] == Feld[x][y])
687         StorePlayer[jx][jy] = 0;
688
689       StorePlayer[x][y] = Feld[x][y];
690
691       if (options.debug)
692       {
693         printf("Player %d activated.\n", player->element_nr);
694         printf("[Local player is %d and currently %s.]\n",
695                local_player->element_nr,
696                local_player->active ? "active" : "not active");
697       }
698     }
699
700     Feld[x][y] = EL_EMPTY;
701     player->jx = player->last_jx = x;
702     player->jy = player->last_jy = y;
703   }
704 }
705
706 static void InitField(int x, int y, boolean init_game)
707 {
708   int element = Feld[x][y];
709
710   switch (element)
711   {
712     case EL_SP_MURPHY:
713     case EL_PLAYER_1:
714     case EL_PLAYER_2:
715     case EL_PLAYER_3:
716     case EL_PLAYER_4:
717       InitPlayerField(x, y, element, init_game);
718       break;
719
720     case EL_SOKOBAN_FIELD_PLAYER:
721       element = Feld[x][y] = EL_PLAYER_1;
722       InitField(x, y, init_game);
723
724       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
725       InitField(x, y, init_game);
726       break;
727
728     case EL_SOKOBAN_FIELD_EMPTY:
729       local_player->sokobanfields_still_needed++;
730       break;
731
732     case EL_STONEBLOCK:
733       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
734         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
735       else if (x > 0 && Feld[x-1][y] == EL_ACID)
736         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
737       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
738         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
739       else if (y > 0 && Feld[x][y-1] == EL_ACID)
740         Feld[x][y] = EL_ACID_POOL_BOTTOM;
741       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
742         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
743       break;
744
745     case EL_BUG_RIGHT:
746     case EL_BUG_UP:
747     case EL_BUG_LEFT:
748     case EL_BUG_DOWN:
749     case EL_BUG:
750     case EL_SPACESHIP_RIGHT:
751     case EL_SPACESHIP_UP:
752     case EL_SPACESHIP_LEFT:
753     case EL_SPACESHIP_DOWN:
754     case EL_SPACESHIP:
755     case EL_BD_BUTTERFLY_RIGHT:
756     case EL_BD_BUTTERFLY_UP:
757     case EL_BD_BUTTERFLY_LEFT:
758     case EL_BD_BUTTERFLY_DOWN:
759     case EL_BD_BUTTERFLY:
760     case EL_BD_FIREFLY_RIGHT:
761     case EL_BD_FIREFLY_UP:
762     case EL_BD_FIREFLY_LEFT:
763     case EL_BD_FIREFLY_DOWN:
764     case EL_BD_FIREFLY:
765     case EL_PACMAN_RIGHT:
766     case EL_PACMAN_UP:
767     case EL_PACMAN_LEFT:
768     case EL_PACMAN_DOWN:
769     case EL_YAMYAM:
770     case EL_DARK_YAMYAM:
771     case EL_ROBOT:
772     case EL_PACMAN:
773     case EL_SP_SNIKSNAK:
774     case EL_SP_ELECTRON:
775     case EL_MOLE_LEFT:
776     case EL_MOLE_RIGHT:
777     case EL_MOLE_UP:
778     case EL_MOLE_DOWN:
779     case EL_MOLE:
780       InitMovDir(x, y);
781       break;
782
783     case EL_AMOEBA_FULL:
784     case EL_BD_AMOEBA:
785       InitAmoebaNr(x, y);
786       break;
787
788     case EL_AMOEBA_DROP:
789       if (y == lev_fieldy - 1)
790       {
791         Feld[x][y] = EL_AMOEBA_GROWING;
792         Store[x][y] = EL_AMOEBA_WET;
793       }
794       break;
795
796     case EL_DYNAMITE_ACTIVE:
797     case EL_SP_DISK_RED_ACTIVE:
798     case EL_DYNABOMB_PLAYER_1_ACTIVE:
799     case EL_DYNABOMB_PLAYER_2_ACTIVE:
800     case EL_DYNABOMB_PLAYER_3_ACTIVE:
801     case EL_DYNABOMB_PLAYER_4_ACTIVE:
802       MovDelay[x][y] = 96;
803       break;
804
805     case EL_LAMP:
806       local_player->lights_still_needed++;
807       break;
808
809     case EL_PENGUIN:
810       local_player->friends_still_needed++;
811       break;
812
813     case EL_PIG:
814     case EL_DRAGON:
815       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
816       break;
817
818 #if 0
819     case EL_SP_EMPTY:
820       Feld[x][y] = EL_EMPTY;
821       break;
822 #endif
823
824 #if 0
825     case EL_EM_KEY_1_FILE:
826       Feld[x][y] = EL_EM_KEY_1;
827       break;
828     case EL_EM_KEY_2_FILE:
829       Feld[x][y] = EL_EM_KEY_2;
830       break;
831     case EL_EM_KEY_3_FILE:
832       Feld[x][y] = EL_EM_KEY_3;
833       break;
834     case EL_EM_KEY_4_FILE:
835       Feld[x][y] = EL_EM_KEY_4;
836       break;
837 #endif
838
839     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
840     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
841     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
842     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
843     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
844     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
845     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
846     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
847     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
848     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
849     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
850     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
851       if (init_game)
852       {
853         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
854         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
855         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
856
857         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
858         {
859           game.belt_dir[belt_nr] = belt_dir;
860           game.belt_dir_nr[belt_nr] = belt_dir_nr;
861         }
862         else    /* more than one switch -- set it like the first switch */
863         {
864           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
865         }
866       }
867       break;
868
869     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
870       if (init_game)
871         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
872       break;
873
874     case EL_LIGHT_SWITCH_ACTIVE:
875       if (init_game)
876         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
877       break;
878
879     default:
880       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
881         InitMovDir(x, y);
882       else if (IS_GROUP_ELEMENT(element))
883       {
884         struct ElementGroupInfo *group = element_info[element].group;
885         int last_anim_random_frame = gfx.anim_random_frame;
886         int element_pos;
887
888         if (group->choice_mode == ANIM_RANDOM)
889           gfx.anim_random_frame = RND(group->num_elements_resolved);
890
891         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
892                                         group->choice_mode, 0,
893                                         group->choice_pos);
894
895         if (group->choice_mode == ANIM_RANDOM)
896           gfx.anim_random_frame = last_anim_random_frame;
897
898         group->choice_pos++;
899
900         Feld[x][y] = group->element_resolved[element_pos];
901
902         InitField(x, y, init_game);
903       }
904       break;
905   }
906 }
907
908 static inline void InitField_WithBug1(int x, int y, boolean init_game)
909 {
910   InitField(x, y, init_game);
911
912   /* not needed to call InitMovDir() -- already done by InitField()! */
913   if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
914       CAN_MOVE(Feld[x][y]))
915     InitMovDir(x, y);
916 }
917
918 static inline void InitField_WithBug2(int x, int y, boolean init_game)
919 {
920   int old_element = Feld[x][y];
921
922   InitField(x, y, init_game);
923
924   /* not needed to call InitMovDir() -- already done by InitField()! */
925   if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
926       CAN_MOVE(old_element) &&
927       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
928     InitMovDir(x, y);
929
930   /* this case is in fact a combination of not less than three bugs:
931      first, it calls InitMovDir() for elements that can move, although this is
932      already done by InitField(); then, it checks the element that was at this
933      field _before_ the call to InitField() (which can change it)
934
935  */
936 }
937
938 inline void DrawGameValue_Emeralds(int value)
939 {
940   DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
941 }
942
943 inline void DrawGameValue_Dynamite(int value)
944 {
945   DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
946 }
947
948 inline void DrawGameValue_Keys(struct PlayerInfo *player)
949 {
950   int i;
951
952   for (i = 0; i < MAX_KEYS; i++)
953     if (player->key[i])
954       DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
955                          el2edimg(EL_KEY_1 + i));
956 }
957
958 inline void DrawGameValue_Score(int value)
959 {
960   DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
961 }
962
963 inline void DrawGameValue_Time(int value)
964 {
965   if (value < 1000)
966     DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
967   else
968     DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
969 }
970
971 inline void DrawGameValue_Level(int value)
972 {
973   if (level_nr < 100)
974     DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
975   else
976   {
977     /* misuse area for displaying emeralds to draw bigger level number */
978     DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
979                 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
980
981     /* now copy it to the area for displaying level number */
982     BlitBitmap(drawto, drawto,
983                DX_EMERALDS, DY_EMERALDS + 1,
984                getFontWidth(FONT_LEVEL_NUMBER) * 3,
985                getFontHeight(FONT_LEVEL_NUMBER) - 1,
986                DX_LEVEL - 1, DY_LEVEL + 1);
987
988     /* restore the area for displaying emeralds */
989     DrawGameValue_Emeralds(local_player->gems_still_needed);
990
991     /* yes, this is all really ugly :-) */
992   }
993 }
994
995 void DrawGameDoorValues()
996 {
997   int i;
998
999   DrawGameValue_Level(level_nr);
1000
1001   for (i = 0; i < MAX_PLAYERS; i++)
1002     DrawGameValue_Keys(&stored_player[i]);
1003
1004   DrawGameValue_Emeralds(local_player->gems_still_needed);
1005   DrawGameValue_Dynamite(local_player->inventory_size);
1006   DrawGameValue_Score(local_player->score);
1007   DrawGameValue_Time(TimeLeft);
1008 }
1009
1010 static void resolve_group_element(int group_element, int recursion_depth)
1011 {
1012   static int group_nr;
1013   static struct ElementGroupInfo *group;
1014   struct ElementGroupInfo *actual_group = element_info[group_element].group;
1015   int i;
1016
1017   if (recursion_depth > NUM_GROUP_ELEMENTS)     /* recursion too deep */
1018   {
1019     Error(ERR_WARN, "recursion too deep when resolving group element %d",
1020           group_element - EL_GROUP_START + 1);
1021
1022     /* replace element which caused too deep recursion by question mark */
1023     group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1024
1025     return;
1026   }
1027
1028   if (recursion_depth == 0)                     /* initialization */
1029   {
1030     group = element_info[group_element].group;
1031     group_nr = group_element - EL_GROUP_START;
1032
1033     group->num_elements_resolved = 0;
1034     group->choice_pos = 0;
1035   }
1036
1037   for (i = 0; i < actual_group->num_elements; i++)
1038   {
1039     int element = actual_group->element[i];
1040
1041     if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1042       break;
1043
1044     if (IS_GROUP_ELEMENT(element))
1045       resolve_group_element(element, recursion_depth + 1);
1046     else
1047     {
1048       group->element_resolved[group->num_elements_resolved++] = element;
1049       element_info[element].in_group[group_nr] = TRUE;
1050     }
1051   }
1052
1053 #if 0
1054   if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1055   {
1056     printf("::: group %d: %d resolved elements\n",
1057            group_element - EL_GROUP_START, group->num_elements_resolved);
1058     for (i = 0; i < group->num_elements_resolved; i++)
1059       printf("::: - %d ['%s']\n", group->element_resolved[i],
1060              element_info[group->element_resolved[i]].token_name);
1061   }
1062 #endif
1063 }
1064
1065
1066 /*
1067   =============================================================================
1068   InitGameEngine()
1069   -----------------------------------------------------------------------------
1070   initialize game engine due to level / tape version number
1071   =============================================================================
1072 */
1073
1074 static void InitGameEngine()
1075 {
1076   int i, j, k;
1077
1078   /* set game engine from tape file when re-playing, else from level file */
1079   game.engine_version = (tape.playing ? tape.engine_version :
1080                          level.game_version);
1081
1082   /* dynamically adjust element properties according to game engine version */
1083   InitElementPropertiesEngine(game.engine_version);
1084
1085 #if 0
1086   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1087   printf("          tape version == %06d [%s] [file: %06d]\n",
1088          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1089          tape.file_version);
1090   printf("       => game.engine_version == %06d\n", game.engine_version);
1091 #endif
1092
1093   /* ---------- recursively resolve group elements ------------------------- */
1094
1095   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1096     for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1097       element_info[i].in_group[j] = FALSE;
1098
1099   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1100     resolve_group_element(EL_GROUP_START + i, 0);
1101
1102   /* ---------- initialize player's initial move delay --------------------- */
1103
1104   /* dynamically adjust player properties according to game engine version */
1105   game.initial_move_delay =
1106     (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1107      INITIAL_MOVE_DELAY_OFF);
1108
1109   /* dynamically adjust player properties according to level information */
1110   game.initial_move_delay_value =
1111     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1112
1113   /* ---------- initialize player's initial push delay --------------------- */
1114
1115   /* dynamically adjust player properties according to game engine version */
1116   game.initial_push_delay_value =
1117     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1118
1119   /* ---------- initialize changing elements ------------------------------- */
1120
1121   /* initialize changing elements information */
1122   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1123   {
1124     struct ElementInfo *ei = &element_info[i];
1125
1126     /* this pointer might have been changed in the level editor */
1127     ei->change = &ei->change_page[0];
1128
1129     if (!IS_CUSTOM_ELEMENT(i))
1130     {
1131       ei->change->target_element = EL_EMPTY_SPACE;
1132       ei->change->delay_fixed = 0;
1133       ei->change->delay_random = 0;
1134       ei->change->delay_frames = 1;
1135     }
1136
1137     ei->change_events = CE_BITMASK_DEFAULT;
1138     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1139     {
1140       ei->event_page_nr[j] = 0;
1141       ei->event_page[j] = &ei->change_page[0];
1142     }
1143   }
1144
1145   /* add changing elements from pre-defined list */
1146   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1147   {
1148     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1149     struct ElementInfo *ei = &element_info[ch_delay->element];
1150
1151     ei->change->target_element       = ch_delay->target_element;
1152     ei->change->delay_fixed          = ch_delay->change_delay;
1153
1154     ei->change->pre_change_function  = ch_delay->pre_change_function;
1155     ei->change->change_function      = ch_delay->change_function;
1156     ei->change->post_change_function = ch_delay->post_change_function;
1157
1158     ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1159   }
1160
1161 #if 1
1162   /* add change events from custom element configuration */
1163   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1164   {
1165     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1166
1167     for (j = 0; j < ei->num_change_pages; j++)
1168     {
1169       if (!ei->change_page[j].can_change)
1170         continue;
1171
1172       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1173       {
1174         /* only add event page for the first page found with this event */
1175         if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1176             !(ei->change_events & CH_EVENT_BIT(k)))
1177         {
1178           ei->change_events |= CH_EVENT_BIT(k);
1179           ei->event_page_nr[k] = j;
1180           ei->event_page[k] = &ei->change_page[j];
1181         }
1182       }
1183     }
1184   }
1185
1186 #else
1187
1188   /* add change events from custom element configuration */
1189   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1190   {
1191     int element = EL_CUSTOM_START + i;
1192
1193     /* only add custom elements that change after fixed/random frame delay */
1194     if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1195       element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1196   }
1197 #endif
1198
1199   /* ---------- initialize trigger events ---------------------------------- */
1200
1201   /* initialize trigger events information */
1202   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1203     trigger_events[i] = EP_BITMASK_DEFAULT;
1204
1205 #if 1
1206   /* add trigger events from element change event properties */
1207   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1208   {
1209     struct ElementInfo *ei = &element_info[i];
1210
1211     for (j = 0; j < ei->num_change_pages; j++)
1212     {
1213       if (!ei->change_page[j].can_change)
1214         continue;
1215
1216       if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1217       {
1218         int trigger_element = ei->change_page[j].trigger_element;
1219
1220         if (IS_GROUP_ELEMENT(trigger_element))
1221         {
1222           struct ElementGroupInfo *group = element_info[trigger_element].group;
1223
1224           for (k = 0; k < group->num_elements_resolved; k++)
1225             trigger_events[group->element_resolved[k]]
1226               |= ei->change_page[j].events;
1227         }
1228         else
1229           trigger_events[trigger_element] |= ei->change_page[j].events;
1230       }
1231     }
1232   }
1233 #else
1234   /* add trigger events from element change event properties */
1235   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1236     if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1237       trigger_events[element_info[i].change->trigger_element] |=
1238         element_info[i].change->events;
1239 #endif
1240
1241   /* ---------- initialize push delay -------------------------------------- */
1242
1243   /* initialize push delay values to default */
1244   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1245   {
1246     if (!IS_CUSTOM_ELEMENT(i))
1247     {
1248       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
1249       element_info[i].push_delay_random = game.default_push_delay_random;
1250     }
1251   }
1252
1253   /* set push delay value for certain elements from pre-defined list */
1254   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1255   {
1256     int e = push_delay_list[i].element;
1257
1258     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1259     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1260   }
1261
1262   /* set push delay value for Supaplex elements for newer engine versions */
1263   if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1264   {
1265     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1266     {
1267       if (IS_SP_ELEMENT(i))
1268       {
1269         element_info[i].push_delay_fixed  = 6;
1270         element_info[i].push_delay_random = 0;
1271       }
1272     }
1273   }
1274
1275   /* ---------- initialize move stepsize ----------------------------------- */
1276
1277   /* initialize move stepsize values to default */
1278   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1279     if (!IS_CUSTOM_ELEMENT(i))
1280       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1281
1282   /* set move stepsize value for certain elements from pre-defined list */
1283   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1284   {
1285     int e = move_stepsize_list[i].element;
1286
1287     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1288   }
1289
1290   /* ---------- initialize move dig/leave ---------------------------------- */
1291
1292   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1293   {
1294     element_info[i].can_leave_element = FALSE;
1295     element_info[i].can_leave_element_last = FALSE;
1296   }
1297
1298   /* ---------- initialize gem count --------------------------------------- */
1299
1300   /* initialize gem count values for each element */
1301   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1302     if (!IS_CUSTOM_ELEMENT(i))
1303       element_info[i].collect_count = 0;
1304
1305   /* add gem count values for all elements from pre-defined list */
1306   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1307     element_info[collect_count_list[i].element].collect_count =
1308       collect_count_list[i].count;
1309
1310   /* ---------- initialize access direction -------------------------------- */
1311
1312   /* initialize access direction values to default */
1313   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1314     if (!IS_CUSTOM_ELEMENT(i))
1315       element_info[i].access_direction = MV_ALL_DIRECTIONS;
1316
1317   /* set access direction value for certain elements from pre-defined list */
1318   for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1319     element_info[tube_access[i].element].access_direction =
1320       tube_access[i].direction;
1321 }
1322
1323
1324 /*
1325   =============================================================================
1326   InitGame()
1327   -----------------------------------------------------------------------------
1328   initialize and start new game
1329   =============================================================================
1330 */
1331
1332 void InitGame()
1333 {
1334   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
1335   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
1336   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
1337   int i, j, k, x, y;
1338
1339   InitGameEngine();
1340
1341 #if 0
1342 #if DEBUG
1343 #if USE_NEW_AMOEBA_CODE
1344   printf("Using new amoeba code.\n");
1345 #else
1346   printf("Using old amoeba code.\n");
1347 #endif
1348 #endif
1349 #endif
1350
1351   /* don't play tapes over network */
1352   network_playing = (options.network && !tape.playing);
1353
1354   for (i = 0; i < MAX_PLAYERS; i++)
1355   {
1356     struct PlayerInfo *player = &stored_player[i];
1357
1358     player->index_nr = i;
1359     player->index_bit = (1 << i);
1360     player->element_nr = EL_PLAYER_1 + i;
1361
1362     player->present = FALSE;
1363     player->active = FALSE;
1364
1365     player->action = 0;
1366     player->effective_action = 0;
1367     player->programmed_action = 0;
1368
1369     player->score = 0;
1370     player->gems_still_needed = level.gems_needed;
1371     player->sokobanfields_still_needed = 0;
1372     player->lights_still_needed = 0;
1373     player->friends_still_needed = 0;
1374
1375     for (j = 0; j < MAX_KEYS; j++)
1376       player->key[j] = FALSE;
1377
1378     player->dynabomb_count = 0;
1379     player->dynabomb_size = 1;
1380     player->dynabombs_left = 0;
1381     player->dynabomb_xl = FALSE;
1382
1383     player->MovDir = MV_NO_MOVING;
1384     player->MovPos = 0;
1385     player->GfxPos = 0;
1386     player->GfxDir = MV_NO_MOVING;
1387     player->GfxAction = ACTION_DEFAULT;
1388     player->Frame = 0;
1389     player->StepFrame = 0;
1390
1391     player->use_murphy_graphic = FALSE;
1392
1393     player->block_last_field = FALSE;
1394
1395     player->actual_frame_counter = 0;
1396
1397     player->step_counter = 0;
1398
1399     player->last_move_dir = MV_NO_MOVING;
1400
1401     player->is_waiting = FALSE;
1402     player->is_moving = FALSE;
1403     player->is_digging = FALSE;
1404     player->is_snapping = FALSE;
1405     player->is_collecting = FALSE;
1406     player->is_pushing = FALSE;
1407     player->is_switching = FALSE;
1408     player->is_dropping = FALSE;
1409
1410     player->is_bored = FALSE;
1411     player->is_sleeping = FALSE;
1412
1413     player->frame_counter_bored = -1;
1414     player->frame_counter_sleeping = -1;
1415
1416     player->anim_delay_counter = 0;
1417     player->post_delay_counter = 0;
1418
1419     player->action_waiting = ACTION_DEFAULT;
1420     player->last_action_waiting = ACTION_DEFAULT;
1421     player->special_action_bored = ACTION_DEFAULT;
1422     player->special_action_sleeping = ACTION_DEFAULT;
1423
1424     player->num_special_action_bored = 0;
1425     player->num_special_action_sleeping = 0;
1426
1427     /* determine number of special actions for bored and sleeping animation */
1428     for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1429     {
1430       boolean found = FALSE;
1431
1432       for (k = 0; k < NUM_DIRECTIONS; k++)
1433         if (el_act_dir2img(player->element_nr, j, k) !=
1434             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1435           found = TRUE;
1436
1437       if (found)
1438         player->num_special_action_bored++;
1439       else
1440         break;
1441     }
1442     for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1443     {
1444       boolean found = FALSE;
1445
1446       for (k = 0; k < NUM_DIRECTIONS; k++)
1447         if (el_act_dir2img(player->element_nr, j, k) !=
1448             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1449           found = TRUE;
1450
1451       if (found)
1452         player->num_special_action_sleeping++;
1453       else
1454         break;
1455     }
1456
1457     player->switch_x = -1;
1458     player->switch_y = -1;
1459
1460     player->show_envelope = 0;
1461
1462     player->move_delay       = game.initial_move_delay;
1463     player->move_delay_value = game.initial_move_delay_value;
1464
1465     player->move_delay_reset_counter = 0;
1466
1467     player->push_delay = 0;
1468     player->push_delay_value = game.initial_push_delay_value;
1469
1470     player->drop_delay = 0;
1471
1472     player->last_jx = player->last_jy = 0;
1473     player->jx = player->jy = 0;
1474
1475     player->shield_normal_time_left = 0;
1476     player->shield_deadly_time_left = 0;
1477
1478     player->inventory_infinite_element = EL_UNDEFINED;
1479     player->inventory_size = 0;
1480
1481     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1482     SnapField(player, 0, 0);
1483
1484     player->LevelSolved = FALSE;
1485     player->GameOver = FALSE;
1486   }
1487
1488   network_player_action_received = FALSE;
1489
1490 #if defined(PLATFORM_UNIX)
1491   /* initial null action */
1492   if (network_playing)
1493     SendToServer_MovePlayer(MV_NO_MOVING);
1494 #endif
1495
1496   ZX = ZY = -1;
1497
1498   FrameCounter = 0;
1499   TimeFrames = 0;
1500   TimePlayed = 0;
1501   TimeLeft = level.time;
1502   TapeTime = 0;
1503
1504   ScreenMovDir = MV_NO_MOVING;
1505   ScreenMovPos = 0;
1506   ScreenGfxPos = 0;
1507
1508   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
1509
1510   AllPlayersGone = FALSE;
1511
1512   game.yamyam_content_nr = 0;
1513   game.magic_wall_active = FALSE;
1514   game.magic_wall_time_left = 0;
1515   game.light_time_left = 0;
1516   game.timegate_time_left = 0;
1517   game.switchgate_pos = 0;
1518   game.balloon_dir = MV_NO_MOVING;
1519   game.gravity = level.initial_gravity;
1520   game.explosions_delayed = TRUE;
1521
1522   game.envelope_active = FALSE;
1523
1524   for (i = 0; i < NUM_BELTS; i++)
1525   {
1526     game.belt_dir[i] = MV_NO_MOVING;
1527     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
1528   }
1529
1530   for (i = 0; i < MAX_NUM_AMOEBA; i++)
1531     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1532
1533   for (x = 0; x < lev_fieldx; x++)
1534   {
1535     for (y = 0; y < lev_fieldy; y++)
1536     {
1537       Feld[x][y] = level.field[x][y];
1538       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1539       ChangeDelay[x][y] = 0;
1540       ChangePage[x][y] = -1;
1541       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1542       AmoebaNr[x][y] = 0;
1543       WasJustMoving[x][y] = 0;
1544       WasJustFalling[x][y] = 0;
1545       Stop[x][y] = FALSE;
1546       Pushed[x][y] = FALSE;
1547
1548       Changed[x][y] = CE_BITMASK_DEFAULT;
1549       ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1550
1551       ExplodePhase[x][y] = 0;
1552       ExplodeDelay[x][y] = 0;
1553       ExplodeField[x][y] = EX_NO_EXPLOSION;
1554
1555       RunnerVisit[x][y] = 0;
1556       PlayerVisit[x][y] = 0;
1557
1558       GfxFrame[x][y] = 0;
1559       GfxRandom[x][y] = INIT_GFX_RANDOM();
1560       GfxElement[x][y] = EL_UNDEFINED;
1561       GfxAction[x][y] = ACTION_DEFAULT;
1562       GfxDir[x][y] = MV_NO_MOVING;
1563     }
1564   }
1565
1566   for (y = 0; y < lev_fieldy; y++)
1567   {
1568     for (x = 0; x < lev_fieldx; x++)
1569     {
1570       if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1571         emulate_bd = FALSE;
1572       if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1573         emulate_sb = FALSE;
1574       if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1575         emulate_sp = FALSE;
1576
1577       InitField(x, y, TRUE);
1578     }
1579   }
1580
1581   InitBeltMovement();
1582
1583   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1584                     emulate_sb ? EMU_SOKOBAN :
1585                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1586
1587   /* initialize explosion and ignition delay */
1588   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1589   {
1590     if (!IS_CUSTOM_ELEMENT(i))
1591     {
1592       int num_phase = 9;
1593       int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1594       int last_phase = num_phase * delay;
1595       int half_phase = (num_phase / 2) * delay;
1596
1597       element_info[i].explosion_delay = last_phase;
1598       element_info[i].ignition_delay = half_phase;
1599
1600       if (i == EL_BLACK_ORB)
1601         element_info[i].ignition_delay = 1;
1602     }
1603
1604     if (element_info[i].explosion_delay < 2)    /* !!! check again !!! */
1605       element_info[i].explosion_delay = 2;
1606
1607     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
1608       element_info[i].ignition_delay = 1;
1609   }
1610
1611   /* correct non-moving belts to start moving left */
1612   for (i = 0; i < NUM_BELTS; i++)
1613     if (game.belt_dir[i] == MV_NO_MOVING)
1614       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
1615
1616   /* check if any connected player was not found in playfield */
1617   for (i = 0; i < MAX_PLAYERS; i++)
1618   {
1619     struct PlayerInfo *player = &stored_player[i];
1620
1621     if (player->connected && !player->present)
1622     {
1623       for (j = 0; j < MAX_PLAYERS; j++)
1624       {
1625         struct PlayerInfo *some_player = &stored_player[j];
1626         int jx = some_player->jx, jy = some_player->jy;
1627
1628         /* assign first free player found that is present in the playfield */
1629         if (some_player->present && !some_player->connected)
1630         {
1631           player->present = TRUE;
1632           player->active = TRUE;
1633
1634           some_player->present = FALSE;
1635           some_player->active = FALSE;
1636
1637 #if 0
1638           player->element_nr = some_player->element_nr;
1639 #endif
1640
1641           StorePlayer[jx][jy] = player->element_nr;
1642           player->jx = player->last_jx = jx;
1643           player->jy = player->last_jy = jy;
1644
1645           break;
1646         }
1647       }
1648     }
1649   }
1650
1651   if (tape.playing)
1652   {
1653     /* when playing a tape, eliminate all players which do not participate */
1654
1655     for (i = 0; i < MAX_PLAYERS; i++)
1656     {
1657       if (stored_player[i].active && !tape.player_participates[i])
1658       {
1659         struct PlayerInfo *player = &stored_player[i];
1660         int jx = player->jx, jy = player->jy;
1661
1662         player->active = FALSE;
1663         StorePlayer[jx][jy] = 0;
1664         Feld[jx][jy] = EL_EMPTY;
1665       }
1666     }
1667   }
1668   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
1669   {
1670     /* when in single player mode, eliminate all but the first active player */
1671
1672     for (i = 0; i < MAX_PLAYERS; i++)
1673     {
1674       if (stored_player[i].active)
1675       {
1676         for (j = i + 1; j < MAX_PLAYERS; j++)
1677         {
1678           if (stored_player[j].active)
1679           {
1680             struct PlayerInfo *player = &stored_player[j];
1681             int jx = player->jx, jy = player->jy;
1682
1683             player->active = FALSE;
1684             player->present = FALSE;
1685
1686             StorePlayer[jx][jy] = 0;
1687             Feld[jx][jy] = EL_EMPTY;
1688           }
1689         }
1690       }
1691     }
1692   }
1693
1694   /* when recording the game, store which players take part in the game */
1695   if (tape.recording)
1696   {
1697     for (i = 0; i < MAX_PLAYERS; i++)
1698       if (stored_player[i].active)
1699         tape.player_participates[i] = TRUE;
1700   }
1701
1702   if (options.debug)
1703   {
1704     for (i = 0; i < MAX_PLAYERS; i++)
1705     {
1706       struct PlayerInfo *player = &stored_player[i];
1707
1708       printf("Player %d: present == %d, connected == %d, active == %d.\n",
1709              i+1,
1710              player->present,
1711              player->connected,
1712              player->active);
1713       if (local_player == player)
1714         printf("Player  %d is local player.\n", i+1);
1715     }
1716   }
1717
1718   if (BorderElement == EL_EMPTY)
1719   {
1720     SBX_Left = 0;
1721     SBX_Right = lev_fieldx - SCR_FIELDX;
1722     SBY_Upper = 0;
1723     SBY_Lower = lev_fieldy - SCR_FIELDY;
1724   }
1725   else
1726   {
1727     SBX_Left = -1;
1728     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1729     SBY_Upper = -1;
1730     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1731   }
1732
1733   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1734     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1735
1736   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1737     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1738
1739   /* if local player not found, look for custom element that might create
1740      the player (make some assumptions about the right custom element) */
1741   if (!local_player->present)
1742   {
1743     int start_x = 0, start_y = 0;
1744     int found_rating = 0;
1745     int found_element = EL_UNDEFINED;
1746
1747     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1748     {
1749       int element = Feld[x][y];
1750       int content;
1751       int xx, yy;
1752       boolean is_player;
1753
1754       if (!IS_CUSTOM_ELEMENT(element))
1755         continue;
1756
1757       if (CAN_CHANGE(element))
1758       {
1759         for (i = 0; i < element_info[element].num_change_pages; i++)
1760         {
1761           content = element_info[element].change_page[i].target_element;
1762           is_player = ELEM_IS_PLAYER(content);
1763
1764           if (is_player && (found_rating < 3 || element < found_element))
1765           {
1766             start_x = x;
1767             start_y = y;
1768
1769             found_rating = 3;
1770             found_element = element;
1771           }
1772         }
1773       }
1774
1775       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1776       {
1777         content = element_info[element].content[xx][yy];
1778         is_player = ELEM_IS_PLAYER(content);
1779
1780         if (is_player && (found_rating < 2 || element < found_element))
1781         {
1782           start_x = x + xx - 1;
1783           start_y = y + yy - 1;
1784
1785           found_rating = 2;
1786           found_element = element;
1787         }
1788
1789         if (!CAN_CHANGE(element))
1790           continue;
1791
1792         for (i = 0; i < element_info[element].num_change_pages; i++)
1793         {
1794           content = element_info[element].change_page[i].content[xx][yy];
1795           is_player = ELEM_IS_PLAYER(content);
1796
1797           if (is_player && (found_rating < 1 || element < found_element))
1798           {
1799             start_x = x + xx - 1;
1800             start_y = y + yy - 1;
1801
1802             found_rating = 1;
1803             found_element = element;
1804           }
1805         }
1806       }
1807     }
1808
1809     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
1810                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1811                 start_x - MIDPOSX);
1812
1813     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1814                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1815                 start_y - MIDPOSY);
1816   }
1817   else
1818   {
1819 #if 1
1820     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
1821                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1822                 local_player->jx - MIDPOSX);
1823
1824     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1825                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1826                 local_player->jy - MIDPOSY);
1827 #else
1828     scroll_x = SBX_Left;
1829     scroll_y = SBY_Upper;
1830     if (local_player->jx >= SBX_Left + MIDPOSX)
1831       scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1832                   local_player->jx - MIDPOSX :
1833                   SBX_Right);
1834     if (local_player->jy >= SBY_Upper + MIDPOSY)
1835       scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1836                   local_player->jy - MIDPOSY :
1837                   SBY_Lower);
1838 #endif
1839   }
1840
1841   CloseDoor(DOOR_CLOSE_1);
1842
1843   DrawLevel();
1844   DrawAllPlayers();
1845
1846   /* after drawing the level, correct some elements */
1847   if (game.timegate_time_left == 0)
1848     CloseAllOpenTimegates();
1849
1850   if (setup.soft_scrolling)
1851     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1852
1853   redraw_mask |= REDRAW_FROM_BACKBUFFER;
1854   FadeToFront();
1855
1856   /* copy default game door content to main double buffer */
1857   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1858              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1859
1860   DrawGameDoorValues();
1861
1862   UnmapGameButtons();
1863   UnmapTapeButtons();
1864   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1865   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1866   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1867   MapGameButtons();
1868   MapTapeButtons();
1869
1870   /* copy actual game door content to door double buffer for OpenDoor() */
1871   BlitBitmap(drawto, bitmap_db_door,
1872              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1873
1874   OpenDoor(DOOR_OPEN_ALL);
1875
1876   PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1877
1878   if (setup.sound_music)
1879     PlayLevelMusic();
1880
1881   KeyboardAutoRepeatOffUnlessAutoplay();
1882
1883   if (options.debug)
1884   {
1885     for (i = 0; i < MAX_PLAYERS; i++)
1886       printf("Player %d %sactive.\n",
1887              i + 1, (stored_player[i].active ? "" : "not "));
1888   }
1889
1890 #if 0
1891   printf("::: starting game [%d]\n", FrameCounter);
1892 #endif
1893 }
1894
1895 void InitMovDir(int x, int y)
1896 {
1897   int i, element = Feld[x][y];
1898   static int xy[4][2] =
1899   {
1900     {  0, +1 },
1901     { +1,  0 },
1902     {  0, -1 },
1903     { -1,  0 }
1904   };
1905   static int direction[3][4] =
1906   {
1907     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
1908     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
1909     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
1910   };
1911
1912   switch(element)
1913   {
1914     case EL_BUG_RIGHT:
1915     case EL_BUG_UP:
1916     case EL_BUG_LEFT:
1917     case EL_BUG_DOWN:
1918       Feld[x][y] = EL_BUG;
1919       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1920       break;
1921
1922     case EL_SPACESHIP_RIGHT:
1923     case EL_SPACESHIP_UP:
1924     case EL_SPACESHIP_LEFT:
1925     case EL_SPACESHIP_DOWN:
1926       Feld[x][y] = EL_SPACESHIP;
1927       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1928       break;
1929
1930     case EL_BD_BUTTERFLY_RIGHT:
1931     case EL_BD_BUTTERFLY_UP:
1932     case EL_BD_BUTTERFLY_LEFT:
1933     case EL_BD_BUTTERFLY_DOWN:
1934       Feld[x][y] = EL_BD_BUTTERFLY;
1935       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1936       break;
1937
1938     case EL_BD_FIREFLY_RIGHT:
1939     case EL_BD_FIREFLY_UP:
1940     case EL_BD_FIREFLY_LEFT:
1941     case EL_BD_FIREFLY_DOWN:
1942       Feld[x][y] = EL_BD_FIREFLY;
1943       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1944       break;
1945
1946     case EL_PACMAN_RIGHT:
1947     case EL_PACMAN_UP:
1948     case EL_PACMAN_LEFT:
1949     case EL_PACMAN_DOWN:
1950       Feld[x][y] = EL_PACMAN;
1951       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1952       break;
1953
1954     case EL_SP_SNIKSNAK:
1955       MovDir[x][y] = MV_UP;
1956       break;
1957
1958     case EL_SP_ELECTRON:
1959       MovDir[x][y] = MV_LEFT;
1960       break;
1961
1962     case EL_MOLE_LEFT:
1963     case EL_MOLE_RIGHT:
1964     case EL_MOLE_UP:
1965     case EL_MOLE_DOWN:
1966       Feld[x][y] = EL_MOLE;
1967       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1968       break;
1969
1970     default:
1971       if (IS_CUSTOM_ELEMENT(element))
1972       {
1973         struct ElementInfo *ei = &element_info[element];
1974         int move_direction_initial = ei->move_direction_initial;
1975         int move_pattern = ei->move_pattern;
1976
1977         if (move_direction_initial == MV_START_PREVIOUS)
1978         {
1979           if (MovDir[x][y] != MV_NO_MOVING)
1980             return;
1981
1982           move_direction_initial = MV_START_AUTOMATIC;
1983         }
1984
1985         if (move_direction_initial == MV_START_RANDOM)
1986           MovDir[x][y] = 1 << RND(4);
1987         else if (move_direction_initial & MV_ANY_DIRECTION)
1988           MovDir[x][y] = move_direction_initial;
1989         else if (move_pattern == MV_ALL_DIRECTIONS ||
1990                  move_pattern == MV_TURNING_LEFT ||
1991                  move_pattern == MV_TURNING_RIGHT ||
1992                  move_pattern == MV_TURNING_LEFT_RIGHT ||
1993                  move_pattern == MV_TURNING_RIGHT_LEFT ||
1994                  move_pattern == MV_TURNING_RANDOM)
1995           MovDir[x][y] = 1 << RND(4);
1996         else if (move_pattern == MV_HORIZONTAL)
1997           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1998         else if (move_pattern == MV_VERTICAL)
1999           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2000         else if (move_pattern & MV_ANY_DIRECTION)
2001           MovDir[x][y] = element_info[element].move_pattern;
2002         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2003                  move_pattern == MV_ALONG_RIGHT_SIDE)
2004         {
2005           for (i = 0; i < NUM_DIRECTIONS; i++)
2006           {
2007             int x1 = x + xy[i][0];
2008             int y1 = y + xy[i][1];
2009
2010             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2011             {
2012               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2013                 MovDir[x][y] = direction[0][i];
2014               else
2015                 MovDir[x][y] = direction[1][i];
2016
2017               break;
2018             }
2019           }
2020         }                
2021       }
2022       else
2023       {
2024         MovDir[x][y] = 1 << RND(4);
2025
2026         if (element != EL_BUG &&
2027             element != EL_SPACESHIP &&
2028             element != EL_BD_BUTTERFLY &&
2029             element != EL_BD_FIREFLY)
2030           break;
2031
2032         for (i = 0; i < NUM_DIRECTIONS; i++)
2033         {
2034           int x1 = x + xy[i][0];
2035           int y1 = y + xy[i][1];
2036
2037           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2038           {
2039             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2040             {
2041               MovDir[x][y] = direction[0][i];
2042               break;
2043             }
2044             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2045                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2046             {
2047               MovDir[x][y] = direction[1][i];
2048               break;
2049             }
2050           }
2051         }
2052       }
2053       break;
2054   }
2055
2056   GfxDir[x][y] = MovDir[x][y];
2057 }
2058
2059 void InitAmoebaNr(int x, int y)
2060 {
2061   int i;
2062   int group_nr = AmoebeNachbarNr(x, y);
2063
2064   if (group_nr == 0)
2065   {
2066     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2067     {
2068       if (AmoebaCnt[i] == 0)
2069       {
2070         group_nr = i;
2071         break;
2072       }
2073     }
2074   }
2075
2076   AmoebaNr[x][y] = group_nr;
2077   AmoebaCnt[group_nr]++;
2078   AmoebaCnt2[group_nr]++;
2079 }
2080
2081 void GameWon()
2082 {
2083   int hi_pos;
2084   boolean raise_level = FALSE;
2085
2086   if (local_player->MovPos)
2087     return;
2088
2089 #if 1
2090   if (tape.auto_play)           /* tape might already be stopped here */
2091     tape.auto_play_level_solved = TRUE;
2092 #else
2093   if (tape.playing && tape.auto_play)
2094     tape.auto_play_level_solved = TRUE;
2095 #endif
2096
2097   local_player->LevelSolved = FALSE;
2098
2099   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2100
2101   if (TimeLeft)
2102   {
2103     if (!tape.playing && setup.sound_loops)
2104       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2105                    SND_CTRL_PLAY_LOOP);
2106
2107     while (TimeLeft > 0)
2108     {
2109       if (!tape.playing && !setup.sound_loops)
2110         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2111       if (TimeLeft > 0 && !(TimeLeft % 10))
2112         RaiseScore(level.score[SC_TIME_BONUS]);
2113       if (TimeLeft > 100 && !(TimeLeft % 10))
2114         TimeLeft -= 10;
2115       else
2116         TimeLeft--;
2117
2118       DrawGameValue_Time(TimeLeft);
2119
2120       BackToFront();
2121
2122       if (!tape.playing)
2123         Delay(10);
2124     }
2125
2126     if (!tape.playing && setup.sound_loops)
2127       StopSound(SND_GAME_LEVELTIME_BONUS);
2128   }
2129   else if (level.time == 0)             /* level without time limit */
2130   {
2131     if (!tape.playing && setup.sound_loops)
2132       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2133                    SND_CTRL_PLAY_LOOP);
2134
2135     while (TimePlayed < 999)
2136     {
2137       if (!tape.playing && !setup.sound_loops)
2138         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2139       if (TimePlayed < 999 && !(TimePlayed % 10))
2140         RaiseScore(level.score[SC_TIME_BONUS]);
2141       if (TimePlayed < 900 && !(TimePlayed % 10))
2142         TimePlayed += 10;
2143       else
2144         TimePlayed++;
2145
2146       DrawGameValue_Time(TimePlayed);
2147
2148       BackToFront();
2149
2150       if (!tape.playing)
2151         Delay(10);
2152     }
2153
2154     if (!tape.playing && setup.sound_loops)
2155       StopSound(SND_GAME_LEVELTIME_BONUS);
2156   }
2157
2158   /* close exit door after last player */
2159   if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2160        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2161   {
2162     int element = Feld[ExitX][ExitY];
2163
2164     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2165                           EL_SP_EXIT_CLOSING);
2166
2167     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2168   }
2169
2170   /* Hero disappears */
2171   DrawLevelField(ExitX, ExitY);
2172   BackToFront();
2173
2174   if (tape.playing)
2175     return;
2176
2177   CloseDoor(DOOR_CLOSE_1);
2178
2179   if (tape.recording)
2180   {
2181     TapeStop();
2182     SaveTape(tape.level_nr);            /* Ask to save tape */
2183   }
2184
2185   if (level_nr == leveldir_current->handicap_level)
2186   {
2187     leveldir_current->handicap_level++;
2188     SaveLevelSetup_SeriesInfo();
2189   }
2190
2191   if (level_editor_test_game)
2192     local_player->score = -1;   /* no highscore when playing from editor */
2193   else if (level_nr < leveldir_current->last_level)
2194     raise_level = TRUE;         /* advance to next level */
2195
2196   if ((hi_pos = NewHiScore()) >= 0) 
2197   {
2198     game_status = GAME_MODE_SCORES;
2199     DrawHallOfFame(hi_pos);
2200     if (raise_level)
2201     {
2202       level_nr++;
2203       TapeErase();
2204     }
2205   }
2206   else
2207   {
2208     game_status = GAME_MODE_MAIN;
2209     if (raise_level)
2210     {
2211       level_nr++;
2212       TapeErase();
2213     }
2214     DrawMainMenu();
2215   }
2216
2217   BackToFront();
2218 }
2219
2220 int NewHiScore()
2221 {
2222   int k, l;
2223   int position = -1;
2224
2225   LoadScore(level_nr);
2226
2227   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2228       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
2229     return -1;
2230
2231   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
2232   {
2233     if (local_player->score > highscore[k].Score)
2234     {
2235       /* player has made it to the hall of fame */
2236
2237       if (k < MAX_SCORE_ENTRIES - 1)
2238       {
2239         int m = MAX_SCORE_ENTRIES - 1;
2240
2241 #ifdef ONE_PER_NAME
2242         for (l = k; l < MAX_SCORE_ENTRIES; l++)
2243           if (!strcmp(setup.player_name, highscore[l].Name))
2244             m = l;
2245         if (m == k)     /* player's new highscore overwrites his old one */
2246           goto put_into_list;
2247 #endif
2248
2249         for (l = m; l > k; l--)
2250         {
2251           strcpy(highscore[l].Name, highscore[l - 1].Name);
2252           highscore[l].Score = highscore[l - 1].Score;
2253         }
2254       }
2255
2256 #ifdef ONE_PER_NAME
2257       put_into_list:
2258 #endif
2259       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2260       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2261       highscore[k].Score = local_player->score; 
2262       position = k;
2263       break;
2264     }
2265
2266 #ifdef ONE_PER_NAME
2267     else if (!strncmp(setup.player_name, highscore[k].Name,
2268                       MAX_PLAYER_NAME_LEN))
2269       break;    /* player already there with a higher score */
2270 #endif
2271
2272   }
2273
2274   if (position >= 0) 
2275     SaveScore(level_nr);
2276
2277   return position;
2278 }
2279
2280 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2281 {
2282   if (player->GfxAction != action || player->GfxDir != dir)
2283   {
2284 #if 0
2285     printf("Player frame reset! (%d => %d, %d => %d)\n",
2286            player->GfxAction, action, player->GfxDir, dir);
2287 #endif
2288
2289     player->GfxAction = action;
2290     player->GfxDir = dir;
2291     player->Frame = 0;
2292     player->StepFrame = 0;
2293   }
2294 }
2295
2296 static void ResetRandomAnimationValue(int x, int y)
2297 {
2298   GfxRandom[x][y] = INIT_GFX_RANDOM();
2299 }
2300
2301 static void ResetGfxAnimation(int x, int y)
2302 {
2303   GfxFrame[x][y] = 0;
2304   GfxAction[x][y] = ACTION_DEFAULT;
2305   GfxDir[x][y] = MovDir[x][y];
2306 }
2307
2308 void InitMovingField(int x, int y, int direction)
2309 {
2310   int element = Feld[x][y];
2311   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2312   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2313   int newx = x + dx;
2314   int newy = y + dy;
2315
2316   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2317     ResetGfxAnimation(x, y);
2318
2319   MovDir[newx][newy] = MovDir[x][y] = direction;
2320   GfxDir[x][y] = direction;
2321
2322   if (Feld[newx][newy] == EL_EMPTY)
2323     Feld[newx][newy] = EL_BLOCKED;
2324
2325   if (direction == MV_DOWN && CAN_FALL(element))
2326     GfxAction[x][y] = ACTION_FALLING;
2327   else
2328     GfxAction[x][y] = ACTION_MOVING;
2329
2330   GfxFrame[newx][newy] = GfxFrame[x][y];
2331   GfxRandom[newx][newy] = GfxRandom[x][y];
2332   GfxAction[newx][newy] = GfxAction[x][y];
2333   GfxDir[newx][newy] = GfxDir[x][y];
2334 }
2335
2336 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2337 {
2338   int direction = MovDir[x][y];
2339   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2340   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2341
2342   *goes_to_x = newx;
2343   *goes_to_y = newy;
2344 }
2345
2346 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2347 {
2348   int oldx = x, oldy = y;
2349   int direction = MovDir[x][y];
2350
2351   if (direction == MV_LEFT)
2352     oldx++;
2353   else if (direction == MV_RIGHT)
2354     oldx--;
2355   else if (direction == MV_UP)
2356     oldy++;
2357   else if (direction == MV_DOWN)
2358     oldy--;
2359
2360   *comes_from_x = oldx;
2361   *comes_from_y = oldy;
2362 }
2363
2364 int MovingOrBlocked2Element(int x, int y)
2365 {
2366   int element = Feld[x][y];
2367
2368   if (element == EL_BLOCKED)
2369   {
2370     int oldx, oldy;
2371
2372     Blocked2Moving(x, y, &oldx, &oldy);
2373     return Feld[oldx][oldy];
2374   }
2375   else
2376     return element;
2377 }
2378
2379 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2380 {
2381   /* like MovingOrBlocked2Element(), but if element is moving
2382      and (x,y) is the field the moving element is just leaving,
2383      return EL_BLOCKED instead of the element value */
2384   int element = Feld[x][y];
2385
2386   if (IS_MOVING(x, y))
2387   {
2388     if (element == EL_BLOCKED)
2389     {
2390       int oldx, oldy;
2391
2392       Blocked2Moving(x, y, &oldx, &oldy);
2393       return Feld[oldx][oldy];
2394     }
2395     else
2396       return EL_BLOCKED;
2397   }
2398   else
2399     return element;
2400 }
2401
2402 static void RemoveField(int x, int y)
2403 {
2404   Feld[x][y] = EL_EMPTY;
2405
2406   MovPos[x][y] = 0;
2407   MovDir[x][y] = 0;
2408   MovDelay[x][y] = 0;
2409
2410   AmoebaNr[x][y] = 0;
2411   ChangeDelay[x][y] = 0;
2412   ChangePage[x][y] = -1;
2413   Pushed[x][y] = FALSE;
2414
2415   GfxElement[x][y] = EL_UNDEFINED;
2416   GfxAction[x][y] = ACTION_DEFAULT;
2417   GfxDir[x][y] = MV_NO_MOVING;
2418 }
2419
2420 void RemoveMovingField(int x, int y)
2421 {
2422   int oldx = x, oldy = y, newx = x, newy = y;
2423   int element = Feld[x][y];
2424   int next_element = EL_UNDEFINED;
2425
2426   if (element != EL_BLOCKED && !IS_MOVING(x, y))
2427     return;
2428
2429   if (IS_MOVING(x, y))
2430   {
2431     Moving2Blocked(x, y, &newx, &newy);
2432 #if 0
2433     if (Feld[newx][newy] != EL_BLOCKED)
2434       return;
2435 #else
2436     if (Feld[newx][newy] != EL_BLOCKED)
2437     {
2438       /* element is moving, but target field is not free (blocked), but
2439          already occupied by something different (example: acid pool);
2440          in this case, only remove the moving field, but not the target */
2441
2442       RemoveField(oldx, oldy);
2443
2444       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2445
2446       DrawLevelField(oldx, oldy);
2447
2448       return;
2449     }
2450 #endif
2451   }
2452   else if (element == EL_BLOCKED)
2453   {
2454     Blocked2Moving(x, y, &oldx, &oldy);
2455     if (!IS_MOVING(oldx, oldy))
2456       return;
2457   }
2458
2459   if (element == EL_BLOCKED &&
2460       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2461        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2462        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2463        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2464     next_element = get_next_element(Feld[oldx][oldy]);
2465
2466   RemoveField(oldx, oldy);
2467   RemoveField(newx, newy);
2468
2469   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2470
2471   if (next_element != EL_UNDEFINED)
2472     Feld[oldx][oldy] = next_element;
2473
2474   DrawLevelField(oldx, oldy);
2475   DrawLevelField(newx, newy);
2476 }
2477
2478 void DrawDynamite(int x, int y)
2479 {
2480   int sx = SCREENX(x), sy = SCREENY(y);
2481   int graphic = el2img(Feld[x][y]);
2482   int frame;
2483
2484   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2485     return;
2486
2487   if (IS_WALKABLE_INSIDE(Back[x][y]))
2488     return;
2489
2490   if (Back[x][y])
2491     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2492   else if (Store[x][y])
2493     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2494
2495   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2496
2497 #if 1
2498   if (Back[x][y] || Store[x][y])
2499     DrawGraphicThruMask(sx, sy, graphic, frame);
2500   else
2501     DrawGraphic(sx, sy, graphic, frame);
2502 #else
2503   if (game.emulation == EMU_SUPAPLEX)
2504     DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2505   else if (Store[x][y])
2506     DrawGraphicThruMask(sx, sy, graphic, frame);
2507   else
2508     DrawGraphic(sx, sy, graphic, frame);
2509 #endif
2510 }
2511
2512 void CheckDynamite(int x, int y)
2513 {
2514   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
2515   {
2516     MovDelay[x][y]--;
2517
2518     if (MovDelay[x][y] != 0)
2519     {
2520       DrawDynamite(x, y);
2521       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2522
2523       return;
2524     }
2525   }
2526
2527 #if 1
2528   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2529 #else
2530   if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2531       Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2532     StopSound(SND_DYNAMITE_ACTIVE);
2533   else
2534     StopSound(SND_DYNABOMB_ACTIVE);
2535 #endif
2536
2537   Bang(x, y);
2538 }
2539
2540 void RelocatePlayer(int x, int y, int element_raw)
2541 {
2542   int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2543   struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2544   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2545   boolean no_delay = (tape.index_search);
2546   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2547   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2548 #if 1
2549   int old_jx, old_jy;
2550 #endif
2551
2552   if (player->GameOver)         /* do not reanimate dead player */
2553     return;
2554
2555 #if 1
2556   RemoveField(x, y);            /* temporarily remove newly placed player */
2557   DrawLevelField(x, y);
2558 #endif
2559
2560   if (player->present)
2561   {
2562     while (player->MovPos)
2563     {
2564       ScrollPlayer(player, SCROLL_GO_ON);
2565       ScrollScreen(NULL, SCROLL_GO_ON);
2566       FrameCounter++;
2567
2568       DrawPlayer(player);
2569
2570       BackToFront();
2571       Delay(wait_delay_value);
2572     }
2573
2574     DrawPlayer(player);         /* needed here only to cleanup last field */
2575     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
2576
2577     player->is_moving = FALSE;
2578   }
2579
2580 #if 1
2581   old_jx = player->jx;
2582   old_jy = player->jy;
2583 #endif
2584
2585   Feld[x][y] = element;
2586   InitPlayerField(x, y, element, TRUE);
2587
2588 #if 0
2589   if (player == local_player)
2590   {
2591 #if 1
2592
2593     scroll_x += (local_player->jx - old_jx);
2594     scroll_y += (local_player->jy - old_jy);
2595
2596     /* don't scroll over playfield boundaries */
2597     if (scroll_x < SBX_Left || scroll_x > SBX_Right)
2598       scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
2599
2600     /* don't scroll over playfield boundaries */
2601     if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
2602       scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
2603
2604 #else
2605     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2606                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2607                 local_player->jx - MIDPOSX);
2608
2609     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2610                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2611                 local_player->jy - MIDPOSY);
2612 #endif
2613
2614     RedrawPlayfield(TRUE, 0,0,0,0);
2615 #if 0
2616     DrawAllPlayers();
2617     BackToFront();
2618 #endif
2619   }
2620
2621 #else
2622
2623   if (player == local_player)
2624   {
2625     int scroll_xx = -999, scroll_yy = -999;
2626
2627     while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2628     {
2629       int dx = 0, dy = 0;
2630       int fx = FX, fy = FY;
2631
2632       scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2633                    local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2634                    local_player->jx - MIDPOSX);
2635
2636       scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2637                    local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2638                    local_player->jy - MIDPOSY);
2639
2640       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2641       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2642
2643       scroll_x -= dx;
2644       scroll_y -= dy;
2645
2646       fx += dx * TILEX / 2;
2647       fy += dy * TILEY / 2;
2648
2649       ScrollLevel(dx, dy);
2650       DrawAllPlayers();
2651
2652       /* scroll in two steps of half tile size to make things smoother */
2653       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2654       FlushDisplay();
2655       Delay(wait_delay_value);
2656
2657       /* scroll second step to align at full tile size */
2658       BackToFront();
2659       Delay(wait_delay_value);
2660     }
2661   }
2662 #endif
2663 }
2664
2665 void Explode(int ex, int ey, int phase, int mode)
2666 {
2667   int x, y;
2668 #if 0
2669   int num_phase = 9;
2670 #endif
2671
2672   /* !!! eliminate this variable !!! */
2673   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2674
2675 #if 1
2676   int last_phase;
2677 #else
2678   int last_phase = num_phase * delay;
2679   int half_phase = (num_phase / 2) * delay;
2680   int first_phase_after_start = EX_PHASE_START + 1;
2681 #endif
2682   int border_element;
2683
2684   if (game.explosions_delayed)
2685   {
2686     ExplodeField[ex][ey] = mode;
2687     return;
2688   }
2689
2690   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
2691   {
2692     int center_element = Feld[ex][ey];
2693
2694 #if 0
2695     printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
2696 #endif
2697
2698 #if 0
2699     /* --- This is only really needed (and now handled) in "Impact()". --- */
2700     /* do not explode moving elements that left the explode field in time */
2701     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2702         center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2703       return;
2704 #endif
2705
2706     if (mode == EX_NORMAL || mode == EX_CENTER)
2707       PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2708
2709     /* remove things displayed in background while burning dynamite */
2710     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2711       Back[ex][ey] = 0;
2712
2713     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2714     {
2715       /* put moving element to center field (and let it explode there) */
2716       center_element = MovingOrBlocked2Element(ex, ey);
2717       RemoveMovingField(ex, ey);
2718       Feld[ex][ey] = center_element;
2719     }
2720
2721 #if 1
2722     last_phase = element_info[center_element].explosion_delay;
2723 #endif
2724
2725     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2726     {
2727       int xx = x - ex + 1;
2728       int yy = y - ey + 1;
2729       int element;
2730
2731 #if 1
2732       if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
2733         continue;
2734 #else
2735       if (!IN_LEV_FIELD(x, y) ||
2736           ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2737            (x != ex || y != ey)))
2738         continue;
2739 #endif
2740
2741       element = Feld[x][y];
2742
2743       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2744       {
2745         element = MovingOrBlocked2Element(x, y);
2746
2747         if (!IS_EXPLOSION_PROOF(element))
2748           RemoveMovingField(x, y);
2749       }
2750
2751 #if 1
2752
2753 #if 0
2754       if (IS_EXPLOSION_PROOF(element))
2755         continue;
2756 #else
2757       /* indestructible elements can only explode in center (but not flames) */
2758       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2759           element == EL_FLAMES)
2760         continue;
2761 #endif
2762
2763 #else
2764       if ((IS_INDESTRUCTIBLE(element) &&
2765            (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2766             (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2767           element == EL_FLAMES)
2768         continue;
2769 #endif
2770
2771       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2772       {
2773         if (IS_ACTIVE_BOMB(element))
2774         {
2775           /* re-activate things under the bomb like gate or penguin */
2776           Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2777           Store[x][y] = 0;
2778         }
2779
2780         continue;
2781       }
2782
2783       /* save walkable background elements while explosion on same tile */
2784 #if 0
2785       if (IS_INDESTRUCTIBLE(element))
2786         Back[x][y] = element;
2787 #else
2788       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2789         Back[x][y] = element;
2790 #endif
2791
2792       /* ignite explodable elements reached by other explosion */
2793       if (element == EL_EXPLOSION)
2794         element = Store2[x][y];
2795
2796 #if 1
2797       if (AmoebaNr[x][y] &&
2798           (element == EL_AMOEBA_FULL ||
2799            element == EL_BD_AMOEBA ||
2800            element == EL_AMOEBA_GROWING))
2801       {
2802         AmoebaCnt[AmoebaNr[x][y]]--;
2803         AmoebaCnt2[AmoebaNr[x][y]]--;
2804       }
2805
2806       RemoveField(x, y);
2807 #endif
2808
2809       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2810       {
2811         switch(StorePlayer[ex][ey])
2812         {
2813           case EL_PLAYER_2:
2814             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2815             break;
2816           case EL_PLAYER_3:
2817             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2818             break;
2819           case EL_PLAYER_4:
2820             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2821             break;
2822           case EL_PLAYER_1:
2823           default:
2824             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2825             break;
2826         }
2827
2828         if (game.emulation == EMU_SUPAPLEX)
2829           Store[x][y] = EL_EMPTY;
2830       }
2831       else if (center_element == EL_MOLE)
2832         Store[x][y] = EL_EMERALD_RED;
2833       else if (center_element == EL_PENGUIN)
2834         Store[x][y] = EL_EMERALD_PURPLE;
2835       else if (center_element == EL_BUG)
2836         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2837       else if (center_element == EL_BD_BUTTERFLY)
2838         Store[x][y] = EL_BD_DIAMOND;
2839       else if (center_element == EL_SP_ELECTRON)
2840         Store[x][y] = EL_SP_INFOTRON;
2841       else if (center_element == EL_AMOEBA_TO_DIAMOND)
2842         Store[x][y] = level.amoeba_content;
2843       else if (center_element == EL_YAMYAM)
2844         Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2845       else if (IS_CUSTOM_ELEMENT(center_element) &&
2846                element_info[center_element].content[xx][yy] != EL_EMPTY)
2847         Store[x][y] = element_info[center_element].content[xx][yy];
2848       else if (element == EL_WALL_EMERALD)
2849         Store[x][y] = EL_EMERALD;
2850       else if (element == EL_WALL_DIAMOND)
2851         Store[x][y] = EL_DIAMOND;
2852       else if (element == EL_WALL_BD_DIAMOND)
2853         Store[x][y] = EL_BD_DIAMOND;
2854       else if (element == EL_WALL_EMERALD_YELLOW)
2855         Store[x][y] = EL_EMERALD_YELLOW;
2856       else if (element == EL_WALL_EMERALD_RED)
2857         Store[x][y] = EL_EMERALD_RED;
2858       else if (element == EL_WALL_EMERALD_PURPLE)
2859         Store[x][y] = EL_EMERALD_PURPLE;
2860       else if (element == EL_WALL_PEARL)
2861         Store[x][y] = EL_PEARL;
2862       else if (element == EL_WALL_CRYSTAL)
2863         Store[x][y] = EL_CRYSTAL;
2864       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2865         Store[x][y] = element_info[element].content[1][1];
2866       else
2867         Store[x][y] = EL_EMPTY;
2868
2869       if (x != ex || y != ey ||
2870           center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2871         Store2[x][y] = element;
2872
2873 #if 0
2874       if (AmoebaNr[x][y] &&
2875           (element == EL_AMOEBA_FULL ||
2876            element == EL_BD_AMOEBA ||
2877            element == EL_AMOEBA_GROWING))
2878       {
2879         AmoebaCnt[AmoebaNr[x][y]]--;
2880         AmoebaCnt2[AmoebaNr[x][y]]--;
2881       }
2882
2883 #if 1
2884       RemoveField(x, y);
2885 #else
2886       MovDir[x][y] = MovPos[x][y] = 0;
2887       GfxDir[x][y] = MovDir[x][y];
2888       AmoebaNr[x][y] = 0;
2889 #endif
2890 #endif
2891
2892       Feld[x][y] = EL_EXPLOSION;
2893 #if 1
2894       GfxElement[x][y] = center_element;
2895 #else
2896       GfxElement[x][y] = EL_UNDEFINED;
2897 #endif
2898
2899       ExplodePhase[x][y] = 1;
2900 #if 1
2901       ExplodeDelay[x][y] = last_phase;
2902 #endif
2903       Stop[x][y] = TRUE;
2904     }
2905
2906     if (center_element == EL_YAMYAM)
2907       game.yamyam_content_nr =
2908         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2909
2910     return;
2911   }
2912
2913   if (Stop[ex][ey])
2914     return;
2915
2916   x = ex;
2917   y = ey;
2918
2919 #if 1
2920   last_phase = ExplodeDelay[x][y];
2921 #endif
2922
2923   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2924
2925 #ifdef DEBUG
2926
2927   /* activate this even in non-DEBUG version until cause for crash in
2928      getGraphicAnimationFrame() (see below) is found and eliminated */
2929 #endif
2930 #if 1
2931
2932   if (GfxElement[x][y] == EL_UNDEFINED)
2933   {
2934     printf("\n\n");
2935     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2936     printf("Explode(): This should never happen!\n");
2937     printf("\n\n");
2938
2939     GfxElement[x][y] = EL_EMPTY;
2940   }
2941 #endif
2942
2943 #if 1
2944
2945   border_element = Store2[x][y];
2946   if (IS_PLAYER(x, y))
2947     border_element = StorePlayer[x][y];
2948
2949   if (phase == element_info[border_element].ignition_delay ||
2950       phase == last_phase)
2951   {
2952     boolean border_explosion = FALSE;
2953
2954 #if 1
2955     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2956 #else
2957     if (IS_PLAYER(x, y))
2958 #endif
2959     {
2960       KillHeroUnlessExplosionProtected(x, y);
2961       border_explosion = TRUE;
2962
2963 #if 0
2964       if (phase == last_phase)
2965         printf("::: IS_PLAYER\n");
2966 #endif
2967     }
2968     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2969     {
2970       Feld[x][y] = Store2[x][y];
2971       Store2[x][y] = 0;
2972       Bang(x, y);
2973       border_explosion = TRUE;
2974
2975 #if 0
2976       if (phase == last_phase)
2977         printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2978 #endif
2979     }
2980     else if (border_element == EL_AMOEBA_TO_DIAMOND)
2981     {
2982       AmoebeUmwandeln(x, y);
2983       Store2[x][y] = 0;
2984       border_explosion = TRUE;
2985
2986 #if 0
2987       if (phase == last_phase)
2988         printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2989                element_info[border_element].explosion_delay,
2990                element_info[border_element].ignition_delay,
2991                phase);
2992 #endif
2993     }
2994
2995 #if 1
2996     /* if an element just explodes due to another explosion (chain-reaction),
2997        do not immediately end the new explosion when it was the last frame of
2998        the explosion (as it would be done in the following "if"-statement!) */
2999     if (border_explosion && phase == last_phase)
3000       return;
3001 #endif
3002   }
3003
3004 #else
3005
3006   if (phase == first_phase_after_start)
3007   {
3008     int element = Store2[x][y];
3009
3010     if (element == EL_BLACK_ORB)
3011     {
3012       Feld[x][y] = Store2[x][y];
3013       Store2[x][y] = 0;
3014       Bang(x, y);
3015     }
3016   }
3017   else if (phase == half_phase)
3018   {
3019     int element = Store2[x][y];
3020
3021     if (IS_PLAYER(x, y))
3022       KillHeroUnlessExplosionProtected(x, y);
3023     else if (CAN_EXPLODE_BY_EXPLOSION(element))
3024     {
3025       Feld[x][y] = Store2[x][y];
3026       Store2[x][y] = 0;
3027       Bang(x, y);
3028     }
3029     else if (element == EL_AMOEBA_TO_DIAMOND)
3030       AmoebeUmwandeln(x, y);
3031   }
3032 #endif
3033
3034   if (phase == last_phase)
3035   {
3036     int element;
3037
3038 #if 0
3039     printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3040 #endif
3041
3042     element = Feld[x][y] = Store[x][y];
3043     Store[x][y] = Store2[x][y] = 0;
3044     GfxElement[x][y] = EL_UNDEFINED;
3045
3046     /* player can escape from explosions and might therefore be still alive */
3047     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3048         element <= EL_PLAYER_IS_EXPLODING_4)
3049       Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3050                     EL_EMPTY :
3051                     element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3052                     element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3053                     element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3054                     EL_EMERALD_PURPLE);
3055
3056     /* restore probably existing indestructible background element */
3057     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3058       element = Feld[x][y] = Back[x][y];
3059     Back[x][y] = 0;
3060
3061     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3062     GfxDir[x][y] = MV_NO_MOVING;
3063     ChangeDelay[x][y] = 0;
3064     ChangePage[x][y] = -1;
3065
3066 #if 1
3067     InitField_WithBug2(x, y, FALSE);
3068 #else
3069     InitField(x, y, FALSE);
3070 #if 1
3071     /* !!! not needed !!! */
3072 #if 1
3073     if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
3074         CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3075       InitMovDir(x, y);
3076 #else
3077     if (CAN_MOVE(element))
3078       InitMovDir(x, y);
3079 #endif
3080 #endif
3081 #endif
3082     DrawLevelField(x, y);
3083
3084     TestIfElementTouchesCustomElement(x, y);
3085
3086     if (GFX_CRUMBLED(element))
3087       DrawLevelFieldCrumbledSandNeighbours(x, y);
3088
3089     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3090       StorePlayer[x][y] = 0;
3091
3092     if (ELEM_IS_PLAYER(element))
3093       RelocatePlayer(x, y, element);
3094   }
3095 #if 1
3096   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3097 #else
3098   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3099 #endif
3100   {
3101 #if 1
3102     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3103 #else
3104     int stored = Store[x][y];
3105     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3106                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3107                    IMG_SP_EXPLOSION);
3108 #endif
3109     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3110
3111 #if 0
3112     printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3113            element_info[GfxElement[x][y]].token_name,
3114            graphic);
3115 #endif
3116
3117     if (phase == delay)
3118       DrawLevelFieldCrumbledSand(x, y);
3119
3120     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3121     {
3122       DrawLevelElement(x, y, Back[x][y]);
3123       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3124     }
3125     else if (IS_WALKABLE_UNDER(Back[x][y]))
3126     {
3127       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3128       DrawLevelElementThruMask(x, y, Back[x][y]);
3129     }
3130     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3131       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3132   }
3133 }
3134
3135 void DynaExplode(int ex, int ey)
3136 {
3137   int i, j;
3138   int dynabomb_element = Feld[ex][ey];
3139   int dynabomb_size = 1;
3140   boolean dynabomb_xl = FALSE;
3141   struct PlayerInfo *player;
3142   static int xy[4][2] =
3143   {
3144     { 0, -1 },
3145     { -1, 0 },
3146     { +1, 0 },
3147     { 0, +1 }
3148   };
3149
3150   if (IS_ACTIVE_BOMB(dynabomb_element))
3151   {
3152     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3153     dynabomb_size = player->dynabomb_size;
3154     dynabomb_xl = player->dynabomb_xl;
3155     player->dynabombs_left++;
3156   }
3157
3158   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3159
3160   for (i = 0; i < NUM_DIRECTIONS; i++)
3161   {
3162     for (j = 1; j <= dynabomb_size; j++)
3163     {
3164       int x = ex + j * xy[i][0];
3165       int y = ey + j * xy[i][1];
3166       int element;
3167
3168       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3169         break;
3170
3171       element = Feld[x][y];
3172
3173       /* do not restart explosions of fields with active bombs */
3174       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3175         continue;
3176
3177       Explode(x, y, EX_PHASE_START, EX_BORDER);
3178
3179       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3180       if (element != EL_EMPTY &&
3181           element != EL_SAND &&
3182           element != EL_EXPLOSION &&
3183           !dynabomb_xl)
3184         break;
3185     }
3186   }
3187 }
3188
3189 void Bang(int x, int y)
3190 {
3191 #if 1
3192   int element = MovingOrBlocked2Element(x, y);
3193 #else
3194   int element = Feld[x][y];
3195 #endif
3196
3197 #if 1
3198   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3199 #else
3200   if (IS_PLAYER(x, y))
3201 #endif
3202   {
3203     struct PlayerInfo *player = PLAYERINFO(x, y);
3204
3205     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3206                             player->element_nr);
3207   }
3208
3209 #if 0
3210 #if 1
3211   PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3212 #else
3213   if (game.emulation == EMU_SUPAPLEX)
3214     PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3215   else
3216     PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3217 #endif
3218 #endif
3219
3220 #if 0
3221   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
3222     element = EL_EMPTY;
3223 #endif
3224
3225   switch(element)
3226   {
3227     case EL_BUG:
3228     case EL_SPACESHIP:
3229     case EL_BD_BUTTERFLY:
3230     case EL_BD_FIREFLY:
3231     case EL_YAMYAM:
3232     case EL_DARK_YAMYAM:
3233     case EL_ROBOT:
3234     case EL_PACMAN:
3235     case EL_MOLE:
3236       RaiseScoreElement(element);
3237       Explode(x, y, EX_PHASE_START, EX_NORMAL);
3238       break;
3239     case EL_DYNABOMB_PLAYER_1_ACTIVE:
3240     case EL_DYNABOMB_PLAYER_2_ACTIVE:
3241     case EL_DYNABOMB_PLAYER_3_ACTIVE:
3242     case EL_DYNABOMB_PLAYER_4_ACTIVE:
3243     case EL_DYNABOMB_INCREASE_NUMBER:
3244     case EL_DYNABOMB_INCREASE_SIZE:
3245     case EL_DYNABOMB_INCREASE_POWER:
3246       DynaExplode(x, y);
3247       break;
3248     case EL_PENGUIN:
3249     case EL_LAMP:
3250     case EL_LAMP_ACTIVE:
3251 #if 1
3252     case EL_AMOEBA_TO_DIAMOND:
3253 #endif
3254       if (IS_PLAYER(x, y))
3255         Explode(x, y, EX_PHASE_START, EX_NORMAL);
3256       else
3257         Explode(x, y, EX_PHASE_START, EX_CENTER);
3258       break;
3259     default:
3260       if (CAN_EXPLODE_DYNA(element))
3261         DynaExplode(x, y);
3262       else if (CAN_EXPLODE_1X1(element))
3263         Explode(x, y, EX_PHASE_START, EX_CENTER);
3264       else
3265         Explode(x, y, EX_PHASE_START, EX_NORMAL);
3266       break;
3267   }
3268
3269   CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3270 }
3271
3272 void SplashAcid(int x, int y)
3273 {
3274 #if 1
3275   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3276       (!IN_LEV_FIELD(x - 1, y - 2) ||
3277        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3278     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3279
3280   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3281       (!IN_LEV_FIELD(x + 1, y - 2) ||
3282        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3283     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3284
3285   PlayLevelSound(x, y, SND_ACID_SPLASHING);
3286 #else
3287   /* input: position of element entering acid (obsolete) */
3288
3289   int element = Feld[x][y];
3290
3291   if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3292     return;
3293
3294   if (element != EL_ACID_SPLASH_LEFT &&
3295       element != EL_ACID_SPLASH_RIGHT)
3296   {
3297     PlayLevelSound(x, y, SND_ACID_SPLASHING);
3298
3299     if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3300         (!IN_LEV_FIELD(x - 1, y - 1) ||
3301          !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3302       Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3303
3304     if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3305         (!IN_LEV_FIELD(x + 1, y - 1) ||
3306          !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3307       Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3308   }
3309 #endif
3310 }
3311
3312 static void InitBeltMovement()
3313 {
3314   static int belt_base_element[4] =
3315   {
3316     EL_CONVEYOR_BELT_1_LEFT,
3317     EL_CONVEYOR_BELT_2_LEFT,
3318     EL_CONVEYOR_BELT_3_LEFT,
3319     EL_CONVEYOR_BELT_4_LEFT
3320   };
3321   static int belt_base_active_element[4] =
3322   {
3323     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3324     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3325     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3326     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3327   };
3328
3329   int x, y, i, j;
3330
3331   /* set frame order for belt animation graphic according to belt direction */
3332   for (i = 0; i < NUM_BELTS; i++)
3333   {
3334     int belt_nr = i;
3335
3336     for (j = 0; j < NUM_BELT_PARTS; j++)
3337     {
3338       int element = belt_base_active_element[belt_nr] + j;
3339       int graphic = el2img(element);
3340
3341       if (game.belt_dir[i] == MV_LEFT)
3342         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3343       else
3344         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3345     }
3346   }
3347
3348   for (y = 0; y < lev_fieldy; y++)
3349   {
3350     for (x = 0; x < lev_fieldx; x++)
3351     {
3352       int element = Feld[x][y];
3353
3354       for (i = 0; i < NUM_BELTS; i++)
3355       {
3356         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3357         {
3358           int e_belt_nr = getBeltNrFromBeltElement(element);
3359           int belt_nr = i;
3360
3361           if (e_belt_nr == belt_nr)
3362           {
3363             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3364
3365             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3366           }
3367         }
3368       }
3369     }
3370   }
3371 }
3372
3373 static void ToggleBeltSwitch(int x, int y)
3374 {
3375   static int belt_base_element[4] =
3376   {
3377     EL_CONVEYOR_BELT_1_LEFT,
3378     EL_CONVEYOR_BELT_2_LEFT,
3379     EL_CONVEYOR_BELT_3_LEFT,
3380     EL_CONVEYOR_BELT_4_LEFT
3381   };
3382   static int belt_base_active_element[4] =
3383   {
3384     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3385     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3386     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3387     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3388   };
3389   static int belt_base_switch_element[4] =
3390   {
3391     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3392     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3393     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3394     EL_CONVEYOR_BELT_4_SWITCH_LEFT
3395   };
3396   static int belt_move_dir[4] =
3397   {
3398     MV_LEFT,
3399     MV_NO_MOVING,
3400     MV_RIGHT,
3401     MV_NO_MOVING,
3402   };
3403
3404   int element = Feld[x][y];
3405   int belt_nr = getBeltNrFromBeltSwitchElement(element);
3406   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3407   int belt_dir = belt_move_dir[belt_dir_nr];
3408   int xx, yy, i;
3409
3410   if (!IS_BELT_SWITCH(element))
3411     return;
3412
3413   game.belt_dir_nr[belt_nr] = belt_dir_nr;
3414   game.belt_dir[belt_nr] = belt_dir;
3415
3416   if (belt_dir_nr == 3)
3417     belt_dir_nr = 1;
3418
3419   /* set frame order for belt animation graphic according to belt direction */
3420   for (i = 0; i < NUM_BELT_PARTS; i++)
3421   {
3422     int element = belt_base_active_element[belt_nr] + i;
3423     int graphic = el2img(element);
3424
3425     if (belt_dir == MV_LEFT)
3426       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3427     else
3428       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3429   }
3430
3431   for (yy = 0; yy < lev_fieldy; yy++)
3432   {
3433     for (xx = 0; xx < lev_fieldx; xx++)
3434     {
3435       int element = Feld[xx][yy];
3436
3437       if (IS_BELT_SWITCH(element))
3438       {
3439         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3440
3441         if (e_belt_nr == belt_nr)
3442         {
3443           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3444           DrawLevelField(xx, yy);
3445         }
3446       }
3447       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3448       {
3449         int e_belt_nr = getBeltNrFromBeltElement(element);
3450
3451         if (e_belt_nr == belt_nr)
3452         {
3453           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3454
3455           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3456           DrawLevelField(xx, yy);
3457         }
3458       }
3459       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3460       {
3461         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3462
3463         if (e_belt_nr == belt_nr)
3464         {
3465           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3466
3467           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3468           DrawLevelField(xx, yy);
3469         }
3470       }
3471     }
3472   }
3473 }
3474
3475 static void ToggleSwitchgateSwitch(int x, int y)
3476 {
3477   int xx, yy;
3478
3479   game.switchgate_pos = !game.switchgate_pos;
3480
3481   for (yy = 0; yy < lev_fieldy; yy++)
3482   {
3483     for (xx = 0; xx < lev_fieldx; xx++)
3484     {
3485       int element = Feld[xx][yy];
3486
3487       if (element == EL_SWITCHGATE_SWITCH_UP ||
3488           element == EL_SWITCHGATE_SWITCH_DOWN)
3489       {
3490         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3491         DrawLevelField(xx, yy);
3492       }
3493       else if (element == EL_SWITCHGATE_OPEN ||
3494                element == EL_SWITCHGATE_OPENING)
3495       {
3496         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3497 #if 1
3498         PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3499 #else
3500         PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3501 #endif
3502       }
3503       else if (element == EL_SWITCHGATE_CLOSED ||
3504                element == EL_SWITCHGATE_CLOSING)
3505       {
3506         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3507 #if 1
3508         PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3509 #else
3510         PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3511 #endif
3512       }
3513     }
3514   }
3515 }
3516
3517 static int getInvisibleActiveFromInvisibleElement(int element)
3518 {
3519   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3520           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
3521           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
3522           element);
3523 }
3524
3525 static int getInvisibleFromInvisibleActiveElement(int element)
3526 {
3527   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3528           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
3529           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
3530           element);
3531 }
3532
3533 static void RedrawAllLightSwitchesAndInvisibleElements()
3534 {
3535   int x, y;
3536
3537   for (y = 0; y < lev_fieldy; y++)
3538   {
3539     for (x = 0; x < lev_fieldx; x++)
3540     {
3541       int element = Feld[x][y];
3542
3543       if (element == EL_LIGHT_SWITCH &&
3544           game.light_time_left > 0)
3545       {
3546         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3547         DrawLevelField(x, y);
3548       }
3549       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3550                game.light_time_left == 0)
3551       {
3552         Feld[x][y] = EL_LIGHT_SWITCH;
3553         DrawLevelField(x, y);
3554       }
3555       else if (element == EL_INVISIBLE_STEELWALL ||
3556                element == EL_INVISIBLE_WALL ||
3557                element == EL_INVISIBLE_SAND)
3558       {
3559         if (game.light_time_left > 0)
3560           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3561
3562         DrawLevelField(x, y);
3563       }
3564       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3565                element == EL_INVISIBLE_WALL_ACTIVE ||
3566                element == EL_INVISIBLE_SAND_ACTIVE)
3567       {
3568         if (game.light_time_left == 0)
3569           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3570
3571         DrawLevelField(x, y);
3572       }
3573     }
3574   }
3575 }
3576
3577 static void ToggleLightSwitch(int x, int y)
3578 {
3579   int element = Feld[x][y];
3580
3581   game.light_time_left =
3582     (element == EL_LIGHT_SWITCH ?
3583      level.time_light * FRAMES_PER_SECOND : 0);
3584
3585   RedrawAllLightSwitchesAndInvisibleElements();
3586 }
3587
3588 static void ActivateTimegateSwitch(int x, int y)
3589 {
3590   int xx, yy;
3591
3592   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3593
3594   for (yy = 0; yy < lev_fieldy; yy++)
3595   {
3596     for (xx = 0; xx < lev_fieldx; xx++)
3597     {
3598       int element = Feld[xx][yy];
3599
3600       if (element == EL_TIMEGATE_CLOSED ||
3601           element == EL_TIMEGATE_CLOSING)
3602       {
3603         Feld[xx][yy] = EL_TIMEGATE_OPENING;
3604         PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3605       }
3606
3607       /*
3608       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3609       {
3610         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3611         DrawLevelField(xx, yy);
3612       }
3613       */
3614
3615     }
3616   }
3617
3618   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3619 }
3620
3621 inline static int getElementMoveStepsize(int x, int y)
3622 {
3623   int element = Feld[x][y];
3624   int direction = MovDir[x][y];
3625   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3626   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3627   int horiz_move = (dx != 0);
3628   int sign = (horiz_move ? dx : dy);
3629   int step = sign * element_info[element].move_stepsize;
3630
3631   /* special values for move stepsize for spring and things on conveyor belt */
3632   if (horiz_move)
3633   {
3634 #if 0
3635     if (element == EL_SPRING)
3636       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3637     else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3638              y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3639       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3640 #else
3641     if (CAN_FALL(element) &&
3642         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3643       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3644     else if (element == EL_SPRING)
3645       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3646 #endif
3647   }
3648
3649   return step;
3650 }
3651
3652 void Impact(int x, int y)
3653 {
3654   boolean lastline = (y == lev_fieldy-1);
3655   boolean object_hit = FALSE;
3656   boolean impact = (lastline || object_hit);
3657   int element = Feld[x][y];
3658   int smashed = EL_UNDEFINED;
3659
3660   if (!lastline)        /* check if element below was hit */
3661   {
3662     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3663       return;
3664
3665     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3666                                          MovDir[x][y + 1] != MV_DOWN ||
3667                                          MovPos[x][y + 1] <= TILEY / 2));
3668
3669 #if 0
3670     object_hit = !IS_FREE(x, y + 1);
3671 #endif
3672
3673     /* do not smash moving elements that left the smashed field in time */
3674     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3675         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3676       object_hit = FALSE;
3677
3678     if (object_hit)
3679       smashed = MovingOrBlocked2Element(x, y + 1);
3680
3681     impact = (lastline || object_hit);
3682   }
3683
3684   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
3685   {
3686     SplashAcid(x, y + 1);
3687     return;
3688   }
3689
3690   /* only reset graphic animation if graphic really changes after impact */
3691   if (impact &&
3692       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3693   {
3694     ResetGfxAnimation(x, y);
3695     DrawLevelField(x, y);
3696   }
3697
3698   if (impact && CAN_EXPLODE_IMPACT(element))
3699   {
3700     Bang(x, y);
3701     return;
3702   }
3703   else if (impact && element == EL_PEARL)
3704   {
3705     Feld[x][y] = EL_PEARL_BREAKING;
3706     PlayLevelSound(x, y, SND_PEARL_BREAKING);
3707     return;
3708   }
3709   else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3710   {
3711     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3712
3713     return;
3714   }
3715
3716   if (impact && element == EL_AMOEBA_DROP)
3717   {
3718     if (object_hit && IS_PLAYER(x, y + 1))
3719       KillHeroUnlessEnemyProtected(x, y + 1);
3720     else if (object_hit && smashed == EL_PENGUIN)
3721       Bang(x, y + 1);
3722     else
3723     {
3724       Feld[x][y] = EL_AMOEBA_GROWING;
3725       Store[x][y] = EL_AMOEBA_WET;
3726
3727       ResetRandomAnimationValue(x, y);
3728     }
3729     return;
3730   }
3731
3732   if (object_hit)               /* check which object was hit */
3733   {
3734     if (CAN_PASS_MAGIC_WALL(element) && 
3735         (smashed == EL_MAGIC_WALL ||
3736          smashed == EL_BD_MAGIC_WALL))
3737     {
3738       int xx, yy;
3739       int activated_magic_wall =
3740         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3741          EL_BD_MAGIC_WALL_ACTIVE);
3742
3743       /* activate magic wall / mill */
3744       for (yy = 0; yy < lev_fieldy; yy++)
3745         for (xx = 0; xx < lev_fieldx; xx++)
3746           if (Feld[xx][yy] == smashed)
3747             Feld[xx][yy] = activated_magic_wall;
3748
3749       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3750       game.magic_wall_active = TRUE;
3751
3752       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3753                             SND_MAGIC_WALL_ACTIVATING :
3754                             SND_BD_MAGIC_WALL_ACTIVATING));
3755     }
3756
3757     if (IS_PLAYER(x, y + 1))
3758     {
3759       if (CAN_SMASH_PLAYER(element))
3760       {
3761         KillHeroUnlessEnemyProtected(x, y + 1);
3762         return;
3763       }
3764     }
3765     else if (smashed == EL_PENGUIN)
3766     {
3767       if (CAN_SMASH_PLAYER(element))
3768       {
3769         Bang(x, y + 1);
3770         return;
3771       }
3772     }
3773     else if (element == EL_BD_DIAMOND)
3774     {
3775       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3776       {
3777         Bang(x, y + 1);
3778         return;
3779       }
3780     }
3781     else if (((element == EL_SP_INFOTRON ||
3782                element == EL_SP_ZONK) &&
3783               (smashed == EL_SP_SNIKSNAK ||
3784                smashed == EL_SP_ELECTRON ||
3785                smashed == EL_SP_DISK_ORANGE)) ||
3786              (element == EL_SP_INFOTRON &&
3787               smashed == EL_SP_DISK_YELLOW))
3788     {
3789       Bang(x, y + 1);
3790       return;
3791     }
3792 #if 0
3793     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3794     {
3795       Bang(x, y + 1);
3796       return;
3797     }
3798 #endif
3799     else if (CAN_SMASH_EVERYTHING(element))
3800     {
3801       if (IS_CLASSIC_ENEMY(smashed) ||
3802           CAN_EXPLODE_SMASHED(smashed))
3803       {
3804         Bang(x, y + 1);
3805         return;
3806       }
3807       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3808       {
3809         if (smashed == EL_LAMP ||
3810             smashed == EL_LAMP_ACTIVE)
3811         {
3812           Bang(x, y + 1);
3813           return;
3814         }
3815         else if (smashed == EL_NUT)
3816         {
3817           Feld[x][y + 1] = EL_NUT_BREAKING;
3818           PlayLevelSound(x, y, SND_NUT_BREAKING);
3819           RaiseScoreElement(EL_NUT);
3820           return;
3821         }
3822         else if (smashed == EL_PEARL)
3823         {
3824           Feld[x][y + 1] = EL_PEARL_BREAKING;
3825           PlayLevelSound(x, y, SND_PEARL_BREAKING);
3826           return;
3827         }
3828         else if (smashed == EL_DIAMOND)
3829         {
3830           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3831           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3832           return;
3833         }
3834         else if (IS_BELT_SWITCH(smashed))
3835         {
3836           ToggleBeltSwitch(x, y + 1);
3837         }
3838         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3839                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
3840         {
3841           ToggleSwitchgateSwitch(x, y + 1);
3842         }
3843         else if (smashed == EL_LIGHT_SWITCH ||
3844                  smashed == EL_LIGHT_SWITCH_ACTIVE)
3845         {
3846           ToggleLightSwitch(x, y + 1);
3847         }
3848         else
3849         {
3850 #if 0
3851           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
3852 #endif
3853
3854           CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3855
3856           CheckTriggeredElementChangeSide(x, y + 1, smashed,
3857                                           CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
3858           CheckElementChangeSide(x, y + 1, smashed, CE_SWITCHED, CH_SIDE_TOP);
3859         }
3860       }
3861       else
3862       {
3863         CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3864       }
3865     }
3866   }
3867
3868   /* play sound of magic wall / mill */
3869   if (!lastline &&
3870       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3871        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3872   {
3873     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3874       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3875     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3876       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3877
3878     return;
3879   }
3880
3881   /* play sound of object that hits the ground */
3882   if (lastline || object_hit)
3883     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3884 }
3885
3886 inline static void TurnRoundExt(int x, int y)
3887 {
3888   static struct
3889   {
3890     int x, y;
3891   } move_xy[] =
3892   {
3893     {  0,  0 },
3894     { -1,  0 },
3895     { +1,  0 },
3896     {  0,  0 },
3897     {  0, -1 },
3898     {  0,  0 }, { 0, 0 }, { 0, 0 },
3899     {  0, +1 }
3900   };
3901   static struct
3902   {
3903     int left, right, back;
3904   } turn[] =
3905   {
3906     { 0,        0,              0        },
3907     { MV_DOWN,  MV_UP,          MV_RIGHT },
3908     { MV_UP,    MV_DOWN,        MV_LEFT  },
3909     { 0,        0,              0        },
3910     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
3911     { 0,        0,              0        },
3912     { 0,        0,              0        },
3913     { 0,        0,              0        },
3914     { MV_RIGHT, MV_LEFT,        MV_UP    }
3915   };
3916
3917   int element = Feld[x][y];
3918   int move_pattern = element_info[element].move_pattern;
3919
3920   int old_move_dir = MovDir[x][y];
3921   int left_dir  = turn[old_move_dir].left;
3922   int right_dir = turn[old_move_dir].right;
3923   int back_dir  = turn[old_move_dir].back;
3924
3925   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
3926   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
3927   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
3928   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
3929
3930   int left_x  = x + left_dx,  left_y  = y + left_dy;
3931   int right_x = x + right_dx, right_y = y + right_dy;
3932   int move_x  = x + move_dx,  move_y  = y + move_dy;
3933
3934   int xx, yy;
3935
3936   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3937   {
3938     TestIfBadThingTouchesOtherBadThing(x, y);
3939
3940     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3941       MovDir[x][y] = right_dir;
3942     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3943       MovDir[x][y] = left_dir;
3944
3945     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3946       MovDelay[x][y] = 9;
3947     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
3948       MovDelay[x][y] = 1;
3949   }
3950   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3951            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3952   {
3953     TestIfBadThingTouchesOtherBadThing(x, y);
3954
3955     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3956       MovDir[x][y] = left_dir;
3957     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3958       MovDir[x][y] = right_dir;
3959
3960     if ((element == EL_SPACESHIP ||
3961          element == EL_SP_SNIKSNAK ||
3962          element == EL_SP_ELECTRON)
3963         && MovDir[x][y] != old_move_dir)
3964       MovDelay[x][y] = 9;
3965     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
3966       MovDelay[x][y] = 1;
3967   }
3968   else if (element == EL_YAMYAM)
3969   {
3970     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3971     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3972
3973     if (can_turn_left && can_turn_right)
3974       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3975     else if (can_turn_left)
3976       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3977     else if (can_turn_right)
3978       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3979     else
3980       MovDir[x][y] = back_dir;
3981
3982     MovDelay[x][y] = 16 + 16 * RND(3);
3983   }
3984   else if (element == EL_DARK_YAMYAM)
3985   {
3986     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3987     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3988
3989     if (can_turn_left && can_turn_right)
3990       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3991     else if (can_turn_left)
3992       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3993     else if (can_turn_right)
3994       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3995     else
3996       MovDir[x][y] = back_dir;
3997
3998     MovDelay[x][y] = 16 + 16 * RND(3);
3999   }
4000   else if (element == EL_PACMAN)
4001   {
4002     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
4003     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
4004
4005     if (can_turn_left && can_turn_right)
4006       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4007     else if (can_turn_left)
4008       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4009     else if (can_turn_right)
4010       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4011     else
4012       MovDir[x][y] = back_dir;
4013
4014     MovDelay[x][y] = 6 + RND(40);
4015   }
4016   else if (element == EL_PIG)
4017   {
4018     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(left_x, left_y);
4019     boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
4020     boolean can_move_on    = PIG_CAN_ENTER_FIELD(move_x, move_y);
4021     boolean should_turn_left, should_turn_right, should_move_on;
4022     int rnd_value = 24;
4023     int rnd = RND(rnd_value);
4024
4025     should_turn_left = (can_turn_left &&
4026                         (!can_move_on ||
4027                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4028                                                    y + back_dy + left_dy)));
4029     should_turn_right = (can_turn_right &&
4030                          (!can_move_on ||
4031                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4032                                                     y + back_dy + right_dy)));
4033     should_move_on = (can_move_on &&
4034                       (!can_turn_left ||
4035                        !can_turn_right ||
4036                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4037                                                  y + move_dy + left_dy) ||
4038                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4039                                                  y + move_dy + right_dy)));
4040
4041     if (should_turn_left || should_turn_right || should_move_on)
4042     {
4043       if (should_turn_left && should_turn_right && should_move_on)
4044         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
4045                         rnd < 2 * rnd_value / 3 ? right_dir :
4046                         old_move_dir);
4047       else if (should_turn_left && should_turn_right)
4048         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4049       else if (should_turn_left && should_move_on)
4050         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4051       else if (should_turn_right && should_move_on)
4052         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4053       else if (should_turn_left)
4054         MovDir[x][y] = left_dir;
4055       else if (should_turn_right)
4056         MovDir[x][y] = right_dir;
4057       else if (should_move_on)
4058         MovDir[x][y] = old_move_dir;
4059     }
4060     else if (can_move_on && rnd > rnd_value / 8)
4061       MovDir[x][y] = old_move_dir;
4062     else if (can_turn_left && can_turn_right)
4063       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4064     else if (can_turn_left && rnd > rnd_value / 8)
4065       MovDir[x][y] = left_dir;
4066     else if (can_turn_right && rnd > rnd_value/8)
4067       MovDir[x][y] = right_dir;
4068     else
4069       MovDir[x][y] = back_dir;
4070
4071     xx = x + move_xy[MovDir[x][y]].x;
4072     yy = y + move_xy[MovDir[x][y]].y;
4073
4074     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4075       MovDir[x][y] = old_move_dir;
4076
4077     MovDelay[x][y] = 0;
4078   }
4079   else if (element == EL_DRAGON)
4080   {
4081     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
4082     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
4083     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
4084     int rnd_value = 24;
4085     int rnd = RND(rnd_value);
4086
4087 #if 0
4088     if (FrameCounter < 1 && x == 0 && y == 29)
4089       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4090 #endif
4091
4092     if (can_move_on && rnd > rnd_value / 8)
4093       MovDir[x][y] = old_move_dir;
4094     else if (can_turn_left && can_turn_right)
4095       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4096     else if (can_turn_left && rnd > rnd_value / 8)
4097       MovDir[x][y] = left_dir;
4098     else if (can_turn_right && rnd > rnd_value / 8)
4099       MovDir[x][y] = right_dir;
4100     else
4101       MovDir[x][y] = back_dir;
4102
4103     xx = x + move_xy[MovDir[x][y]].x;
4104     yy = y + move_xy[MovDir[x][y]].y;
4105
4106 #if 0
4107     if (FrameCounter < 1 && x == 0 && y == 29)
4108       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4109              xx, yy, Feld[xx][yy],
4110              FrameCounter);
4111 #endif
4112
4113 #if 1
4114     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4115       MovDir[x][y] = old_move_dir;
4116 #else
4117     if (!IS_FREE(xx, yy))
4118       MovDir[x][y] = old_move_dir;
4119 #endif
4120
4121 #if 0
4122     if (FrameCounter < 1 && x == 0 && y == 29)
4123       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4124 #endif
4125
4126     MovDelay[x][y] = 0;
4127   }
4128   else if (element == EL_MOLE)
4129   {
4130     boolean can_move_on =
4131       (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4132                             IS_AMOEBOID(Feld[move_x][move_y]) ||
4133                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4134     if (!can_move_on)
4135     {
4136       boolean can_turn_left =
4137         (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4138                               IS_AMOEBOID(Feld[left_x][left_y])));
4139
4140       boolean can_turn_right =
4141         (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4142                               IS_AMOEBOID(Feld[right_x][right_y])));
4143
4144       if (can_turn_left && can_turn_right)
4145         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4146       else if (can_turn_left)
4147         MovDir[x][y] = left_dir;
4148       else
4149         MovDir[x][y] = right_dir;
4150     }
4151
4152     if (MovDir[x][y] != old_move_dir)
4153       MovDelay[x][y] = 9;
4154   }
4155   else if (element == EL_BALLOON)
4156   {
4157     MovDir[x][y] = game.balloon_dir;
4158     MovDelay[x][y] = 0;
4159   }
4160   else if (element == EL_SPRING)
4161   {
4162 #if 0
4163     if (MovDir[x][y] & MV_HORIZONTAL &&
4164         !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4165       MovDir[x][y] = MV_NO_MOVING;
4166 #else
4167     if (MovDir[x][y] & MV_HORIZONTAL &&
4168         (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4169          SPRING_CAN_ENTER_FIELD(x, y + 1)))
4170       MovDir[x][y] = MV_NO_MOVING;
4171 #endif
4172
4173     MovDelay[x][y] = 0;
4174   }
4175   else if (element == EL_ROBOT ||
4176            element == EL_SATELLITE ||
4177            element == EL_PENGUIN)
4178   {
4179     int attr_x = -1, attr_y = -1;
4180
4181     if (AllPlayersGone)
4182     {
4183       attr_x = ExitX;
4184       attr_y = ExitY;
4185     }
4186     else
4187     {
4188       int i;
4189
4190       for (i = 0; i < MAX_PLAYERS; i++)
4191       {
4192         struct PlayerInfo *player = &stored_player[i];
4193         int jx = player->jx, jy = player->jy;
4194
4195         if (!player->active)
4196           continue;
4197
4198         if (attr_x == -1 ||
4199             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4200         {
4201           attr_x = jx;
4202           attr_y = jy;
4203         }
4204       }
4205     }
4206
4207     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4208     {
4209       attr_x = ZX;
4210       attr_y = ZY;
4211     }
4212
4213     if (element == EL_PENGUIN)
4214     {
4215       int i;
4216       static int xy[4][2] =
4217       {
4218         { 0, -1 },
4219         { -1, 0 },
4220         { +1, 0 },
4221         { 0, +1 }
4222       };
4223
4224       for (i = 0; i < NUM_DIRECTIONS; i++)
4225       {
4226         int ex = x + xy[i][0];
4227         int ey = y + xy[i][1];
4228
4229         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4230         {
4231           attr_x = ex;
4232           attr_y = ey;
4233           break;
4234         }
4235       }
4236     }
4237
4238     MovDir[x][y] = MV_NO_MOVING;
4239     if (attr_x < x)
4240       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4241     else if (attr_x > x)
4242       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4243     if (attr_y < y)
4244       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4245     else if (attr_y > y)
4246       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4247
4248     if (element == EL_ROBOT)
4249     {
4250       int newx, newy;
4251
4252       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4253         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4254       Moving2Blocked(x, y, &newx, &newy);
4255
4256       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4257         MovDelay[x][y] = 8 + 8 * !RND(3);
4258       else
4259         MovDelay[x][y] = 16;
4260     }
4261     else if (element == EL_PENGUIN)
4262     {
4263       int newx, newy;
4264
4265       MovDelay[x][y] = 1;
4266
4267       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4268       {
4269         boolean first_horiz = RND(2);
4270         int new_move_dir = MovDir[x][y];
4271
4272         MovDir[x][y] =
4273           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4274         Moving2Blocked(x, y, &newx, &newy);
4275
4276         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4277           return;
4278
4279         MovDir[x][y] =
4280           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4281         Moving2Blocked(x, y, &newx, &newy);
4282
4283         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4284           return;
4285
4286         MovDir[x][y] = old_move_dir;
4287         return;
4288       }
4289     }
4290     else        /* (element == EL_SATELLITE) */
4291     {
4292       int newx, newy;
4293
4294       MovDelay[x][y] = 1;
4295
4296       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4297       {
4298         boolean first_horiz = RND(2);
4299         int new_move_dir = MovDir[x][y];
4300
4301         MovDir[x][y] =
4302           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4303         Moving2Blocked(x, y, &newx, &newy);
4304
4305         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4306           return;
4307
4308         MovDir[x][y] =
4309           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4310         Moving2Blocked(x, y, &newx, &newy);
4311
4312         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4313           return;
4314
4315         MovDir[x][y] = old_move_dir;
4316         return;
4317       }
4318     }
4319   }
4320   else if (move_pattern == MV_TURNING_LEFT ||
4321            move_pattern == MV_TURNING_RIGHT ||
4322            move_pattern == MV_TURNING_LEFT_RIGHT ||
4323            move_pattern == MV_TURNING_RIGHT_LEFT ||
4324            move_pattern == MV_TURNING_RANDOM ||
4325            move_pattern == MV_ALL_DIRECTIONS)
4326   {
4327     boolean can_turn_left =
4328       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4329     boolean can_turn_right =
4330       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4331
4332     if (move_pattern == MV_TURNING_LEFT)
4333       MovDir[x][y] = left_dir;
4334     else if (move_pattern == MV_TURNING_RIGHT)
4335       MovDir[x][y] = right_dir;
4336     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4337       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4338     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4339       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4340     else if (move_pattern == MV_TURNING_RANDOM)
4341       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4342                       can_turn_right && !can_turn_left ? right_dir :
4343                       RND(2) ? left_dir : right_dir);
4344     else if (can_turn_left && can_turn_right)
4345       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4346     else if (can_turn_left)
4347       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4348     else if (can_turn_right)
4349       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4350     else
4351       MovDir[x][y] = back_dir;
4352
4353     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4354   }
4355   else if (move_pattern == MV_HORIZONTAL ||
4356            move_pattern == MV_VERTICAL)
4357   {
4358     if (move_pattern & old_move_dir)
4359       MovDir[x][y] = back_dir;
4360     else if (move_pattern == MV_HORIZONTAL)
4361       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4362     else if (move_pattern == MV_VERTICAL)
4363       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4364
4365     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4366   }
4367   else if (move_pattern & MV_ANY_DIRECTION)
4368   {
4369     MovDir[x][y] = move_pattern;
4370     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4371   }
4372   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4373   {
4374     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4375       MovDir[x][y] = left_dir;
4376     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4377       MovDir[x][y] = right_dir;
4378
4379     if (MovDir[x][y] != old_move_dir)
4380       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4381   }
4382   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4383   {
4384     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4385       MovDir[x][y] = right_dir;
4386     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4387       MovDir[x][y] = left_dir;
4388
4389     if (MovDir[x][y] != old_move_dir)
4390       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4391   }
4392   else if (move_pattern == MV_TOWARDS_PLAYER ||
4393            move_pattern == MV_AWAY_FROM_PLAYER)
4394   {
4395     int attr_x = -1, attr_y = -1;
4396     int newx, newy;
4397     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4398
4399     if (AllPlayersGone)
4400     {
4401       attr_x = ExitX;
4402       attr_y = ExitY;
4403     }
4404     else
4405     {
4406       int i;
4407
4408       for (i = 0; i < MAX_PLAYERS; i++)
4409       {
4410         struct PlayerInfo *player = &stored_player[i];
4411         int jx = player->jx, jy = player->jy;
4412
4413         if (!player->active)
4414           continue;
4415
4416         if (attr_x == -1 ||
4417             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4418         {
4419           attr_x = jx;
4420           attr_y = jy;
4421         }
4422       }
4423     }
4424
4425     MovDir[x][y] = MV_NO_MOVING;
4426     if (attr_x < x)
4427       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4428     else if (attr_x > x)
4429       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4430     if (attr_y < y)
4431       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4432     else if (attr_y > y)
4433       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4434
4435     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4436
4437     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4438     {
4439       boolean first_horiz = RND(2);
4440       int new_move_dir = MovDir[x][y];
4441
4442       MovDir[x][y] =
4443         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4444       Moving2Blocked(x, y, &newx, &newy);
4445
4446       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4447         return;
4448
4449       MovDir[x][y] =
4450         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4451       Moving2Blocked(x, y, &newx, &newy);
4452
4453       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4454         return;
4455
4456       MovDir[x][y] = old_move_dir;
4457     }
4458   }
4459   else if (move_pattern == MV_WHEN_PUSHED ||
4460            move_pattern == MV_WHEN_DROPPED)
4461   {
4462     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4463       MovDir[x][y] = MV_NO_MOVING;
4464
4465     MovDelay[x][y] = 0;
4466   }
4467   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4468   {
4469     static int test_xy[7][2] =
4470     {
4471       { 0, -1 },
4472       { -1, 0 },
4473       { +1, 0 },
4474       { 0, +1 },
4475       { 0, -1 },
4476       { -1, 0 },
4477       { +1, 0 },
4478     };
4479     static int test_dir[7] =
4480     {
4481       MV_UP,
4482       MV_LEFT,
4483       MV_RIGHT,
4484       MV_DOWN,
4485       MV_UP,
4486       MV_LEFT,
4487       MV_RIGHT,
4488     };
4489     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4490     int move_preference = -1000000;     /* start with very low preference */
4491     int new_move_dir = MV_NO_MOVING;
4492     int start_test = RND(4);
4493     int i;
4494
4495     for (i = 0; i < NUM_DIRECTIONS; i++)
4496     {
4497       int move_dir = test_dir[start_test + i];
4498       int move_dir_preference;
4499
4500       xx = x + test_xy[start_test + i][0];
4501       yy = y + test_xy[start_test + i][1];
4502
4503       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4504           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4505       {
4506         new_move_dir = move_dir;
4507
4508         break;
4509       }
4510
4511       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4512         continue;
4513
4514       move_dir_preference = -1 * RunnerVisit[xx][yy];
4515       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4516         move_dir_preference = PlayerVisit[xx][yy];
4517
4518       if (move_dir_preference > move_preference)
4519       {
4520         /* prefer field that has not been visited for the longest time */
4521         move_preference = move_dir_preference;
4522         new_move_dir = move_dir;
4523       }
4524       else if (move_dir_preference == move_preference &&
4525                move_dir == old_move_dir)
4526       {
4527         /* prefer last direction when all directions are preferred equally */
4528         move_preference = move_dir_preference;
4529         new_move_dir = move_dir;
4530       }
4531     }
4532
4533     MovDir[x][y] = new_move_dir;
4534     if (old_move_dir != new_move_dir)
4535       MovDelay[x][y] = 9;
4536   }
4537 }
4538
4539 static void TurnRound(int x, int y)
4540 {
4541   int direction = MovDir[x][y];
4542
4543 #if 0
4544   GfxDir[x][y] = MovDir[x][y];
4545 #endif
4546
4547   TurnRoundExt(x, y);
4548
4549 #if 1
4550   GfxDir[x][y] = MovDir[x][y];
4551 #endif
4552
4553   if (direction != MovDir[x][y])
4554     GfxFrame[x][y] = 0;
4555
4556 #if 1
4557   if (MovDelay[x][y])
4558     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4559 #else
4560   if (MovDelay[x][y])
4561     GfxAction[x][y] = ACTION_WAITING;
4562 #endif
4563 }
4564
4565 static boolean JustBeingPushed(int x, int y)
4566 {
4567   int i;
4568
4569   for (i = 0; i < MAX_PLAYERS; i++)
4570   {
4571     struct PlayerInfo *player = &stored_player[i];
4572
4573     if (player->active && player->is_pushing && player->MovPos)
4574     {
4575       int next_jx = player->jx + (player->jx - player->last_jx);
4576       int next_jy = player->jy + (player->jy - player->last_jy);
4577
4578       if (x == next_jx && y == next_jy)
4579         return TRUE;
4580     }
4581   }
4582
4583   return FALSE;
4584 }
4585
4586 void StartMoving(int x, int y)
4587 {
4588 #if 0
4589   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4590 #endif
4591   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4592   int element = Feld[x][y];
4593
4594   if (Stop[x][y])
4595     return;
4596
4597 #if 1
4598   if (MovDelay[x][y] == 0)
4599     GfxAction[x][y] = ACTION_DEFAULT;
4600 #else
4601   /* !!! this should be handled more generic (not only for mole) !!! */
4602   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4603     GfxAction[x][y] = ACTION_DEFAULT;
4604 #endif
4605
4606   if (CAN_FALL(element) && y < lev_fieldy - 1)
4607   {
4608     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
4609         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4610       if (JustBeingPushed(x, y))
4611         return;
4612
4613     if (element == EL_QUICKSAND_FULL)
4614     {
4615       if (IS_FREE(x, y + 1))
4616       {
4617         InitMovingField(x, y, MV_DOWN);
4618         started_moving = TRUE;
4619
4620         Feld[x][y] = EL_QUICKSAND_EMPTYING;
4621         Store[x][y] = EL_ROCK;
4622 #if 1
4623         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4624 #else
4625         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4626 #endif
4627       }
4628       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4629       {
4630         if (!MovDelay[x][y])
4631           MovDelay[x][y] = TILEY + 1;
4632
4633         if (MovDelay[x][y])
4634         {
4635           MovDelay[x][y]--;
4636           if (MovDelay[x][y])
4637             return;
4638         }
4639
4640         Feld[x][y] = EL_QUICKSAND_EMPTY;
4641         Feld[x][y + 1] = EL_QUICKSAND_FULL;
4642         Store[x][y + 1] = Store[x][y];
4643         Store[x][y] = 0;
4644 #if 1
4645         PlayLevelSoundAction(x, y, ACTION_FILLING);
4646 #else
4647         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4648 #endif
4649       }
4650     }
4651     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4652              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4653     {
4654       InitMovingField(x, y, MV_DOWN);
4655       started_moving = TRUE;
4656
4657       Feld[x][y] = EL_QUICKSAND_FILLING;
4658       Store[x][y] = element;
4659 #if 1
4660       PlayLevelSoundAction(x, y, ACTION_FILLING);
4661 #else
4662       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4663 #endif
4664     }
4665     else if (element == EL_MAGIC_WALL_FULL)
4666     {
4667       if (IS_FREE(x, y + 1))
4668       {
4669         InitMovingField(x, y, MV_DOWN);
4670         started_moving = TRUE;
4671
4672         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4673         Store[x][y] = EL_CHANGED(Store[x][y]);
4674       }
4675       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4676       {
4677         if (!MovDelay[x][y])
4678           MovDelay[x][y] = TILEY/4 + 1;
4679
4680         if (MovDelay[x][y])
4681         {
4682           MovDelay[x][y]--;
4683           if (MovDelay[x][y])
4684             return;
4685         }
4686
4687         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4688         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4689         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4690         Store[x][y] = 0;
4691       }
4692     }
4693     else if (element == EL_BD_MAGIC_WALL_FULL)
4694     {
4695       if (IS_FREE(x, y + 1))
4696       {
4697         InitMovingField(x, y, MV_DOWN);
4698         started_moving = TRUE;
4699
4700         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4701         Store[x][y] = EL_CHANGED2(Store[x][y]);
4702       }
4703       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4704       {
4705         if (!MovDelay[x][y])
4706           MovDelay[x][y] = TILEY/4 + 1;
4707
4708         if (MovDelay[x][y])
4709         {
4710           MovDelay[x][y]--;
4711           if (MovDelay[x][y])
4712             return;
4713         }
4714
4715         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4716         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4717         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4718         Store[x][y] = 0;
4719       }
4720     }
4721     else if (CAN_PASS_MAGIC_WALL(element) &&
4722              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4723               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4724     {
4725       InitMovingField(x, y, MV_DOWN);
4726       started_moving = TRUE;
4727
4728       Feld[x][y] =
4729         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4730          EL_BD_MAGIC_WALL_FILLING);
4731       Store[x][y] = element;
4732     }
4733 #if 0
4734     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4735 #else
4736     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4737 #endif
4738     {
4739       SplashAcid(x, y + 1);
4740
4741       InitMovingField(x, y, MV_DOWN);
4742       started_moving = TRUE;
4743
4744       Store[x][y] = EL_ACID;
4745 #if 0
4746       /* !!! TEST !!! better use "_FALLING" etc. !!! */
4747       GfxAction[x][y + 1] = ACTION_ACTIVE;
4748 #endif
4749     }
4750 #if 1
4751     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4752               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4753               (Feld[x][y + 1] == EL_BLOCKED)) ||
4754              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4755               CAN_SMASH(element) && WasJustFalling[x][y] &&
4756               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4757
4758 #else
4759 #if 1
4760     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4761              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4762              WasJustMoving[x][y] && !Pushed[x][y + 1])
4763 #else
4764     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4765              WasJustMoving[x][y])
4766 #endif
4767 #endif
4768
4769     {
4770       /* this is needed for a special case not covered by calling "Impact()"
4771          from "ContinueMoving()": if an element moves to a tile directly below
4772          another element which was just falling on that tile (which was empty
4773          in the previous frame), the falling element above would just stop
4774          instead of smashing the element below (in previous version, the above
4775          element was just checked for "moving" instead of "falling", resulting
4776          in incorrect smashes caused by horizontal movement of the above
4777          element; also, the case of the player being the element to smash was
4778          simply not covered here... :-/ ) */
4779
4780 #if 0
4781       WasJustMoving[x][y] = 0;
4782       WasJustFalling[x][y] = 0;
4783 #endif
4784
4785       Impact(x, y);
4786     }
4787     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4788     {
4789       if (MovDir[x][y] == MV_NO_MOVING)
4790       {
4791         InitMovingField(x, y, MV_DOWN);
4792         started_moving = TRUE;
4793       }
4794     }
4795     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4796     {
4797       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4798         MovDir[x][y] = MV_DOWN;
4799
4800       InitMovingField(x, y, MV_DOWN);
4801       started_moving = TRUE;
4802     }
4803     else if (element == EL_AMOEBA_DROP)
4804     {
4805       Feld[x][y] = EL_AMOEBA_GROWING;
4806       Store[x][y] = EL_AMOEBA_WET;
4807     }
4808     /* Store[x][y + 1] must be zero, because:
4809        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4810     */
4811 #if 0
4812 #if OLD_GAME_BEHAVIOUR
4813     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4814 #else
4815     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4816              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4817              element != EL_DX_SUPABOMB)
4818 #endif
4819 #else
4820     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4821               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4822              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4823              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4824 #endif
4825     {
4826       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
4827                                 (IS_FREE(x - 1, y + 1) ||
4828                                  Feld[x - 1][y + 1] == EL_ACID));
4829       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4830                                 (IS_FREE(x + 1, y + 1) ||
4831                                  Feld[x + 1][y + 1] == EL_ACID));
4832       boolean can_fall_any  = (can_fall_left || can_fall_right);
4833       boolean can_fall_both = (can_fall_left && can_fall_right);
4834
4835       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4836       {
4837         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4838
4839         if (slippery_type == SLIPPERY_ONLY_LEFT)
4840           can_fall_right = FALSE;
4841         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4842           can_fall_left = FALSE;
4843         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4844           can_fall_right = FALSE;
4845         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4846           can_fall_left = FALSE;
4847
4848         can_fall_any  = (can_fall_left || can_fall_right);
4849         can_fall_both = (can_fall_left && can_fall_right);
4850       }
4851
4852       if (can_fall_any)
4853       {
4854         if (can_fall_both &&
4855             (game.emulation != EMU_BOULDERDASH &&
4856              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4857           can_fall_left = !(can_fall_right = RND(2));
4858
4859         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4860         started_moving = TRUE;
4861       }
4862     }
4863 #if 0
4864     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4865 #else
4866     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4867 #endif
4868     {
4869       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4870       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4871       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4872       int belt_dir = game.belt_dir[belt_nr];
4873
4874       if ((belt_dir == MV_LEFT  && left_is_free) ||
4875           (belt_dir == MV_RIGHT && right_is_free))
4876       {
4877 #if 1
4878         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4879 #endif
4880
4881         InitMovingField(x, y, belt_dir);
4882         started_moving = TRUE;
4883
4884 #if 1
4885         Pushed[x][y] = TRUE;
4886         Pushed[nextx][y] = TRUE;
4887 #endif
4888
4889         GfxAction[x][y] = ACTION_DEFAULT;
4890       }
4891       else
4892       {
4893         MovDir[x][y] = 0;       /* if element was moving, stop it */
4894       }
4895     }
4896   }
4897
4898   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4899   if (CAN_MOVE(element) && !started_moving)
4900   {
4901     int move_pattern = element_info[element].move_pattern;
4902     int newx, newy;
4903
4904     Moving2Blocked(x, y, &newx, &newy);
4905
4906 #if 1
4907     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4908       return;
4909 #else
4910     if ((element == EL_SATELLITE ||
4911          element == EL_BALLOON ||
4912          element == EL_SPRING)
4913         && JustBeingPushed(x, y))
4914       return;
4915 #endif
4916
4917 #if 1
4918     if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4919         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4920         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4921     {
4922 #if 0
4923       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4924              element, element_info[element].token_name,
4925              WasJustMoving[x][y],
4926              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4927              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4928              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4929              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4930 #endif
4931
4932 #if 1
4933       WasJustMoving[x][y] = 0;
4934 #endif
4935
4936       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4937
4938 #if 0
4939       if (Feld[x][y] != element)        /* element has changed */
4940       {
4941         element = Feld[x][y];
4942         move_pattern = element_info[element].move_pattern;
4943
4944         if (!CAN_MOVE(element))
4945           return;
4946       }
4947 #else
4948       if (Feld[x][y] != element)        /* element has changed */
4949         return;
4950 #endif
4951     }
4952 #endif
4953
4954 #if 0
4955 #if 0
4956     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4957       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
4958 #else
4959     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4960     {
4961       Moving2Blocked(x, y, &newx, &newy);
4962       if (Feld[newx][newy] == EL_BLOCKED)
4963         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
4964     }
4965 #endif
4966 #endif
4967
4968 #if 0
4969     if (FrameCounter < 1 && x == 0 && y == 29)
4970       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4971 #endif
4972
4973     if (!MovDelay[x][y])        /* start new movement phase */
4974     {
4975       /* all objects that can change their move direction after each step
4976          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4977
4978       if (element != EL_YAMYAM &&
4979           element != EL_DARK_YAMYAM &&
4980           element != EL_PACMAN &&
4981           !(move_pattern & MV_ANY_DIRECTION) &&
4982           move_pattern != MV_TURNING_LEFT &&
4983           move_pattern != MV_TURNING_RIGHT &&
4984           move_pattern != MV_TURNING_LEFT_RIGHT &&
4985           move_pattern != MV_TURNING_RIGHT_LEFT &&
4986           move_pattern != MV_TURNING_RANDOM)
4987       {
4988         TurnRound(x, y);
4989
4990 #if 0
4991         if (FrameCounter < 1 && x == 0 && y == 29)
4992           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4993 #endif
4994
4995         if (MovDelay[x][y] && (element == EL_BUG ||
4996                                element == EL_SPACESHIP ||
4997                                element == EL_SP_SNIKSNAK ||
4998                                element == EL_SP_ELECTRON ||
4999                                element == EL_MOLE))
5000           DrawLevelField(x, y);
5001       }
5002     }
5003
5004     if (MovDelay[x][y])         /* wait some time before next movement */
5005     {
5006       MovDelay[x][y]--;
5007
5008 #if 0
5009       if (element == EL_YAMYAM)
5010       {
5011         printf("::: %d\n",
5012                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5013         DrawLevelElementAnimation(x, y, element);
5014       }
5015 #endif
5016
5017       if (MovDelay[x][y])       /* element still has to wait some time */
5018       {
5019 #if 0
5020         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5021         ResetGfxAnimation(x, y);
5022 #endif
5023
5024 #if 0
5025         if (GfxAction[x][y] != ACTION_WAITING)
5026           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5027
5028         GfxAction[x][y] = ACTION_WAITING;
5029 #endif
5030       }
5031
5032       if (element == EL_ROBOT ||
5033 #if 0
5034           element == EL_PACMAN ||
5035 #endif
5036           element == EL_YAMYAM ||
5037           element == EL_DARK_YAMYAM)
5038       {
5039 #if 0
5040         DrawLevelElementAnimation(x, y, element);
5041 #else
5042         DrawLevelElementAnimationIfNeeded(x, y, element);
5043 #endif
5044         PlayLevelSoundAction(x, y, ACTION_WAITING);
5045       }
5046       else if (element == EL_SP_ELECTRON)
5047         DrawLevelElementAnimationIfNeeded(x, y, element);
5048       else if (element == EL_DRAGON)
5049       {
5050         int i;
5051         int dir = MovDir[x][y];
5052         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5053         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5054         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
5055                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
5056                        dir == MV_UP     ? IMG_FLAMES_1_UP :
5057                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5058         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5059
5060 #if 0
5061         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5062 #endif
5063
5064         GfxAction[x][y] = ACTION_ATTACKING;
5065
5066         if (IS_PLAYER(x, y))
5067           DrawPlayerField(x, y);
5068         else
5069           DrawLevelField(x, y);
5070
5071         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5072
5073         for (i = 1; i <= 3; i++)
5074         {
5075           int xx = x + i * dx;
5076           int yy = y + i * dy;
5077           int sx = SCREENX(xx);
5078           int sy = SCREENY(yy);
5079           int flame_graphic = graphic + (i - 1);
5080
5081           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5082             break;
5083
5084           if (MovDelay[x][y])
5085           {
5086             int flamed = MovingOrBlocked2Element(xx, yy);
5087
5088             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5089               Bang(xx, yy);
5090             else
5091               RemoveMovingField(xx, yy);
5092
5093             Feld[xx][yy] = EL_FLAMES;
5094             if (IN_SCR_FIELD(sx, sy))
5095             {
5096               DrawLevelFieldCrumbledSand(xx, yy);
5097               DrawGraphic(sx, sy, flame_graphic, frame);
5098             }
5099           }
5100           else
5101           {
5102             if (Feld[xx][yy] == EL_FLAMES)
5103               Feld[xx][yy] = EL_EMPTY;
5104             DrawLevelField(xx, yy);
5105           }
5106         }
5107       }
5108
5109       if (MovDelay[x][y])       /* element still has to wait some time */
5110       {
5111         PlayLevelSoundAction(x, y, ACTION_WAITING);
5112
5113         return;
5114       }
5115
5116 #if 0
5117       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5118          for all other elements GfxAction will be set by InitMovingField() */
5119       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5120         GfxAction[x][y] = ACTION_MOVING;
5121 #endif
5122     }
5123
5124     /* now make next step */
5125
5126     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5127
5128     if (DONT_COLLIDE_WITH(element) &&
5129         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5130         !PLAYER_ENEMY_PROTECTED(newx, newy))
5131     {
5132 #if 1
5133       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5134
5135       return;
5136 #else
5137       /* player killed by element which is deadly when colliding with */
5138       MovDir[x][y] = 0;
5139       KillHero(PLAYERINFO(newx, newy));
5140       return;
5141 #endif
5142
5143     }
5144 #if 1
5145 #if 1
5146     else if (CAN_MOVE_INTO_ACID(element) &&
5147              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5148              (MovDir[x][y] == MV_DOWN ||
5149               game.engine_version > VERSION_IDENT(3,0,8,0)))
5150 #else
5151     else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5152              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5153 #endif
5154 #else
5155
5156     else if ((element == EL_PENGUIN ||
5157               element == EL_ROBOT ||
5158               element == EL_SATELLITE ||
5159               element == EL_BALLOON ||
5160               IS_CUSTOM_ELEMENT(element)) &&
5161              IN_LEV_FIELD(newx, newy) &&
5162              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5163 #endif
5164     {
5165       SplashAcid(newx, newy);
5166       Store[x][y] = EL_ACID;
5167     }
5168     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5169     {
5170       if (Feld[newx][newy] == EL_EXIT_OPEN)
5171       {
5172 #if 1
5173         RemoveField(x, y);
5174         DrawLevelField(x, y);
5175 #else
5176         Feld[x][y] = EL_EMPTY;
5177         DrawLevelField(x, y);
5178 #endif
5179
5180         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5181         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5182           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5183
5184         local_player->friends_still_needed--;
5185         if (!local_player->friends_still_needed &&
5186             !local_player->GameOver && AllPlayersGone)
5187           local_player->LevelSolved = local_player->GameOver = TRUE;
5188
5189         return;
5190       }
5191       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5192       {
5193         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5194           DrawLevelField(newx, newy);
5195         else
5196           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5197       }
5198       else if (!IS_FREE(newx, newy))
5199       {
5200         GfxAction[x][y] = ACTION_WAITING;
5201
5202         if (IS_PLAYER(x, y))
5203           DrawPlayerField(x, y);
5204         else
5205           DrawLevelField(x, y);
5206
5207         return;
5208       }
5209     }
5210     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5211     {
5212       if (IS_FOOD_PIG(Feld[newx][newy]))
5213       {
5214         if (IS_MOVING(newx, newy))
5215           RemoveMovingField(newx, newy);
5216         else
5217         {
5218           Feld[newx][newy] = EL_EMPTY;
5219           DrawLevelField(newx, newy);
5220         }
5221
5222         PlayLevelSound(x, y, SND_PIG_DIGGING);
5223       }
5224       else if (!IS_FREE(newx, newy))
5225       {
5226         if (IS_PLAYER(x, y))
5227           DrawPlayerField(x, y);
5228         else
5229           DrawLevelField(x, y);
5230
5231         return;
5232       }
5233     }
5234
5235 #if 1
5236
5237     /*
5238     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5239     */
5240
5241     else if (IS_CUSTOM_ELEMENT(element) &&
5242              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5243
5244 #if 0
5245  &&
5246              !IS_FREE(newx, newy)
5247 #endif
5248
5249 )
5250     {
5251       int new_element = Feld[newx][newy];
5252
5253 #if 0
5254       printf("::: '%s' digs '%s' [%d]\n",
5255              element_info[element].token_name,
5256              element_info[Feld[newx][newy]].token_name,
5257              StorePlayer[newx][newy]);
5258 #endif
5259
5260       if (!IS_FREE(newx, newy))
5261       {
5262         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5263                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5264                       ACTION_BREAKING);
5265
5266         /* no element can dig solid indestructible elements */
5267         if (IS_INDESTRUCTIBLE(new_element) &&
5268             !IS_DIGGABLE(new_element) &&
5269             !IS_COLLECTIBLE(new_element))
5270           return;
5271
5272         if (AmoebaNr[newx][newy] &&
5273             (new_element == EL_AMOEBA_FULL ||
5274              new_element == EL_BD_AMOEBA ||
5275              new_element == EL_AMOEBA_GROWING))
5276         {
5277           AmoebaCnt[AmoebaNr[newx][newy]]--;
5278           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5279         }
5280
5281         if (IS_MOVING(newx, newy))
5282           RemoveMovingField(newx, newy);
5283         else
5284         {
5285           RemoveField(newx, newy);
5286           DrawLevelField(newx, newy);
5287         }
5288
5289         PlayLevelSoundAction(x, y, action);
5290       }
5291
5292       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5293         element_info[element].can_leave_element = TRUE;
5294
5295       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5296       {
5297         RunnerVisit[x][y] = FrameCounter;
5298         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5299       }
5300     }
5301
5302 #endif
5303
5304     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5305     {
5306       if (!IS_FREE(newx, newy))
5307       {
5308         if (IS_PLAYER(x, y))
5309           DrawPlayerField(x, y);
5310         else
5311           DrawLevelField(x, y);
5312
5313         return;
5314       }
5315       else
5316       {
5317         boolean wanna_flame = !RND(10);
5318         int dx = newx - x, dy = newy - y;
5319         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5320         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5321         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5322                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5323         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5324                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5325
5326         if ((wanna_flame ||
5327              IS_CLASSIC_ENEMY(element1) ||
5328              IS_CLASSIC_ENEMY(element2)) &&
5329             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5330             element1 != EL_FLAMES && element2 != EL_FLAMES)
5331         {
5332 #if 1
5333           ResetGfxAnimation(x, y);
5334           GfxAction[x][y] = ACTION_ATTACKING;
5335 #endif
5336
5337           if (IS_PLAYER(x, y))
5338             DrawPlayerField(x, y);
5339           else
5340             DrawLevelField(x, y);
5341
5342           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5343
5344           MovDelay[x][y] = 50;
5345
5346           Feld[newx][newy] = EL_FLAMES;
5347           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5348             Feld[newx1][newy1] = EL_FLAMES;
5349           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5350             Feld[newx2][newy2] = EL_FLAMES;
5351
5352           return;
5353         }
5354       }
5355     }
5356     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5357              Feld[newx][newy] == EL_DIAMOND)
5358     {
5359       if (IS_MOVING(newx, newy))
5360         RemoveMovingField(newx, newy);
5361       else
5362       {
5363         Feld[newx][newy] = EL_EMPTY;
5364         DrawLevelField(newx, newy);
5365       }
5366
5367       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5368     }
5369     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5370              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5371     {
5372       if (AmoebaNr[newx][newy])
5373       {
5374         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5375         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5376             Feld[newx][newy] == EL_BD_AMOEBA)
5377           AmoebaCnt[AmoebaNr[newx][newy]]--;
5378       }
5379
5380 #if 0
5381       /* !!! test !!! */
5382       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5383 #else
5384       if (IS_MOVING(newx, newy))
5385 #endif
5386       {
5387         RemoveMovingField(newx, newy);
5388       }
5389       else
5390       {
5391         Feld[newx][newy] = EL_EMPTY;
5392         DrawLevelField(newx, newy);
5393       }
5394
5395       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5396     }
5397     else if ((element == EL_PACMAN || element == EL_MOLE)
5398              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5399     {
5400       if (AmoebaNr[newx][newy])
5401       {
5402         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5403         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5404             Feld[newx][newy] == EL_BD_AMOEBA)
5405           AmoebaCnt[AmoebaNr[newx][newy]]--;
5406       }
5407
5408       if (element == EL_MOLE)
5409       {
5410         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5411         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5412
5413         ResetGfxAnimation(x, y);
5414         GfxAction[x][y] = ACTION_DIGGING;
5415         DrawLevelField(x, y);
5416
5417         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5418
5419         return;                         /* wait for shrinking amoeba */
5420       }
5421       else      /* element == EL_PACMAN */
5422       {
5423         Feld[newx][newy] = EL_EMPTY;
5424         DrawLevelField(newx, newy);
5425         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5426       }
5427     }
5428     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5429              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5430               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5431     {
5432       /* wait for shrinking amoeba to completely disappear */
5433       return;
5434     }
5435     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5436     {
5437       /* object was running against a wall */
5438
5439       TurnRound(x, y);
5440
5441 #if 1
5442       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5443         DrawLevelElementAnimation(x, y, element);
5444 #else
5445       if (element == EL_BUG ||
5446           element == EL_SPACESHIP ||
5447           element == EL_SP_SNIKSNAK)
5448         DrawLevelField(x, y);
5449       else if (element == EL_MOLE)
5450         DrawLevelField(x, y);
5451       else if (element == EL_BD_BUTTERFLY ||
5452                element == EL_BD_FIREFLY)
5453         DrawLevelElementAnimationIfNeeded(x, y, element);
5454       else if (element == EL_SATELLITE)
5455         DrawLevelElementAnimationIfNeeded(x, y, element);
5456       else if (element == EL_SP_ELECTRON)
5457         DrawLevelElementAnimationIfNeeded(x, y, element);
5458 #endif
5459
5460       if (DONT_TOUCH(element))
5461         TestIfBadThingTouchesHero(x, y);
5462
5463 #if 0
5464       PlayLevelSoundAction(x, y, ACTION_WAITING);
5465 #endif
5466
5467       return;
5468     }
5469
5470     InitMovingField(x, y, MovDir[x][y]);
5471
5472     PlayLevelSoundAction(x, y, ACTION_MOVING);
5473   }
5474
5475   if (MovDir[x][y])
5476     ContinueMoving(x, y);
5477 }
5478
5479 void ContinueMoving(int x, int y)
5480 {
5481   int element = Feld[x][y];
5482   struct ElementInfo *ei = &element_info[element];
5483   int direction = MovDir[x][y];
5484   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5485   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5486   int newx = x + dx, newy = y + dy;
5487 #if 0
5488   int nextx = newx + dx, nexty = newy + dy;
5489 #endif
5490 #if 1
5491   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
5492   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5493 #else
5494   boolean pushed_by_player = Pushed[x][y];
5495 #endif
5496
5497   MovPos[x][y] += getElementMoveStepsize(x, y);
5498
5499 #if 0
5500   if (pushed_by_player && IS_PLAYER(x, y))
5501   {
5502     /* special case: moving object pushed by player */
5503     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5504   }
5505 #else
5506   if (pushed_by_player) /* special case: moving object pushed by player */
5507     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5508 #endif
5509
5510   if (ABS(MovPos[x][y]) < TILEX)
5511   {
5512     DrawLevelField(x, y);
5513
5514     return;     /* element is still moving */
5515   }
5516
5517   /* element reached destination field */
5518
5519   Feld[x][y] = EL_EMPTY;
5520   Feld[newx][newy] = element;
5521   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
5522
5523   if (element == EL_MOLE)
5524   {
5525     Feld[x][y] = EL_SAND;
5526
5527     DrawLevelFieldCrumbledSandNeighbours(x, y);
5528   }
5529   else if (element == EL_QUICKSAND_FILLING)
5530   {
5531     element = Feld[newx][newy] = get_next_element(element);
5532     Store[newx][newy] = Store[x][y];
5533   }
5534   else if (element == EL_QUICKSAND_EMPTYING)
5535   {
5536     Feld[x][y] = get_next_element(element);
5537     element = Feld[newx][newy] = Store[x][y];
5538   }
5539   else if (element == EL_MAGIC_WALL_FILLING)
5540   {
5541     element = Feld[newx][newy] = get_next_element(element);
5542     if (!game.magic_wall_active)
5543       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5544     Store[newx][newy] = Store[x][y];
5545   }
5546   else if (element == EL_MAGIC_WALL_EMPTYING)
5547   {
5548     Feld[x][y] = get_next_element(element);
5549     if (!game.magic_wall_active)
5550       Feld[x][y] = EL_MAGIC_WALL_DEAD;
5551     element = Feld[newx][newy] = Store[x][y];
5552   }
5553   else if (element == EL_BD_MAGIC_WALL_FILLING)
5554   {
5555     element = Feld[newx][newy] = get_next_element(element);
5556     if (!game.magic_wall_active)
5557       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5558     Store[newx][newy] = Store[x][y];
5559   }
5560   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5561   {
5562     Feld[x][y] = get_next_element(element);
5563     if (!game.magic_wall_active)
5564       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5565     element = Feld[newx][newy] = Store[x][y];
5566   }
5567   else if (element == EL_AMOEBA_DROPPING)
5568   {
5569     Feld[x][y] = get_next_element(element);
5570     element = Feld[newx][newy] = Store[x][y];
5571   }
5572   else if (element == EL_SOKOBAN_OBJECT)
5573   {
5574     if (Back[x][y])
5575       Feld[x][y] = Back[x][y];
5576
5577     if (Back[newx][newy])
5578       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5579
5580     Back[x][y] = Back[newx][newy] = 0;
5581   }
5582   else if (Store[x][y] == EL_ACID)
5583   {
5584     element = Feld[newx][newy] = EL_ACID;
5585   }
5586
5587   Store[x][y] = 0;
5588   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5589   MovDelay[newx][newy] = 0;
5590
5591   /* copy element change control values to new field */
5592   ChangeDelay[newx][newy] = ChangeDelay[x][y];
5593   ChangePage[newx][newy] = ChangePage[x][y];
5594   Changed[newx][newy] = Changed[x][y];
5595   ChangeEvent[newx][newy] = ChangeEvent[x][y];
5596
5597   ChangeDelay[x][y] = 0;
5598   ChangePage[x][y] = -1;
5599   Changed[x][y] = CE_BITMASK_DEFAULT;
5600   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5601
5602   /* copy animation control values to new field */
5603   GfxFrame[newx][newy]  = GfxFrame[x][y];
5604   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
5605   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
5606   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
5607
5608   Pushed[x][y] = Pushed[newx][newy] = FALSE;
5609
5610   ResetGfxAnimation(x, y);      /* reset animation values for old field */
5611
5612 #if 1
5613   /* some elements can leave other elements behind after moving */
5614   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5615       ei->move_leave_element != EL_EMPTY &&
5616       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5617        ei->can_leave_element_last))
5618   {
5619     Feld[x][y] = ei->move_leave_element;
5620     InitField(x, y, FALSE);
5621
5622     if (GFX_CRUMBLED(Feld[x][y]))
5623       DrawLevelFieldCrumbledSandNeighbours(x, y);
5624   }
5625
5626   ei->can_leave_element_last = ei->can_leave_element;
5627   ei->can_leave_element = FALSE;
5628 #endif
5629
5630 #if 0
5631   /* 2.1.1 (does not work correctly for spring) */
5632   if (!CAN_MOVE(element))
5633     MovDir[newx][newy] = 0;
5634 #else
5635
5636 #if 0
5637   /* (does not work for falling objects that slide horizontally) */
5638   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5639     MovDir[newx][newy] = 0;
5640 #else
5641   /*
5642   if (!CAN_MOVE(element) ||
5643       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5644     MovDir[newx][newy] = 0;
5645   */
5646
5647   if (!CAN_MOVE(element) ||
5648       (CAN_FALL(element) && direction == MV_DOWN))
5649     GfxDir[x][y] = MovDir[newx][newy] = 0;
5650
5651 #endif
5652 #endif
5653
5654   DrawLevelField(x, y);
5655   DrawLevelField(newx, newy);
5656
5657   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
5658
5659   /* prevent pushed element from moving on in pushed direction */
5660   if (pushed_by_player && CAN_MOVE(element) &&
5661       element_info[element].move_pattern & MV_ANY_DIRECTION &&
5662       !(element_info[element].move_pattern & direction))
5663     TurnRound(newx, newy);
5664
5665 #if 1
5666   /* prevent elements on conveyor belt from moving on in last direction */
5667   if (pushed_by_conveyor && CAN_FALL(element) &&
5668       direction & MV_HORIZONTAL)
5669     MovDir[newx][newy] = 0;
5670 #endif
5671
5672   if (!pushed_by_player)
5673   {
5674     WasJustMoving[newx][newy] = 3;
5675
5676     if (CAN_FALL(element) && direction == MV_DOWN)
5677       WasJustFalling[newx][newy] = 3;
5678   }
5679
5680   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
5681   {
5682     TestIfBadThingTouchesHero(newx, newy);
5683     TestIfBadThingTouchesFriend(newx, newy);
5684
5685     if (!IS_CUSTOM_ELEMENT(element))
5686       TestIfBadThingTouchesOtherBadThing(newx, newy);
5687   }
5688   else if (element == EL_PENGUIN)
5689     TestIfFriendTouchesBadThing(newx, newy);
5690
5691   if (CAN_FALL(element) && direction == MV_DOWN &&
5692       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5693     Impact(x, newy);
5694
5695 #if 1
5696   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
5697 #endif
5698
5699 #if 0
5700   if (ChangePage[newx][newy] != -1)                     /* delayed change */
5701     ChangeElement(newx, newy, ChangePage[newx][newy]);
5702 #endif
5703
5704 #if 1
5705
5706   TestIfElementHitsCustomElement(newx, newy, direction);
5707
5708 #else
5709
5710   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5711   {
5712     int hitting_element = Feld[newx][newy];
5713
5714     /* !!! fix side (direction) orientation here and elsewhere !!! */
5715     CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
5716                            direction);
5717
5718 #if 0
5719     if (IN_LEV_FIELD(nextx, nexty))
5720     {
5721       int opposite_direction = MV_DIR_OPPOSITE(direction);
5722       int hitting_side = direction;
5723       int touched_side = opposite_direction;
5724       int touched_element = MovingOrBlocked2Element(nextx, nexty);
5725       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5726                             MovDir[nextx][nexty] != direction ||
5727                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5728
5729       if (object_hit)
5730       {
5731         int i;
5732
5733         CheckElementChangeSide(nextx, nexty, touched_element,
5734                                CE_HIT_BY_SOMETHING, opposite_direction);
5735
5736         if (IS_CUSTOM_ELEMENT(hitting_element) &&
5737             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5738         {
5739           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5740           {
5741             struct ElementChangeInfo *change =
5742               &element_info[hitting_element].change_page[i];
5743
5744             if (change->can_change &&
5745                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5746                 change->trigger_side & touched_side &&
5747                 change->trigger_element == touched_element)
5748             {
5749               CheckElementChangePage(newx, newy, hitting_element,
5750                                      CE_OTHER_IS_HITTING, i);
5751               break;
5752             }
5753           }
5754         }
5755
5756         if (IS_CUSTOM_ELEMENT(touched_element) &&
5757             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5758         {
5759           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5760           {
5761             struct ElementChangeInfo *change =
5762               &element_info[touched_element].change_page[i];
5763
5764             if (change->can_change &&
5765                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5766                 change->trigger_side & hitting_side &&
5767                 change->trigger_element == hitting_element)
5768             {
5769               CheckElementChangePage(nextx, nexty, touched_element,
5770                                      CE_OTHER_GETS_HIT, i);
5771               break;
5772             }
5773           }
5774         }
5775       }
5776     }
5777 #endif
5778   }
5779 #endif
5780
5781   TestIfPlayerTouchesCustomElement(newx, newy);
5782   TestIfElementTouchesCustomElement(newx, newy);
5783 }
5784
5785 int AmoebeNachbarNr(int ax, int ay)
5786 {
5787   int i;
5788   int element = Feld[ax][ay];
5789   int group_nr = 0;
5790   static int xy[4][2] =
5791   {
5792     { 0, -1 },
5793     { -1, 0 },
5794     { +1, 0 },
5795     { 0, +1 }
5796   };
5797
5798   for (i = 0; i < NUM_DIRECTIONS; i++)
5799   {
5800     int x = ax + xy[i][0];
5801     int y = ay + xy[i][1];
5802
5803     if (!IN_LEV_FIELD(x, y))
5804       continue;
5805
5806     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5807       group_nr = AmoebaNr[x][y];
5808   }
5809
5810   return group_nr;
5811 }
5812
5813 void AmoebenVereinigen(int ax, int ay)
5814 {
5815   int i, x, y, xx, yy;
5816   int new_group_nr = AmoebaNr[ax][ay];
5817   static int xy[4][2] =
5818   {
5819     { 0, -1 },
5820     { -1, 0 },
5821     { +1, 0 },
5822     { 0, +1 }
5823   };
5824
5825   if (new_group_nr == 0)
5826     return;
5827
5828   for (i = 0; i < NUM_DIRECTIONS; i++)
5829   {
5830     x = ax + xy[i][0];
5831     y = ay + xy[i][1];
5832
5833     if (!IN_LEV_FIELD(x, y))
5834       continue;
5835
5836     if ((Feld[x][y] == EL_AMOEBA_FULL ||
5837          Feld[x][y] == EL_BD_AMOEBA ||
5838          Feld[x][y] == EL_AMOEBA_DEAD) &&
5839         AmoebaNr[x][y] != new_group_nr)
5840     {
5841       int old_group_nr = AmoebaNr[x][y];
5842
5843       if (old_group_nr == 0)
5844         return;
5845
5846       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5847       AmoebaCnt[old_group_nr] = 0;
5848       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5849       AmoebaCnt2[old_group_nr] = 0;
5850
5851       for (yy = 0; yy < lev_fieldy; yy++)
5852       {
5853         for (xx = 0; xx < lev_fieldx; xx++)
5854         {
5855           if (AmoebaNr[xx][yy] == old_group_nr)
5856             AmoebaNr[xx][yy] = new_group_nr;
5857         }
5858       }
5859     }
5860   }
5861 }
5862
5863 void AmoebeUmwandeln(int ax, int ay)
5864 {
5865   int i, x, y;
5866
5867   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5868   {
5869     int group_nr = AmoebaNr[ax][ay];
5870
5871 #ifdef DEBUG
5872     if (group_nr == 0)
5873     {
5874       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5875       printf("AmoebeUmwandeln(): This should never happen!\n");
5876       return;
5877     }
5878 #endif
5879
5880     for (y = 0; y < lev_fieldy; y++)
5881     {
5882       for (x = 0; x < lev_fieldx; x++)
5883       {
5884         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5885         {
5886           AmoebaNr[x][y] = 0;
5887           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5888         }
5889       }
5890     }
5891     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5892                             SND_AMOEBA_TURNING_TO_GEM :
5893                             SND_AMOEBA_TURNING_TO_ROCK));
5894     Bang(ax, ay);
5895   }
5896   else
5897   {
5898     static int xy[4][2] =
5899     {
5900       { 0, -1 },
5901       { -1, 0 },
5902       { +1, 0 },
5903       { 0, +1 }
5904     };
5905
5906     for (i = 0; i < NUM_DIRECTIONS; i++)
5907     {
5908       x = ax + xy[i][0];
5909       y = ay + xy[i][1];
5910
5911       if (!IN_LEV_FIELD(x, y))
5912         continue;
5913
5914       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5915       {
5916         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5917                               SND_AMOEBA_TURNING_TO_GEM :
5918                               SND_AMOEBA_TURNING_TO_ROCK));
5919         Bang(x, y);
5920       }
5921     }
5922   }
5923 }
5924
5925 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5926 {
5927   int x, y;
5928   int group_nr = AmoebaNr[ax][ay];
5929   boolean done = FALSE;
5930
5931 #ifdef DEBUG
5932   if (group_nr == 0)
5933   {
5934     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5935     printf("AmoebeUmwandelnBD(): This should never happen!\n");
5936     return;
5937   }
5938 #endif
5939
5940   for (y = 0; y < lev_fieldy; y++)
5941   {
5942     for (x = 0; x < lev_fieldx; x++)
5943     {
5944       if (AmoebaNr[x][y] == group_nr &&
5945           (Feld[x][y] == EL_AMOEBA_DEAD ||
5946            Feld[x][y] == EL_BD_AMOEBA ||
5947            Feld[x][y] == EL_AMOEBA_GROWING))
5948       {
5949         AmoebaNr[x][y] = 0;
5950         Feld[x][y] = new_element;
5951         InitField(x, y, FALSE);
5952         DrawLevelField(x, y);
5953         done = TRUE;
5954       }
5955     }
5956   }
5957
5958   if (done)
5959     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5960                             SND_BD_AMOEBA_TURNING_TO_ROCK :
5961                             SND_BD_AMOEBA_TURNING_TO_GEM));
5962 }
5963
5964 void AmoebeWaechst(int x, int y)
5965 {
5966   static unsigned long sound_delay = 0;
5967   static unsigned long sound_delay_value = 0;
5968
5969   if (!MovDelay[x][y])          /* start new growing cycle */
5970   {
5971     MovDelay[x][y] = 7;
5972
5973     if (DelayReached(&sound_delay, sound_delay_value))
5974     {
5975 #if 1
5976       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5977 #else
5978       if (Store[x][y] == EL_BD_AMOEBA)
5979         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5980       else
5981         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5982 #endif
5983       sound_delay_value = 30;
5984     }
5985   }
5986
5987   if (MovDelay[x][y])           /* wait some time before growing bigger */
5988   {
5989     MovDelay[x][y]--;
5990     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5991     {
5992       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5993                                            6 - MovDelay[x][y]);
5994
5995       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5996     }
5997
5998     if (!MovDelay[x][y])
5999     {
6000       Feld[x][y] = Store[x][y];
6001       Store[x][y] = 0;
6002       DrawLevelField(x, y);
6003     }
6004   }
6005 }
6006
6007 void AmoebaDisappearing(int x, int y)
6008 {
6009   static unsigned long sound_delay = 0;
6010   static unsigned long sound_delay_value = 0;
6011
6012   if (!MovDelay[x][y])          /* start new shrinking cycle */
6013   {
6014     MovDelay[x][y] = 7;
6015
6016     if (DelayReached(&sound_delay, sound_delay_value))
6017       sound_delay_value = 30;
6018   }
6019
6020   if (MovDelay[x][y])           /* wait some time before shrinking */
6021   {
6022     MovDelay[x][y]--;
6023     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6024     {
6025       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6026                                            6 - MovDelay[x][y]);
6027
6028       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6029     }
6030
6031     if (!MovDelay[x][y])
6032     {
6033       Feld[x][y] = EL_EMPTY;
6034       DrawLevelField(x, y);
6035
6036       /* don't let mole enter this field in this cycle;
6037          (give priority to objects falling to this field from above) */
6038       Stop[x][y] = TRUE;
6039     }
6040   }
6041 }
6042
6043 void AmoebeAbleger(int ax, int ay)
6044 {
6045   int i;
6046   int element = Feld[ax][ay];
6047   int graphic = el2img(element);
6048   int newax = ax, neway = ay;
6049   static int xy[4][2] =
6050   {
6051     { 0, -1 },
6052     { -1, 0 },
6053     { +1, 0 },
6054     { 0, +1 }
6055   };
6056
6057   if (!level.amoeba_speed)
6058   {
6059     Feld[ax][ay] = EL_AMOEBA_DEAD;
6060     DrawLevelField(ax, ay);
6061     return;
6062   }
6063
6064   if (IS_ANIMATED(graphic))
6065     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6066
6067   if (!MovDelay[ax][ay])        /* start making new amoeba field */
6068     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6069
6070   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
6071   {
6072     MovDelay[ax][ay]--;
6073     if (MovDelay[ax][ay])
6074       return;
6075   }
6076
6077   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6078   {
6079     int start = RND(4);
6080     int x = ax + xy[start][0];
6081     int y = ay + xy[start][1];
6082
6083     if (!IN_LEV_FIELD(x, y))
6084       return;
6085
6086     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6087     if (IS_FREE(x, y) ||
6088         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6089     {
6090       newax = x;
6091       neway = y;
6092     }
6093
6094     if (newax == ax && neway == ay)
6095       return;
6096   }
6097   else                          /* normal or "filled" (BD style) amoeba */
6098   {
6099     int start = RND(4);
6100     boolean waiting_for_player = FALSE;
6101
6102     for (i = 0; i < NUM_DIRECTIONS; i++)
6103     {
6104       int j = (start + i) % 4;
6105       int x = ax + xy[j][0];
6106       int y = ay + xy[j][1];
6107
6108       if (!IN_LEV_FIELD(x, y))
6109         continue;
6110
6111       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6112       if (IS_FREE(x, y) ||
6113           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6114       {
6115         newax = x;
6116         neway = y;
6117         break;
6118       }
6119       else if (IS_PLAYER(x, y))
6120         waiting_for_player = TRUE;
6121     }
6122
6123     if (newax == ax && neway == ay)             /* amoeba cannot grow */
6124     {
6125       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6126       {
6127         Feld[ax][ay] = EL_AMOEBA_DEAD;
6128         DrawLevelField(ax, ay);
6129         AmoebaCnt[AmoebaNr[ax][ay]]--;
6130
6131         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
6132         {
6133           if (element == EL_AMOEBA_FULL)
6134             AmoebeUmwandeln(ax, ay);
6135           else if (element == EL_BD_AMOEBA)
6136             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6137         }
6138       }
6139       return;
6140     }
6141     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6142     {
6143       /* amoeba gets larger by growing in some direction */
6144
6145       int new_group_nr = AmoebaNr[ax][ay];
6146
6147 #ifdef DEBUG
6148   if (new_group_nr == 0)
6149   {
6150     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6151     printf("AmoebeAbleger(): This should never happen!\n");
6152     return;
6153   }
6154 #endif
6155
6156       AmoebaNr[newax][neway] = new_group_nr;
6157       AmoebaCnt[new_group_nr]++;
6158       AmoebaCnt2[new_group_nr]++;
6159
6160       /* if amoeba touches other amoeba(s) after growing, unify them */
6161       AmoebenVereinigen(newax, neway);
6162
6163       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6164       {
6165         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6166         return;
6167       }
6168     }
6169   }
6170
6171   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6172       (neway == lev_fieldy - 1 && newax != ax))
6173   {
6174     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6175     Store[newax][neway] = element;
6176   }
6177   else if (neway == ay)
6178   {
6179     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6180 #if 1
6181     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6182 #else
6183     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6184 #endif
6185   }
6186   else
6187   {
6188     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6189     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6190     Store[ax][ay] = EL_AMOEBA_DROP;
6191     ContinueMoving(ax, ay);
6192     return;
6193   }
6194
6195   DrawLevelField(newax, neway);
6196 }
6197
6198 void Life(int ax, int ay)
6199 {
6200   int x1, y1, x2, y2;
6201   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6202   int life_time = 40;
6203   int element = Feld[ax][ay];
6204   int graphic = el2img(element);
6205   boolean changed = FALSE;
6206
6207   if (IS_ANIMATED(graphic))
6208     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6209
6210   if (Stop[ax][ay])
6211     return;
6212
6213   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6214     MovDelay[ax][ay] = life_time;
6215
6216   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6217   {
6218     MovDelay[ax][ay]--;
6219     if (MovDelay[ax][ay])
6220       return;
6221   }
6222
6223   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6224   {
6225     int xx = ax+x1, yy = ay+y1;
6226     int nachbarn = 0;
6227
6228     if (!IN_LEV_FIELD(xx, yy))
6229       continue;
6230
6231     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6232     {
6233       int x = xx+x2, y = yy+y2;
6234
6235       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6236         continue;
6237
6238       if (((Feld[x][y] == element ||
6239             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6240            !Stop[x][y]) ||
6241           (IS_FREE(x, y) && Stop[x][y]))
6242         nachbarn++;
6243     }
6244
6245     if (xx == ax && yy == ay)           /* field in the middle */
6246     {
6247       if (nachbarn < life[0] || nachbarn > life[1])
6248       {
6249         Feld[xx][yy] = EL_EMPTY;
6250         if (!Stop[xx][yy])
6251           DrawLevelField(xx, yy);
6252         Stop[xx][yy] = TRUE;
6253         changed = TRUE;
6254       }
6255     }
6256     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6257     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6258     {                                   /* free border field */
6259       if (nachbarn >= life[2] && nachbarn <= life[3])
6260       {
6261         Feld[xx][yy] = element;
6262         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6263         if (!Stop[xx][yy])
6264           DrawLevelField(xx, yy);
6265         Stop[xx][yy] = TRUE;
6266         changed = TRUE;
6267       }
6268     }
6269   }
6270
6271   if (changed)
6272     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6273                    SND_GAME_OF_LIFE_GROWING);
6274 }
6275
6276 static void InitRobotWheel(int x, int y)
6277 {
6278   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6279 }
6280
6281 static void RunRobotWheel(int x, int y)
6282 {
6283   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6284 }
6285
6286 static void StopRobotWheel(int x, int y)
6287 {
6288   if (ZX == x && ZY == y)
6289     ZX = ZY = -1;
6290 }
6291
6292 static void InitTimegateWheel(int x, int y)
6293 {
6294   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6295 }
6296
6297 static void RunTimegateWheel(int x, int y)
6298 {
6299   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6300 }
6301
6302 void CheckExit(int x, int y)
6303 {
6304   if (local_player->gems_still_needed > 0 ||
6305       local_player->sokobanfields_still_needed > 0 ||
6306       local_player->lights_still_needed > 0)
6307   {
6308     int element = Feld[x][y];
6309     int graphic = el2img(element);
6310
6311     if (IS_ANIMATED(graphic))
6312       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6313
6314     return;
6315   }
6316
6317   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6318     return;
6319
6320   Feld[x][y] = EL_EXIT_OPENING;
6321
6322   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6323 }
6324
6325 void CheckExitSP(int x, int y)
6326 {
6327   if (local_player->gems_still_needed > 0)
6328   {
6329     int element = Feld[x][y];
6330     int graphic = el2img(element);
6331
6332     if (IS_ANIMATED(graphic))
6333       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6334
6335     return;
6336   }
6337
6338   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6339     return;
6340
6341   Feld[x][y] = EL_SP_EXIT_OPENING;
6342
6343   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6344 }
6345
6346 static void CloseAllOpenTimegates()
6347 {
6348   int x, y;
6349
6350   for (y = 0; y < lev_fieldy; y++)
6351   {
6352     for (x = 0; x < lev_fieldx; x++)
6353     {
6354       int element = Feld[x][y];
6355
6356       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6357       {
6358         Feld[x][y] = EL_TIMEGATE_CLOSING;
6359 #if 1
6360         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6361 #else
6362         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6363 #endif
6364       }
6365     }
6366   }
6367 }
6368
6369 void EdelsteinFunkeln(int x, int y)
6370 {
6371   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6372     return;
6373
6374   if (Feld[x][y] == EL_BD_DIAMOND)
6375     return;
6376
6377   if (MovDelay[x][y] == 0)      /* next animation frame */
6378     MovDelay[x][y] = 11 * !SimpleRND(500);
6379
6380   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6381   {
6382     MovDelay[x][y]--;
6383
6384     if (setup.direct_draw && MovDelay[x][y])
6385       SetDrawtoField(DRAW_BUFFERED);
6386
6387     DrawLevelElementAnimation(x, y, Feld[x][y]);
6388
6389     if (MovDelay[x][y] != 0)
6390     {
6391       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6392                                            10 - MovDelay[x][y]);
6393
6394       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6395
6396       if (setup.direct_draw)
6397       {
6398         int dest_x, dest_y;
6399
6400         dest_x = FX + SCREENX(x) * TILEX;
6401         dest_y = FY + SCREENY(y) * TILEY;
6402
6403         BlitBitmap(drawto_field, window,
6404                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6405         SetDrawtoField(DRAW_DIRECT);
6406       }
6407     }
6408   }
6409 }
6410
6411 void MauerWaechst(int x, int y)
6412 {
6413   int delay = 6;
6414
6415   if (!MovDelay[x][y])          /* next animation frame */
6416     MovDelay[x][y] = 3 * delay;
6417
6418   if (MovDelay[x][y])           /* wait some time before next frame */
6419   {
6420     MovDelay[x][y]--;
6421
6422     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6423     {
6424       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6425       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6426
6427       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6428     }
6429
6430     if (!MovDelay[x][y])
6431     {
6432       if (MovDir[x][y] == MV_LEFT)
6433       {
6434         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6435           DrawLevelField(x - 1, y);
6436       }
6437       else if (MovDir[x][y] == MV_RIGHT)
6438       {
6439         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6440           DrawLevelField(x + 1, y);
6441       }
6442       else if (MovDir[x][y] == MV_UP)
6443       {
6444         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6445           DrawLevelField(x, y - 1);
6446       }
6447       else
6448       {
6449         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6450           DrawLevelField(x, y + 1);
6451       }
6452
6453       Feld[x][y] = Store[x][y];
6454       Store[x][y] = 0;
6455       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6456       DrawLevelField(x, y);
6457     }
6458   }
6459 }
6460
6461 void MauerAbleger(int ax, int ay)
6462 {
6463   int element = Feld[ax][ay];
6464   int graphic = el2img(element);
6465   boolean oben_frei = FALSE, unten_frei = FALSE;
6466   boolean links_frei = FALSE, rechts_frei = FALSE;
6467   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6468   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6469   boolean new_wall = FALSE;
6470
6471   if (IS_ANIMATED(graphic))
6472     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6473
6474   if (!MovDelay[ax][ay])        /* start building new wall */
6475     MovDelay[ax][ay] = 6;
6476
6477   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6478   {
6479     MovDelay[ax][ay]--;
6480     if (MovDelay[ax][ay])
6481       return;
6482   }
6483
6484   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6485     oben_frei = TRUE;
6486   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6487     unten_frei = TRUE;
6488   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6489     links_frei = TRUE;
6490   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6491     rechts_frei = TRUE;
6492
6493   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6494       element == EL_EXPANDABLE_WALL_ANY)
6495   {
6496     if (oben_frei)
6497     {
6498       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6499       Store[ax][ay-1] = element;
6500       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6501       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6502         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6503                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6504       new_wall = TRUE;
6505     }
6506     if (unten_frei)
6507     {
6508       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6509       Store[ax][ay+1] = element;
6510       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6511       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6512         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6513                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6514       new_wall = TRUE;
6515     }
6516   }
6517
6518   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6519       element == EL_EXPANDABLE_WALL_ANY ||
6520       element == EL_EXPANDABLE_WALL)
6521   {
6522     if (links_frei)
6523     {
6524       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6525       Store[ax-1][ay] = element;
6526       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6527       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6528         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6529                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6530       new_wall = TRUE;
6531     }
6532
6533     if (rechts_frei)
6534     {
6535       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6536       Store[ax+1][ay] = element;
6537       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6538       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6539         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6540                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6541       new_wall = TRUE;
6542     }
6543   }
6544
6545   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6546     DrawLevelField(ax, ay);
6547
6548   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6549     oben_massiv = TRUE;
6550   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6551     unten_massiv = TRUE;
6552   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6553     links_massiv = TRUE;
6554   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6555     rechts_massiv = TRUE;
6556
6557   if (((oben_massiv && unten_massiv) ||
6558        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6559        element == EL_EXPANDABLE_WALL) &&
6560       ((links_massiv && rechts_massiv) ||
6561        element == EL_EXPANDABLE_WALL_VERTICAL))
6562     Feld[ax][ay] = EL_WALL;
6563
6564   if (new_wall)
6565 #if 1
6566     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6567 #else
6568     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6569 #endif
6570 }
6571
6572 void CheckForDragon(int x, int y)
6573 {
6574   int i, j;
6575   boolean dragon_found = FALSE;
6576   static int xy[4][2] =
6577   {
6578     { 0, -1 },
6579     { -1, 0 },
6580     { +1, 0 },
6581     { 0, +1 }
6582   };
6583
6584   for (i = 0; i < NUM_DIRECTIONS; i++)
6585   {
6586     for (j = 0; j < 4; j++)
6587     {
6588       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6589
6590       if (IN_LEV_FIELD(xx, yy) &&
6591           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6592       {
6593         if (Feld[xx][yy] == EL_DRAGON)
6594           dragon_found = TRUE;
6595       }
6596       else
6597         break;
6598     }
6599   }
6600
6601   if (!dragon_found)
6602   {
6603     for (i = 0; i < NUM_DIRECTIONS; i++)
6604     {
6605       for (j = 0; j < 3; j++)
6606       {
6607         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6608   
6609         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6610         {
6611           Feld[xx][yy] = EL_EMPTY;
6612           DrawLevelField(xx, yy);
6613         }
6614         else
6615           break;
6616       }
6617     }
6618   }
6619 }
6620
6621 static void InitBuggyBase(int x, int y)
6622 {
6623   int element = Feld[x][y];
6624   int activating_delay = FRAMES_PER_SECOND / 4;
6625
6626   ChangeDelay[x][y] =
6627     (element == EL_SP_BUGGY_BASE ?
6628      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6629      element == EL_SP_BUGGY_BASE_ACTIVATING ?
6630      activating_delay :
6631      element == EL_SP_BUGGY_BASE_ACTIVE ?
6632      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6633 }
6634
6635 static void WarnBuggyBase(int x, int y)
6636 {
6637   int i;
6638   static int xy[4][2] =
6639   {
6640     { 0, -1 },
6641     { -1, 0 },
6642     { +1, 0 },
6643     { 0, +1 }
6644   };
6645
6646   for (i = 0; i < NUM_DIRECTIONS; i++)
6647   {
6648     int xx = x + xy[i][0], yy = y + xy[i][1];
6649
6650     if (IS_PLAYER(xx, yy))
6651     {
6652       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6653
6654       break;
6655     }
6656   }
6657 }
6658
6659 static void InitTrap(int x, int y)
6660 {
6661   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6662 }
6663
6664 static void ActivateTrap(int x, int y)
6665 {
6666   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6667 }
6668
6669 static void ChangeActiveTrap(int x, int y)
6670 {
6671   int graphic = IMG_TRAP_ACTIVE;
6672
6673   /* if new animation frame was drawn, correct crumbled sand border */
6674   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6675     DrawLevelFieldCrumbledSand(x, y);
6676 }
6677
6678 static void ChangeElementNowExt(int x, int y, int target_element)
6679 {
6680   int previous_move_direction = MovDir[x][y];
6681
6682   /* check if element under player changes from accessible to unaccessible
6683      (needed for special case of dropping element which then changes) */
6684   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6685       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6686   {
6687     Bang(x, y);
6688     return;
6689   }
6690
6691   RemoveField(x, y);
6692   Feld[x][y] = target_element;
6693
6694   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6695
6696   ResetGfxAnimation(x, y);
6697   ResetRandomAnimationValue(x, y);
6698
6699   if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6700     MovDir[x][y] = previous_move_direction;
6701
6702 #if 1
6703   InitField_WithBug1(x, y, FALSE);
6704 #else
6705   InitField(x, y, FALSE);
6706   if (CAN_MOVE(Feld[x][y]))
6707     InitMovDir(x, y);
6708 #endif
6709
6710   DrawLevelField(x, y);
6711
6712   if (GFX_CRUMBLED(Feld[x][y]))
6713     DrawLevelFieldCrumbledSandNeighbours(x, y);
6714
6715   TestIfBadThingTouchesHero(x, y);
6716   TestIfPlayerTouchesCustomElement(x, y);
6717   TestIfElementTouchesCustomElement(x, y);
6718
6719   if (ELEM_IS_PLAYER(target_element))
6720     RelocatePlayer(x, y, target_element);
6721 }
6722
6723 static boolean ChangeElementNow(int x, int y, int element, int page)
6724 {
6725   struct ElementChangeInfo *change = &element_info[element].change_page[page];
6726
6727   /* always use default change event to prevent running into a loop */
6728   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6729     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6730
6731   /* do not change already changed elements with same change event */
6732 #if 0
6733   if (Changed[x][y] & ChangeEvent[x][y])
6734     return FALSE;
6735 #else
6736   if (Changed[x][y])
6737     return FALSE;
6738 #endif
6739
6740   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6741
6742   CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6743
6744   if (change->explode)
6745   {
6746     Bang(x, y);
6747
6748     return TRUE;
6749   }
6750
6751   if (change->use_content)
6752   {
6753     boolean complete_change = TRUE;
6754     boolean can_change[3][3];
6755     int xx, yy;
6756
6757     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6758     {
6759       boolean half_destructible;
6760       int ex = x + xx - 1;
6761       int ey = y + yy - 1;
6762       int e;
6763
6764       can_change[xx][yy] = TRUE;
6765
6766       if (ex == x && ey == y)   /* do not check changing element itself */
6767         continue;
6768
6769       if (change->content[xx][yy] == EL_EMPTY_SPACE)
6770       {
6771         can_change[xx][yy] = FALSE;     /* do not change empty borders */
6772
6773         continue;
6774       }
6775
6776       if (!IN_LEV_FIELD(ex, ey))
6777       {
6778         can_change[xx][yy] = FALSE;
6779         complete_change = FALSE;
6780
6781         continue;
6782       }
6783
6784       e = Feld[ex][ey];
6785
6786       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6787         e = MovingOrBlocked2Element(ex, ey);
6788
6789       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6790
6791       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
6792           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6793           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6794       {
6795         can_change[xx][yy] = FALSE;
6796         complete_change = FALSE;
6797       }
6798     }
6799
6800     if (!change->only_complete || complete_change)
6801     {
6802       boolean something_has_changed = FALSE;
6803
6804       if (change->only_complete && change->use_random_change &&
6805           RND(100) < change->random)
6806         return FALSE;
6807
6808       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6809       {
6810         int ex = x + xx - 1;
6811         int ey = y + yy - 1;
6812
6813         if (can_change[xx][yy] && (!change->use_random_change ||
6814                                    RND(100) < change->random))
6815         {
6816           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6817             RemoveMovingField(ex, ey);
6818
6819           ChangeEvent[ex][ey] = ChangeEvent[x][y];
6820
6821           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6822
6823           something_has_changed = TRUE;
6824
6825           /* for symmetry reasons, freeze newly created border elements */
6826           if (ex != x || ey != y)
6827             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
6828         }
6829       }
6830
6831       if (something_has_changed)
6832         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6833     }
6834   }
6835   else
6836   {
6837     ChangeElementNowExt(x, y, change->target_element);
6838
6839     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6840   }
6841
6842   return TRUE;
6843 }
6844
6845 static void ChangeElement(int x, int y, int page)
6846 {
6847   int element = MovingOrBlocked2Element(x, y);
6848   struct ElementInfo *ei = &element_info[element];
6849   struct ElementChangeInfo *change = &ei->change_page[page];
6850
6851 #if 0
6852 #ifdef DEBUG
6853   if (!CAN_CHANGE(element))
6854   {
6855     printf("\n\n");
6856     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6857            x, y, element, element_info[element].token_name);
6858     printf("ChangeElement(): This should never happen!\n");
6859     printf("\n\n");
6860   }
6861 #endif
6862 #endif
6863
6864   if (ChangeDelay[x][y] == 0)           /* initialize element change */
6865   {
6866     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
6867                          RND(change->delay_random * change->delay_frames)) + 1;
6868
6869     ResetGfxAnimation(x, y);
6870     ResetRandomAnimationValue(x, y);
6871
6872     if (change->pre_change_function)
6873       change->pre_change_function(x, y);
6874   }
6875
6876   ChangeDelay[x][y]--;
6877
6878   if (ChangeDelay[x][y] != 0)           /* continue element change */
6879   {
6880     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6881
6882     if (IS_ANIMATED(graphic))
6883       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6884
6885     if (change->change_function)
6886       change->change_function(x, y);
6887   }
6888   else                                  /* finish element change */
6889   {
6890     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
6891     {
6892       page = ChangePage[x][y];
6893       ChangePage[x][y] = -1;
6894     }
6895
6896 #if 0
6897     if (IS_MOVING(x, y) && !change->explode)
6898 #else
6899     if (IS_MOVING(x, y))                /* never change a running system ;-) */
6900 #endif
6901     {
6902       ChangeDelay[x][y] = 1;            /* try change after next move step */
6903       ChangePage[x][y] = page;          /* remember page to use for change */
6904
6905       return;
6906     }
6907
6908     if (ChangeElementNow(x, y, element, page))
6909     {
6910       if (change->post_change_function)
6911         change->post_change_function(x, y);
6912     }
6913   }
6914 }
6915
6916 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6917                                               int trigger_element,
6918                                               int trigger_event,
6919                                               int trigger_player,
6920                                               int trigger_side,
6921                                               int trigger_page)
6922 {
6923   int i, j, x, y;
6924
6925   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6926     return FALSE;
6927
6928   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6929   {
6930     int element = EL_CUSTOM_START + i;
6931
6932     boolean change_element = FALSE;
6933     int page = 0;
6934
6935     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6936       continue;
6937
6938     for (j = 0; j < element_info[element].num_change_pages; j++)
6939     {
6940       struct ElementChangeInfo *change = &element_info[element].change_page[j];
6941
6942       if (change->can_change &&
6943           change->events & CH_EVENT_BIT(trigger_event) &&
6944           change->trigger_side & trigger_side &&
6945           change->trigger_player & trigger_player &&
6946           change->trigger_page & (1 << trigger_page) &&
6947           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
6948       {
6949 #if 0
6950         if (!(change->events & CH_EVENT_BIT(trigger_event)))
6951           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6952                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6953 #endif
6954
6955         change_element = TRUE;
6956         page = j;
6957
6958         break;
6959       }
6960     }
6961
6962     if (!change_element)
6963       continue;
6964
6965     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6966     {
6967 #if 0
6968       if (x == lx && y == ly)   /* do not change trigger element itself */
6969         continue;
6970 #endif
6971
6972       if (Feld[x][y] == element)
6973       {
6974         ChangeDelay[x][y] = 1;
6975         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6976         ChangeElement(x, y, page);
6977       }
6978     }
6979   }
6980
6981   return TRUE;
6982 }
6983
6984 static boolean CheckElementChangeExt(int x, int y,
6985                                      int element,
6986                                      int trigger_event,
6987                                      int trigger_player,
6988                                      int trigger_side,
6989                                      int trigger_page)
6990 {
6991   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6992     return FALSE;
6993
6994   if (Feld[x][y] == EL_BLOCKED)
6995   {
6996     Blocked2Moving(x, y, &x, &y);
6997     element = Feld[x][y];
6998   }
6999
7000 #if 1
7001   if (trigger_page < 0)
7002   {
7003     boolean change_element = FALSE;
7004     int i;
7005
7006     for (i = 0; i < element_info[element].num_change_pages; i++)
7007     {
7008       struct ElementChangeInfo *change = &element_info[element].change_page[i];
7009
7010       if (change->can_change &&
7011           change->events & CH_EVENT_BIT(trigger_event) &&
7012           change->trigger_side & trigger_side &&
7013           change->trigger_player & trigger_player)
7014       {
7015         change_element = TRUE;
7016         trigger_page = i;
7017
7018         break;
7019       }
7020     }
7021
7022     if (!change_element)
7023       return FALSE;
7024   }
7025
7026 #else
7027
7028   /* !!! this check misses pages with same event, but different side !!! */
7029
7030   if (trigger_page < 0)
7031     trigger_page = element_info[element].event_page_nr[trigger_event];
7032
7033   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7034     return FALSE;
7035 #endif
7036
7037   ChangeDelay[x][y] = 1;
7038   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7039   ChangeElement(x, y, trigger_page);
7040
7041   return TRUE;
7042 }
7043
7044 static void PlayPlayerSound(struct PlayerInfo *player)
7045 {
7046   int jx = player->jx, jy = player->jy;
7047   int element = player->element_nr;
7048   int last_action = player->last_action_waiting;
7049   int action = player->action_waiting;
7050
7051   if (player->is_waiting)
7052   {
7053     if (action != last_action)
7054       PlayLevelSoundElementAction(jx, jy, element, action);
7055     else
7056       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7057   }
7058   else
7059   {
7060     if (action != last_action)
7061       StopSound(element_info[element].sound[last_action]);
7062
7063     if (last_action == ACTION_SLEEPING)
7064       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7065   }
7066 }
7067
7068 static void PlayAllPlayersSound()
7069 {
7070   int i;
7071
7072   for (i = 0; i < MAX_PLAYERS; i++)
7073     if (stored_player[i].active)
7074       PlayPlayerSound(&stored_player[i]);
7075 }
7076
7077 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7078 {
7079   boolean last_waiting = player->is_waiting;
7080   int move_dir = player->MovDir;
7081
7082   player->last_action_waiting = player->action_waiting;
7083
7084   if (is_waiting)
7085   {
7086     if (!last_waiting)          /* not waiting -> waiting */
7087     {
7088       player->is_waiting = TRUE;
7089
7090       player->frame_counter_bored =
7091         FrameCounter +
7092         game.player_boring_delay_fixed +
7093         SimpleRND(game.player_boring_delay_random);
7094       player->frame_counter_sleeping =
7095         FrameCounter +
7096         game.player_sleeping_delay_fixed +
7097         SimpleRND(game.player_sleeping_delay_random);
7098
7099       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7100     }
7101
7102     if (game.player_sleeping_delay_fixed +
7103         game.player_sleeping_delay_random > 0 &&
7104         player->anim_delay_counter == 0 &&
7105         player->post_delay_counter == 0 &&
7106         FrameCounter >= player->frame_counter_sleeping)
7107       player->is_sleeping = TRUE;
7108     else if (game.player_boring_delay_fixed +
7109              game.player_boring_delay_random > 0 &&
7110              FrameCounter >= player->frame_counter_bored)
7111       player->is_bored = TRUE;
7112
7113     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7114                               player->is_bored ? ACTION_BORING :
7115                               ACTION_WAITING);
7116
7117     if (player->is_sleeping)
7118     {
7119       if (player->num_special_action_sleeping > 0)
7120       {
7121         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7122         {
7123           int last_special_action = player->special_action_sleeping;
7124           int num_special_action = player->num_special_action_sleeping;
7125           int special_action =
7126             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7127              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7128              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7129              last_special_action + 1 : ACTION_SLEEPING);
7130           int special_graphic =
7131             el_act_dir2img(player->element_nr, special_action, move_dir);
7132
7133           player->anim_delay_counter =
7134             graphic_info[special_graphic].anim_delay_fixed +
7135             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7136           player->post_delay_counter =
7137             graphic_info[special_graphic].post_delay_fixed +
7138             SimpleRND(graphic_info[special_graphic].post_delay_random);
7139
7140           player->special_action_sleeping = special_action;
7141         }
7142
7143         if (player->anim_delay_counter > 0)
7144         {
7145           player->action_waiting = player->special_action_sleeping;
7146           player->anim_delay_counter--;
7147         }
7148         else if (player->post_delay_counter > 0)
7149         {
7150           player->post_delay_counter--;
7151         }
7152       }
7153     }
7154     else if (player->is_bored)
7155     {
7156       if (player->num_special_action_bored > 0)
7157       {
7158         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7159         {
7160           int special_action =
7161             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7162           int special_graphic =
7163             el_act_dir2img(player->element_nr, special_action, move_dir);
7164
7165           player->anim_delay_counter =
7166             graphic_info[special_graphic].anim_delay_fixed +
7167             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7168           player->post_delay_counter =
7169             graphic_info[special_graphic].post_delay_fixed +
7170             SimpleRND(graphic_info[special_graphic].post_delay_random);
7171
7172           player->special_action_bored = special_action;
7173         }
7174
7175         if (player->anim_delay_counter > 0)
7176         {
7177           player->action_waiting = player->special_action_bored;
7178           player->anim_delay_counter--;
7179         }
7180         else if (player->post_delay_counter > 0)
7181         {
7182           player->post_delay_counter--;
7183         }
7184       }
7185     }
7186   }
7187   else if (last_waiting)        /* waiting -> not waiting */
7188   {
7189     player->is_waiting = FALSE;
7190     player->is_bored = FALSE;
7191     player->is_sleeping = FALSE;
7192
7193     player->frame_counter_bored = -1;
7194     player->frame_counter_sleeping = -1;
7195
7196     player->anim_delay_counter = 0;
7197     player->post_delay_counter = 0;
7198
7199     player->action_waiting = ACTION_DEFAULT;
7200
7201     player->special_action_bored = ACTION_DEFAULT;
7202     player->special_action_sleeping = ACTION_DEFAULT;
7203   }
7204 }
7205
7206 #if 1
7207 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7208 {
7209 #if 0
7210   static byte stored_player_action[MAX_PLAYERS];
7211   static int num_stored_actions = 0;
7212 #endif
7213   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7214   int left      = player_action & JOY_LEFT;
7215   int right     = player_action & JOY_RIGHT;
7216   int up        = player_action & JOY_UP;
7217   int down      = player_action & JOY_DOWN;
7218   int button1   = player_action & JOY_BUTTON_1;
7219   int button2   = player_action & JOY_BUTTON_2;
7220   int dx        = (left ? -1    : right ? 1     : 0);
7221   int dy        = (up   ? -1    : down  ? 1     : 0);
7222
7223 #if 0
7224   stored_player_action[player->index_nr] = 0;
7225   num_stored_actions++;
7226 #endif
7227
7228 #if 0
7229   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7230 #endif
7231
7232   if (!player->active || tape.pausing)
7233     return 0;
7234
7235 #if 0
7236   printf("::: [%d %d %d %d] [%d %d]\n",
7237          left, right, up, down, button1, button2);
7238 #endif
7239
7240   if (player_action)
7241   {
7242 #if 0
7243     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7244 #endif
7245
7246 #if 0
7247     /* !!! TEST !!! */
7248     if (player->MovPos == 0)
7249       CheckGravityMovement(player);
7250 #endif
7251     if (button1)
7252       snapped = SnapField(player, dx, dy);
7253     else
7254     {
7255       if (button2)
7256         dropped = DropElement(player);
7257
7258       moved = MovePlayer(player, dx, dy);
7259     }
7260
7261     if (tape.single_step && tape.recording && !tape.pausing)
7262     {
7263       if (button1 || (dropped && !moved))
7264       {
7265         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7266         SnapField(player, 0, 0);                /* stop snapping */
7267       }
7268     }
7269
7270     SetPlayerWaiting(player, FALSE);
7271
7272 #if 1
7273     return player_action;
7274 #else
7275     stored_player_action[player->index_nr] = player_action;
7276 #endif
7277   }
7278   else
7279   {
7280 #if 0
7281     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7282 #endif
7283
7284     /* no actions for this player (no input at player's configured device) */
7285
7286     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7287     SnapField(player, 0, 0);
7288     CheckGravityMovementWhenNotMoving(player);
7289
7290     if (player->MovPos == 0)
7291       SetPlayerWaiting(player, TRUE);
7292
7293     if (player->MovPos == 0)    /* needed for tape.playing */
7294       player->is_moving = FALSE;
7295
7296     player->is_dropping = FALSE;
7297
7298     return 0;
7299   }
7300
7301 #if 0
7302   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7303   {
7304     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7305
7306     TapeRecordAction(stored_player_action);
7307     num_stored_actions = 0;
7308   }
7309 #endif
7310 }
7311
7312 #else
7313
7314 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7315 {
7316   static byte stored_player_action[MAX_PLAYERS];
7317   static int num_stored_actions = 0;
7318   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7319   int left      = player_action & JOY_LEFT;
7320   int right     = player_action & JOY_RIGHT;
7321   int up        = player_action & JOY_UP;
7322   int down      = player_action & JOY_DOWN;
7323   int button1   = player_action & JOY_BUTTON_1;
7324   int button2   = player_action & JOY_BUTTON_2;
7325   int dx        = (left ? -1    : right ? 1     : 0);
7326   int dy        = (up   ? -1    : down  ? 1     : 0);
7327
7328   stored_player_action[player->index_nr] = 0;
7329   num_stored_actions++;
7330
7331   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7332
7333   if (!player->active || tape.pausing)
7334     return;
7335
7336   if (player_action)
7337   {
7338     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7339
7340     if (button1)
7341       snapped = SnapField(player, dx, dy);
7342     else
7343     {
7344       if (button2)
7345         dropped = DropElement(player);
7346
7347       moved = MovePlayer(player, dx, dy);
7348     }
7349
7350     if (tape.single_step && tape.recording && !tape.pausing)
7351     {
7352       if (button1 || (dropped && !moved))
7353       {
7354         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7355         SnapField(player, 0, 0);                /* stop snapping */
7356       }
7357     }
7358
7359     stored_player_action[player->index_nr] = player_action;
7360   }
7361   else
7362   {
7363     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7364
7365     /* no actions for this player (no input at player's configured device) */
7366
7367     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7368     SnapField(player, 0, 0);
7369     CheckGravityMovementWhenNotMoving(player);
7370
7371     if (player->MovPos == 0)
7372       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7373
7374     if (player->MovPos == 0)    /* needed for tape.playing */
7375       player->is_moving = FALSE;
7376   }
7377
7378   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7379   {
7380     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7381
7382     TapeRecordAction(stored_player_action);
7383     num_stored_actions = 0;
7384   }
7385 }
7386 #endif
7387
7388 void GameActions()
7389 {
7390   static unsigned long action_delay = 0;
7391   unsigned long action_delay_value;
7392   int magic_wall_x = 0, magic_wall_y = 0;
7393   int i, x, y, element, graphic;
7394   byte *recorded_player_action;
7395   byte summarized_player_action = 0;
7396 #if 1
7397   byte tape_action[MAX_PLAYERS];
7398 #endif
7399
7400   if (game_status != GAME_MODE_PLAYING)
7401     return;
7402
7403   action_delay_value =
7404     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7405
7406   if (tape.playing && tape.index_search && !tape.pausing)
7407     action_delay_value = 0;
7408
7409   /* ---------- main game synchronization point ---------- */
7410
7411   WaitUntilDelayReached(&action_delay, action_delay_value);
7412
7413   if (network_playing && !network_player_action_received)
7414   {
7415     /*
7416 #ifdef DEBUG
7417     printf("DEBUG: try to get network player actions in time\n");
7418 #endif
7419     */
7420
7421 #if defined(PLATFORM_UNIX)
7422     /* last chance to get network player actions without main loop delay */
7423     HandleNetworking();
7424 #endif
7425
7426     if (game_status != GAME_MODE_PLAYING)
7427       return;
7428
7429     if (!network_player_action_received)
7430     {
7431       /*
7432 #ifdef DEBUG
7433       printf("DEBUG: failed to get network player actions in time\n");
7434 #endif
7435       */
7436       return;
7437     }
7438   }
7439
7440   if (tape.pausing)
7441     return;
7442
7443 #if 0
7444   printf("::: getting new tape action [%d]\n", FrameCounter);
7445 #endif
7446
7447   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7448
7449 #if 1
7450   if (recorded_player_action != NULL)
7451     for (i = 0; i < MAX_PLAYERS; i++)
7452       stored_player[i].action = recorded_player_action[i];
7453 #endif
7454
7455   for (i = 0; i < MAX_PLAYERS; i++)
7456   {
7457     summarized_player_action |= stored_player[i].action;
7458
7459     if (!network_playing)
7460       stored_player[i].effective_action = stored_player[i].action;
7461   }
7462
7463 #if defined(PLATFORM_UNIX)
7464   if (network_playing)
7465     SendToServer_MovePlayer(summarized_player_action);
7466 #endif
7467
7468   if (!options.network && !setup.team_mode)
7469     local_player->effective_action = summarized_player_action;
7470
7471   for (i = 0; i < MAX_PLAYERS; i++)
7472   {
7473     int actual_player_action = stored_player[i].effective_action;
7474
7475 #if 1
7476     /* !!! TEST !!! */
7477     if (stored_player[i].MovPos == 0)
7478       CheckGravityMovement(&stored_player[i]);
7479 #endif
7480
7481 #if 1
7482     /* overwrite programmed action with tape action */
7483     if (stored_player[i].programmed_action)
7484       actual_player_action = stored_player[i].programmed_action;
7485 #endif
7486
7487     if (recorded_player_action)
7488     {
7489 #if 0
7490       if (stored_player[i].programmed_action &&
7491           stored_player[i].programmed_action != recorded_player_action[i])
7492         printf("::: %d: %d <-> %d\n", i,
7493                stored_player[i].programmed_action, recorded_player_action[i]);
7494 #endif
7495
7496 #if 0
7497       actual_player_action = recorded_player_action[i];
7498 #endif
7499     }
7500
7501 #if 0
7502     /* overwrite tape action with programmed action */
7503     if (stored_player[i].programmed_action)
7504       actual_player_action = stored_player[i].programmed_action;
7505 #endif
7506
7507 #if 0
7508     if (i == 0)
7509       printf("::: action: %d: %x [%d]\n",
7510              stored_player[i].MovPos, actual_player_action, FrameCounter);
7511 #endif
7512
7513     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7514
7515     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7516       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7517
7518     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7519   }
7520
7521 #if 1
7522   if (tape.recording)
7523     TapeRecordAction(tape_action);
7524 #endif
7525
7526   network_player_action_received = FALSE;
7527
7528   ScrollScreen(NULL, SCROLL_GO_ON);
7529
7530 #if 0
7531   FrameCounter++;
7532   TimeFrames++;
7533
7534   for (i = 0; i < MAX_PLAYERS; i++)
7535     stored_player[i].Frame++;
7536 #endif
7537
7538 #if 1
7539   /* for downwards compatibility, the following code emulates a fixed bug that
7540      occured when pushing elements (causing elements that just made their last
7541      pushing step to already (if possible) make their first falling step in the
7542      same game frame, which is bad); this code is also needed to use the famous
7543      "spring push bug" which is used in older levels and might be wanted to be
7544      used also in newer levels, but in this case the buggy pushing code is only
7545      affecting the "spring" element and no other elements */
7546
7547 #if 1
7548   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7549 #else
7550   if (game.engine_version < VERSION_IDENT(2,2,0,7))
7551 #endif
7552   {
7553     for (i = 0; i < MAX_PLAYERS; i++)
7554     {
7555       struct PlayerInfo *player = &stored_player[i];
7556       int x = player->jx;
7557       int y = player->jy;
7558
7559 #if 1
7560       if (player->active && player->is_pushing && player->is_moving &&
7561           IS_MOVING(x, y) &&
7562           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7563            Feld[x][y] == EL_SPRING))
7564 #else
7565       if (player->active && player->is_pushing && player->is_moving &&
7566           IS_MOVING(x, y))
7567 #endif
7568       {
7569         ContinueMoving(x, y);
7570
7571         /* continue moving after pushing (this is actually a bug) */
7572         if (!IS_MOVING(x, y))
7573         {
7574           Stop[x][y] = FALSE;
7575         }
7576       }
7577     }
7578   }
7579 #endif
7580
7581   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7582   {
7583     Changed[x][y] = CE_BITMASK_DEFAULT;
7584     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7585
7586 #if DEBUG
7587     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7588     {
7589       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7590       printf("GameActions(): This should never happen!\n");
7591
7592       ChangePage[x][y] = -1;
7593     }
7594 #endif
7595
7596     Stop[x][y] = FALSE;
7597     if (WasJustMoving[x][y] > 0)
7598       WasJustMoving[x][y]--;
7599     if (WasJustFalling[x][y] > 0)
7600       WasJustFalling[x][y]--;
7601
7602     GfxFrame[x][y]++;
7603
7604 #if 1
7605     /* reset finished pushing action (not done in ContinueMoving() to allow
7606        continous pushing animation for elements with zero push delay) */
7607     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7608     {
7609       ResetGfxAnimation(x, y);
7610       DrawLevelField(x, y);
7611     }
7612 #endif
7613
7614 #if DEBUG
7615     if (IS_BLOCKED(x, y))
7616     {
7617       int oldx, oldy;
7618
7619       Blocked2Moving(x, y, &oldx, &oldy);
7620       if (!IS_MOVING(oldx, oldy))
7621       {
7622         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7623         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7624         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7625         printf("GameActions(): This should never happen!\n");
7626       }
7627     }
7628 #endif
7629   }
7630
7631   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7632   {
7633     element = Feld[x][y];
7634 #if 1
7635     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7636 #else
7637     graphic = el2img(element);
7638 #endif
7639
7640 #if 0
7641     if (element == -1)
7642     {
7643       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7644
7645       element = graphic = 0;
7646     }
7647 #endif
7648
7649     if (graphic_info[graphic].anim_global_sync)
7650       GfxFrame[x][y] = FrameCounter;
7651
7652     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7653         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7654       ResetRandomAnimationValue(x, y);
7655
7656     SetRandomAnimationValue(x, y);
7657
7658 #if 1
7659     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7660 #endif
7661
7662     if (IS_INACTIVE(element))
7663     {
7664       if (IS_ANIMATED(graphic))
7665         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7666
7667       continue;
7668     }
7669
7670 #if 1
7671     /* this may take place after moving, so 'element' may have changed */
7672 #if 0
7673     if (IS_CHANGING(x, y))
7674 #else
7675     if (IS_CHANGING(x, y) &&
7676         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7677 #endif
7678     {
7679 #if 0
7680       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7681                     element_info[element].event_page_nr[CE_DELAY]);
7682 #else
7683       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7684 #endif
7685
7686       element = Feld[x][y];
7687       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7688     }
7689 #endif
7690
7691     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7692     {
7693       StartMoving(x, y);
7694
7695 #if 1
7696       element = Feld[x][y];
7697       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7698 #if 0
7699       if (element == EL_MOLE)
7700         printf("::: %d, %d, %d [%d]\n",
7701                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7702                GfxAction[x][y]);
7703 #endif
7704 #if 0
7705       if (element == EL_YAMYAM)
7706         printf("::: %d, %d, %d\n",
7707                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7708 #endif
7709 #endif
7710
7711       if (IS_ANIMATED(graphic) &&
7712           !IS_MOVING(x, y) &&
7713           !Stop[x][y])
7714       {
7715         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7716
7717 #if 0
7718         if (element == EL_BUG)
7719           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7720 #endif
7721
7722 #if 0
7723         if (element == EL_MOLE)
7724           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7725 #endif
7726       }
7727
7728       if (IS_GEM(element) || element == EL_SP_INFOTRON)
7729         EdelsteinFunkeln(x, y);
7730     }
7731     else if ((element == EL_ACID ||
7732               element == EL_EXIT_OPEN ||
7733               element == EL_SP_EXIT_OPEN ||
7734               element == EL_SP_TERMINAL ||
7735               element == EL_SP_TERMINAL_ACTIVE ||
7736               element == EL_EXTRA_TIME ||
7737               element == EL_SHIELD_NORMAL ||
7738               element == EL_SHIELD_DEADLY) &&
7739              IS_ANIMATED(graphic))
7740       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7741     else if (IS_MOVING(x, y))
7742       ContinueMoving(x, y);
7743     else if (IS_ACTIVE_BOMB(element))
7744       CheckDynamite(x, y);
7745 #if 0
7746     else if (element == EL_EXPLOSION && !game.explosions_delayed)
7747       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7748 #endif
7749     else if (element == EL_AMOEBA_GROWING)
7750       AmoebeWaechst(x, y);
7751     else if (element == EL_AMOEBA_SHRINKING)
7752       AmoebaDisappearing(x, y);
7753
7754 #if !USE_NEW_AMOEBA_CODE
7755     else if (IS_AMOEBALIVE(element))
7756       AmoebeAbleger(x, y);
7757 #endif
7758
7759     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7760       Life(x, y);
7761     else if (element == EL_EXIT_CLOSED)
7762       CheckExit(x, y);
7763     else if (element == EL_SP_EXIT_CLOSED)
7764       CheckExitSP(x, y);
7765     else if (element == EL_EXPANDABLE_WALL_GROWING)
7766       MauerWaechst(x, y);
7767     else if (element == EL_EXPANDABLE_WALL ||
7768              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7769              element == EL_EXPANDABLE_WALL_VERTICAL ||
7770              element == EL_EXPANDABLE_WALL_ANY)
7771       MauerAbleger(x, y);
7772     else if (element == EL_FLAMES)
7773       CheckForDragon(x, y);
7774 #if 0
7775     else if (IS_AUTO_CHANGING(element))
7776       ChangeElement(x, y);
7777 #endif
7778     else if (element == EL_EXPLOSION)
7779       ; /* drawing of correct explosion animation is handled separately */
7780     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7781       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7782
7783 #if 0
7784     /* this may take place after moving, so 'element' may have changed */
7785     if (IS_AUTO_CHANGING(Feld[x][y]))
7786       ChangeElement(x, y);
7787 #endif
7788
7789     if (IS_BELT_ACTIVE(element))
7790       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7791
7792     if (game.magic_wall_active)
7793     {
7794       int jx = local_player->jx, jy = local_player->jy;
7795
7796       /* play the element sound at the position nearest to the player */
7797       if ((element == EL_MAGIC_WALL_FULL ||
7798            element == EL_MAGIC_WALL_ACTIVE ||
7799            element == EL_MAGIC_WALL_EMPTYING ||
7800            element == EL_BD_MAGIC_WALL_FULL ||
7801            element == EL_BD_MAGIC_WALL_ACTIVE ||
7802            element == EL_BD_MAGIC_WALL_EMPTYING) &&
7803           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7804       {
7805         magic_wall_x = x;
7806         magic_wall_y = y;
7807       }
7808     }
7809   }
7810
7811 #if USE_NEW_AMOEBA_CODE
7812   /* new experimental amoeba growth stuff */
7813 #if 1
7814   if (!(FrameCounter % 8))
7815 #endif
7816   {
7817     static unsigned long random = 1684108901;
7818
7819     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7820     {
7821 #if 0
7822       x = (random >> 10) % lev_fieldx;
7823       y = (random >> 20) % lev_fieldy;
7824 #else
7825       x = RND(lev_fieldx);
7826       y = RND(lev_fieldy);
7827 #endif
7828       element = Feld[x][y];
7829
7830       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7831       if (!IS_PLAYER(x,y) &&
7832           (element == EL_EMPTY ||
7833            element == EL_SAND ||
7834            element == EL_QUICKSAND_EMPTY ||
7835            element == EL_ACID_SPLASH_LEFT ||
7836            element == EL_ACID_SPLASH_RIGHT))
7837       {
7838         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7839             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7840             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7841             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7842           Feld[x][y] = EL_AMOEBA_DROP;
7843       }
7844
7845       random = random * 129 + 1;
7846     }
7847   }
7848 #endif
7849
7850 #if 0
7851   if (game.explosions_delayed)
7852 #endif
7853   {
7854     game.explosions_delayed = FALSE;
7855
7856     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7857     {
7858       element = Feld[x][y];
7859
7860       if (ExplodeField[x][y])
7861         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7862       else if (element == EL_EXPLOSION)
7863         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7864
7865       ExplodeField[x][y] = EX_NO_EXPLOSION;
7866     }
7867
7868     game.explosions_delayed = TRUE;
7869   }
7870
7871   if (game.magic_wall_active)
7872   {
7873     if (!(game.magic_wall_time_left % 4))
7874     {
7875       int element = Feld[magic_wall_x][magic_wall_y];
7876
7877       if (element == EL_BD_MAGIC_WALL_FULL ||
7878           element == EL_BD_MAGIC_WALL_ACTIVE ||
7879           element == EL_BD_MAGIC_WALL_EMPTYING)
7880         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7881       else
7882         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7883     }
7884
7885     if (game.magic_wall_time_left > 0)
7886     {
7887       game.magic_wall_time_left--;
7888       if (!game.magic_wall_time_left)
7889       {
7890         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7891         {
7892           element = Feld[x][y];
7893
7894           if (element == EL_MAGIC_WALL_ACTIVE ||
7895               element == EL_MAGIC_WALL_FULL)
7896           {
7897             Feld[x][y] = EL_MAGIC_WALL_DEAD;
7898             DrawLevelField(x, y);
7899           }
7900           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7901                    element == EL_BD_MAGIC_WALL_FULL)
7902           {
7903             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7904             DrawLevelField(x, y);
7905           }
7906         }
7907
7908         game.magic_wall_active = FALSE;
7909       }
7910     }
7911   }
7912
7913   if (game.light_time_left > 0)
7914   {
7915     game.light_time_left--;
7916
7917     if (game.light_time_left == 0)
7918       RedrawAllLightSwitchesAndInvisibleElements();
7919   }
7920
7921   if (game.timegate_time_left > 0)
7922   {
7923     game.timegate_time_left--;
7924
7925     if (game.timegate_time_left == 0)
7926       CloseAllOpenTimegates();
7927   }
7928
7929   for (i = 0; i < MAX_PLAYERS; i++)
7930   {
7931     struct PlayerInfo *player = &stored_player[i];
7932
7933     if (SHIELD_ON(player))
7934     {
7935       if (player->shield_deadly_time_left)
7936         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7937       else if (player->shield_normal_time_left)
7938         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7939     }
7940   }
7941
7942   if (TimeFrames >= FRAMES_PER_SECOND)
7943   {
7944     TimeFrames = 0;
7945     TapeTime++;
7946
7947     if (!level.use_step_counter)
7948     {
7949       TimePlayed++;
7950
7951       for (i = 0; i < MAX_PLAYERS; i++)
7952       {
7953         struct PlayerInfo *player = &stored_player[i];
7954
7955         if (SHIELD_ON(player))
7956         {
7957           player->shield_normal_time_left--;
7958
7959           if (player->shield_deadly_time_left > 0)
7960             player->shield_deadly_time_left--;
7961         }
7962       }
7963
7964       if (TimeLeft > 0)
7965       {
7966         TimeLeft--;
7967
7968         if (TimeLeft <= 10 && setup.time_limit)
7969           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7970
7971         DrawGameValue_Time(TimeLeft);
7972
7973         if (!TimeLeft && setup.time_limit)
7974           for (i = 0; i < MAX_PLAYERS; i++)
7975             KillHero(&stored_player[i]);
7976       }
7977       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
7978         DrawGameValue_Time(TimePlayed);
7979     }
7980
7981     if (tape.recording || tape.playing)
7982       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
7983   }
7984
7985   DrawAllPlayers();
7986   PlayAllPlayersSound();
7987
7988   if (options.debug)                    /* calculate frames per second */
7989   {
7990     static unsigned long fps_counter = 0;
7991     static int fps_frames = 0;
7992     unsigned long fps_delay_ms = Counter() - fps_counter;
7993
7994     fps_frames++;
7995
7996     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
7997     {
7998       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7999
8000       fps_frames = 0;
8001       fps_counter = Counter();
8002     }
8003
8004     redraw_mask |= REDRAW_FPS;
8005   }
8006
8007 #if 0
8008   if (stored_player[0].jx != stored_player[0].last_jx ||
8009       stored_player[0].jy != stored_player[0].last_jy)
8010     printf("::: %d, %d, %d, %d, %d\n",
8011            stored_player[0].MovDir,
8012            stored_player[0].MovPos,
8013            stored_player[0].GfxPos,
8014            stored_player[0].Frame,
8015            stored_player[0].StepFrame);
8016 #endif
8017
8018 #if 1
8019   FrameCounter++;
8020   TimeFrames++;
8021
8022   for (i = 0; i < MAX_PLAYERS; i++)
8023   {
8024     int move_frames =
8025       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
8026
8027     stored_player[i].Frame += move_frames;
8028
8029     if (stored_player[i].MovPos != 0)
8030       stored_player[i].StepFrame += move_frames;
8031
8032     if (stored_player[i].drop_delay > 0)
8033       stored_player[i].drop_delay--;
8034   }
8035 #endif
8036
8037 #if 1
8038   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8039   {
8040     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8041
8042     local_player->show_envelope = 0;
8043   }
8044 #endif
8045 }
8046
8047 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8048 {
8049   int min_x = x, min_y = y, max_x = x, max_y = y;
8050   int i;
8051
8052   for (i = 0; i < MAX_PLAYERS; i++)
8053   {
8054     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8055
8056     if (!stored_player[i].active || &stored_player[i] == player)
8057       continue;
8058
8059     min_x = MIN(min_x, jx);
8060     min_y = MIN(min_y, jy);
8061     max_x = MAX(max_x, jx);
8062     max_y = MAX(max_y, jy);
8063   }
8064
8065   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8066 }
8067
8068 static boolean AllPlayersInVisibleScreen()
8069 {
8070   int i;
8071
8072   for (i = 0; i < MAX_PLAYERS; i++)
8073   {
8074     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8075
8076     if (!stored_player[i].active)
8077       continue;
8078
8079     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8080       return FALSE;
8081   }
8082
8083   return TRUE;
8084 }
8085
8086 void ScrollLevel(int dx, int dy)
8087 {
8088   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8089   int x, y;
8090
8091   BlitBitmap(drawto_field, drawto_field,
8092              FX + TILEX * (dx == -1) - softscroll_offset,
8093              FY + TILEY * (dy == -1) - softscroll_offset,
8094              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8095              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8096              FX + TILEX * (dx == 1) - softscroll_offset,
8097              FY + TILEY * (dy == 1) - softscroll_offset);
8098
8099   if (dx)
8100   {
8101     x = (dx == 1 ? BX1 : BX2);
8102     for (y = BY1; y <= BY2; y++)
8103       DrawScreenField(x, y);
8104   }
8105
8106   if (dy)
8107   {
8108     y = (dy == 1 ? BY1 : BY2);
8109     for (x = BX1; x <= BX2; x++)
8110       DrawScreenField(x, y);
8111   }
8112
8113   redraw_mask |= REDRAW_FIELD;
8114 }
8115
8116 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8117 {
8118   int nextx = x + dx, nexty = y + dy;
8119   int element = Feld[x][y];
8120
8121   if ((dx == -1 &&
8122        element != EL_SP_PORT_LEFT &&
8123        element != EL_SP_GRAVITY_PORT_LEFT &&
8124        element != EL_SP_PORT_HORIZONTAL &&
8125        element != EL_SP_PORT_ANY) ||
8126       (dx == +1 &&
8127        element != EL_SP_PORT_RIGHT &&
8128        element != EL_SP_GRAVITY_PORT_RIGHT &&
8129        element != EL_SP_PORT_HORIZONTAL &&
8130        element != EL_SP_PORT_ANY) ||
8131       (dy == -1 &&
8132        element != EL_SP_PORT_UP &&
8133        element != EL_SP_GRAVITY_PORT_UP &&
8134        element != EL_SP_PORT_VERTICAL &&
8135        element != EL_SP_PORT_ANY) ||
8136       (dy == +1 &&
8137        element != EL_SP_PORT_DOWN &&
8138        element != EL_SP_GRAVITY_PORT_DOWN &&
8139        element != EL_SP_PORT_VERTICAL &&
8140        element != EL_SP_PORT_ANY) ||
8141       !IN_LEV_FIELD(nextx, nexty) ||
8142       !IS_FREE(nextx, nexty))
8143     return FALSE;
8144
8145   return TRUE;
8146 }
8147
8148 static void CheckGravityMovement(struct PlayerInfo *player)
8149 {
8150   if (game.gravity && !player->programmed_action)
8151   {
8152     int move_dir_horizontal = player->action & MV_HORIZONTAL;
8153     int move_dir_vertical   = player->action & MV_VERTICAL;
8154     int move_dir =
8155       (player->last_move_dir & MV_HORIZONTAL ?
8156        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8157        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8158     int jx = player->jx, jy = player->jy;
8159     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8160     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8161     int new_jx = jx + dx, new_jy = jy + dy;
8162     boolean player_is_snapping = player->action & JOY_BUTTON_1;
8163 #if 1
8164     boolean player_can_fall_down =
8165       (IN_LEV_FIELD(jx, jy + 1) &&
8166        (IS_FREE(jx, jy + 1) ||
8167         (Feld[jx][jy + 1] == EL_ACID && level.player_can_fall_into_acid)));
8168 #else
8169     boolean player_can_fall_down =
8170       (IN_LEV_FIELD(jx, jy + 1) &&
8171        (IS_FREE(jx, jy + 1)));
8172 #endif
8173     boolean player_is_moving_to_valid_field =
8174       (
8175 #if 1
8176        !player_is_snapping &&
8177 #endif
8178        IN_LEV_FIELD(new_jx, new_jy) &&
8179        (Feld[new_jx][new_jy] == EL_SP_BASE ||
8180         Feld[new_jx][new_jy] == EL_SAND ||
8181         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8182          canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8183     /* !!! extend EL_SAND to anything diggable !!! */
8184
8185     boolean player_is_standing_on_valid_field =
8186       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8187        (IS_WALKABLE(Feld[jx][jy]) &&
8188         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8189
8190 #if 0
8191     printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
8192            player_can_fall_down,
8193            player_is_standing_on_valid_field,
8194            player_is_moving_to_valid_field,
8195            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
8196 #endif
8197
8198     if (player_can_fall_down &&
8199         !player_is_standing_on_valid_field &&
8200         !player_is_moving_to_valid_field)
8201     {
8202 #if 0
8203       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8204              jx, jy, FrameCounter);
8205 #endif
8206
8207       player->programmed_action = MV_DOWN;
8208     }
8209   }
8210 }
8211
8212 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8213 {
8214 #if 1
8215   return CheckGravityMovement(player);
8216 #endif
8217
8218   if (game.gravity && !player->programmed_action)
8219   {
8220     int jx = player->jx, jy = player->jy;
8221     boolean field_under_player_is_free =
8222       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8223     boolean player_is_standing_on_valid_field =
8224       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8225        (IS_WALKABLE(Feld[jx][jy]) &&
8226         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8227
8228     if (field_under_player_is_free && !player_is_standing_on_valid_field)
8229       player->programmed_action = MV_DOWN;
8230   }
8231 }
8232
8233 /*
8234   MovePlayerOneStep()
8235   -----------------------------------------------------------------------------
8236   dx, dy:               direction (non-diagonal) to try to move the player to
8237   real_dx, real_dy:     direction as read from input device (can be diagonal)
8238 */
8239
8240 boolean MovePlayerOneStep(struct PlayerInfo *player,
8241                           int dx, int dy, int real_dx, int real_dy)
8242 {
8243 #if 0
8244   static int trigger_sides[4][2] =
8245   {
8246     /* enter side        leave side */
8247     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
8248     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
8249     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
8250     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
8251   };
8252   int move_direction = (dx == -1 ? MV_LEFT :
8253                         dx == +1 ? MV_RIGHT :
8254                         dy == -1 ? MV_UP :
8255                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8256   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8257   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8258 #endif
8259   int jx = player->jx, jy = player->jy;
8260   int new_jx = jx + dx, new_jy = jy + dy;
8261   int element;
8262   int can_move;
8263
8264   if (!player->active || (!dx && !dy))
8265     return MF_NO_ACTION;
8266
8267   player->MovDir = (dx < 0 ? MV_LEFT :
8268                     dx > 0 ? MV_RIGHT :
8269                     dy < 0 ? MV_UP :
8270                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
8271
8272   if (!IN_LEV_FIELD(new_jx, new_jy))
8273     return MF_NO_ACTION;
8274
8275   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8276     return MF_NO_ACTION;
8277
8278 #if 0
8279   element = MovingOrBlocked2Element(new_jx, new_jy);
8280 #else
8281   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8282 #endif
8283
8284   if (DONT_RUN_INTO(element))
8285   {
8286     if (element == EL_ACID && dx == 0 && dy == 1)
8287     {
8288       SplashAcid(new_jx, new_jy);
8289       Feld[jx][jy] = EL_PLAYER_1;
8290       InitMovingField(jx, jy, MV_DOWN);
8291       Store[jx][jy] = EL_ACID;
8292       ContinueMoving(jx, jy);
8293       BuryHero(player);
8294     }
8295     else
8296       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8297
8298     return MF_MOVING;
8299   }
8300
8301   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8302   if (can_move != MF_MOVING)
8303     return can_move;
8304
8305   /* check if DigField() has caused relocation of the player */
8306   if (player->jx != jx || player->jy != jy)
8307     return MF_NO_ACTION;
8308
8309   StorePlayer[jx][jy] = 0;
8310   player->last_jx = jx;
8311   player->last_jy = jy;
8312   player->jx = new_jx;
8313   player->jy = new_jy;
8314   StorePlayer[new_jx][new_jy] = player->element_nr;
8315
8316   player->MovPos =
8317     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8318
8319   player->step_counter++;
8320
8321   player->drop_delay = 0;
8322
8323   PlayerVisit[jx][jy] = FrameCounter;
8324
8325   ScrollPlayer(player, SCROLL_INIT);
8326
8327 #if 0
8328   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8329   {
8330     CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8331                                     leave_side);
8332     CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8333   }
8334
8335   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8336   {
8337     CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8338                                     CE_OTHER_GETS_ENTERED, enter_side);
8339     CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8340                            CE_ENTERED_BY_PLAYER, enter_side);
8341   }
8342 #endif
8343
8344   return MF_MOVING;
8345 }
8346
8347 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8348 {
8349   int jx = player->jx, jy = player->jy;
8350   int old_jx = jx, old_jy = jy;
8351   int moved = MF_NO_ACTION;
8352
8353 #if 1
8354   if (!player->active)
8355     return FALSE;
8356
8357   if (!dx && !dy)
8358   {
8359     if (player->MovPos == 0)
8360     {
8361       player->is_moving = FALSE;
8362       player->is_digging = FALSE;
8363       player->is_collecting = FALSE;
8364       player->is_snapping = FALSE;
8365       player->is_pushing = FALSE;
8366     }
8367
8368     return FALSE;
8369   }
8370 #else
8371   if (!player->active || (!dx && !dy))
8372     return FALSE;
8373 #endif
8374
8375 #if 0
8376   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8377       !tape.playing)
8378     return FALSE;
8379 #else
8380
8381 #if 1
8382   if (!FrameReached(&player->move_delay, player->move_delay_value))
8383     return FALSE;
8384 #else
8385   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8386       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8387     return FALSE;
8388 #endif
8389
8390 #endif
8391
8392   /* remove the last programmed player action */
8393   player->programmed_action = 0;
8394
8395   if (player->MovPos)
8396   {
8397     /* should only happen if pre-1.2 tape recordings are played */
8398     /* this is only for backward compatibility */
8399
8400     int original_move_delay_value = player->move_delay_value;
8401
8402 #if DEBUG
8403     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8404            tape.counter);
8405 #endif
8406
8407     /* scroll remaining steps with finest movement resolution */
8408     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8409
8410     while (player->MovPos)
8411     {
8412       ScrollPlayer(player, SCROLL_GO_ON);
8413       ScrollScreen(NULL, SCROLL_GO_ON);
8414       FrameCounter++;
8415       DrawAllPlayers();
8416       BackToFront();
8417     }
8418
8419     player->move_delay_value = original_move_delay_value;
8420   }
8421
8422   if (player->last_move_dir & MV_HORIZONTAL)
8423   {
8424     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8425       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8426   }
8427   else
8428   {
8429     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8430       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8431   }
8432
8433   jx = player->jx;
8434   jy = player->jy;
8435
8436   if (moved & MF_MOVING && !ScreenMovPos &&
8437       (player == local_player || !options.network))
8438   {
8439     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8440     int offset = (setup.scroll_delay ? 3 : 0);
8441
8442     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8443     {
8444       /* actual player has left the screen -- scroll in that direction */
8445       if (jx != old_jx)         /* player has moved horizontally */
8446         scroll_x += (jx - old_jx);
8447       else                      /* player has moved vertically */
8448         scroll_y += (jy - old_jy);
8449     }
8450     else
8451     {
8452       if (jx != old_jx)         /* player has moved horizontally */
8453       {
8454         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8455             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8456           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8457
8458         /* don't scroll over playfield boundaries */
8459         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8460           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8461
8462         /* don't scroll more than one field at a time */
8463         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8464
8465         /* don't scroll against the player's moving direction */
8466         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8467             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8468           scroll_x = old_scroll_x;
8469       }
8470       else                      /* player has moved vertically */
8471       {
8472         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8473             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8474           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8475
8476         /* don't scroll over playfield boundaries */
8477         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8478           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8479
8480         /* don't scroll more than one field at a time */
8481         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8482
8483         /* don't scroll against the player's moving direction */
8484         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8485             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8486           scroll_y = old_scroll_y;
8487       }
8488     }
8489
8490     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8491     {
8492       if (!options.network && !AllPlayersInVisibleScreen())
8493       {
8494         scroll_x = old_scroll_x;
8495         scroll_y = old_scroll_y;
8496       }
8497       else
8498       {
8499         ScrollScreen(player, SCROLL_INIT);
8500         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8501       }
8502     }
8503   }
8504
8505 #if 0
8506 #if 1
8507   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8508 #else
8509   if (!(moved & MF_MOVING) && !player->is_pushing)
8510     player->Frame = 0;
8511 #endif
8512 #endif
8513
8514   player->StepFrame = 0;
8515
8516   if (moved & MF_MOVING)
8517   {
8518     if (old_jx != jx && old_jy == jy)
8519       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8520     else if (old_jx == jx && old_jy != jy)
8521       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8522
8523     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8524
8525     player->last_move_dir = player->MovDir;
8526     player->is_moving = TRUE;
8527 #if 1
8528     player->is_snapping = FALSE;
8529 #endif
8530
8531 #if 1
8532     player->is_switching = FALSE;
8533 #endif
8534
8535     player->is_dropping = FALSE;
8536
8537
8538 #if 1
8539     {
8540       static int trigger_sides[4][2] =
8541       {
8542         /* enter side           leave side */
8543         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8544         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8545         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8546         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8547       };
8548       int move_direction = player->MovDir;
8549       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8550       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8551
8552 #if 1
8553       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8554       {
8555         CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8556                                           CE_OTHER_GETS_LEFT,
8557                                           player->index_bit, leave_side);
8558         CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8559                                  CE_LEFT_BY_PLAYER,
8560                                  player->index_bit, leave_side);
8561       }
8562
8563       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8564       {
8565         CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8566                                           CE_OTHER_GETS_ENTERED,
8567                                           player->index_bit, enter_side);
8568         CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8569                                  player->index_bit, enter_side);
8570       }
8571 #endif
8572
8573     }
8574 #endif
8575
8576
8577   }
8578   else
8579   {
8580     CheckGravityMovementWhenNotMoving(player);
8581
8582     /*
8583     player->last_move_dir = MV_NO_MOVING;
8584     */
8585     player->is_moving = FALSE;
8586   }
8587
8588   if (game.engine_version < VERSION_IDENT(3,0,7,0))
8589   {
8590     TestIfHeroTouchesBadThing(jx, jy);
8591     TestIfPlayerTouchesCustomElement(jx, jy);
8592   }
8593
8594   if (!player->active)
8595     RemoveHero(player);
8596
8597   return moved;
8598 }
8599
8600 void ScrollPlayer(struct PlayerInfo *player, int mode)
8601 {
8602   int jx = player->jx, jy = player->jy;
8603   int last_jx = player->last_jx, last_jy = player->last_jy;
8604   int move_stepsize = TILEX / player->move_delay_value;
8605
8606   if (!player->active || !player->MovPos)
8607     return;
8608
8609   if (mode == SCROLL_INIT)
8610   {
8611     player->actual_frame_counter = FrameCounter;
8612     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8613
8614     if (Feld[last_jx][last_jy] == EL_EMPTY)
8615       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8616
8617 #if 0
8618     DrawPlayer(player);
8619 #endif
8620
8621     return;
8622   }
8623   else if (!FrameReached(&player->actual_frame_counter, 1))
8624     return;
8625
8626   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8627   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8628
8629   if (!player->block_last_field &&
8630       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8631     Feld[last_jx][last_jy] = EL_EMPTY;
8632
8633   /* before DrawPlayer() to draw correct player graphic for this case */
8634   if (player->MovPos == 0)
8635     CheckGravityMovement(player);
8636
8637 #if 0
8638   DrawPlayer(player);   /* needed here only to cleanup last field */
8639 #endif
8640
8641   if (player->MovPos == 0)      /* player reached destination field */
8642   {
8643 #if 1
8644     if (player->move_delay_reset_counter > 0)
8645     {
8646       player->move_delay_reset_counter--;
8647
8648       if (player->move_delay_reset_counter == 0)
8649       {
8650         /* continue with normal speed after quickly moving through gate */
8651         HALVE_PLAYER_SPEED(player);
8652
8653         /* be able to make the next move without delay */
8654         player->move_delay = 0;
8655       }
8656     }
8657 #else
8658     if (IS_PASSABLE(Feld[last_jx][last_jy]))
8659     {
8660       /* continue with normal speed after quickly moving through gate */
8661       HALVE_PLAYER_SPEED(player);
8662
8663       /* be able to make the next move without delay */
8664       player->move_delay = 0;
8665     }
8666 #endif
8667
8668     if (player->block_last_field &&
8669         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8670       Feld[last_jx][last_jy] = EL_EMPTY;
8671
8672     player->last_jx = jx;
8673     player->last_jy = jy;
8674
8675     if (Feld[jx][jy] == EL_EXIT_OPEN ||
8676         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8677         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
8678     {
8679       DrawPlayer(player);       /* needed here only to cleanup last field */
8680       RemoveHero(player);
8681
8682       if (local_player->friends_still_needed == 0 ||
8683           IS_SP_ELEMENT(Feld[jx][jy]))
8684         player->LevelSolved = player->GameOver = TRUE;
8685     }
8686
8687 #if 0
8688     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
8689     {
8690       static int trigger_sides[4][2] =
8691       {
8692         /* enter side           leave side */
8693         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8694         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8695         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8696         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8697       };
8698       int move_direction = player->MovDir;
8699       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8700       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8701       int old_jx = last_jx;
8702       int old_jy = last_jy;
8703
8704 #if 1
8705       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8706       {
8707         CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8708                                           CE_OTHER_GETS_LEFT,
8709                                           player->index_bit, leave_side);
8710         CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8711                                  CE_LEFT_BY_PLAYER,
8712                                  player->index_bit, leave_side);
8713       }
8714
8715       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8716       {
8717         CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8718                                           CE_OTHER_GETS_ENTERED,
8719                                           player->index_bit, enter_side);
8720         CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8721                                  player->index_bit, enter_side);
8722       }
8723 #endif
8724
8725     }
8726 #endif
8727
8728     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8729     {
8730       TestIfHeroTouchesBadThing(jx, jy);
8731       TestIfPlayerTouchesCustomElement(jx, jy);
8732 #if 1
8733       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
8734 #endif
8735
8736       if (!player->active)
8737         RemoveHero(player);
8738     }
8739
8740     if (level.use_step_counter)
8741     {
8742       int i;
8743
8744       TimePlayed++;
8745
8746       for (i = 0; i < MAX_PLAYERS; i++)
8747       {
8748         struct PlayerInfo *player = &stored_player[i];
8749
8750         if (SHIELD_ON(player))
8751         {
8752           player->shield_normal_time_left--;
8753
8754           if (player->shield_deadly_time_left > 0)
8755             player->shield_deadly_time_left--;
8756         }
8757       }
8758
8759       if (TimeLeft > 0)
8760       {
8761         TimeLeft--;
8762
8763         if (TimeLeft <= 10 && setup.time_limit)
8764           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8765
8766         DrawGameValue_Time(TimeLeft);
8767
8768         if (!TimeLeft && setup.time_limit)
8769           for (i = 0; i < MAX_PLAYERS; i++)
8770             KillHero(&stored_player[i]);
8771       }
8772       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8773         DrawGameValue_Time(TimePlayed);
8774     }
8775
8776     if (tape.single_step && tape.recording && !tape.pausing &&
8777         !player->programmed_action)
8778       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8779   }
8780 }
8781
8782 void ScrollScreen(struct PlayerInfo *player, int mode)
8783 {
8784   static unsigned long screen_frame_counter = 0;
8785
8786   if (mode == SCROLL_INIT)
8787   {
8788     /* set scrolling step size according to actual player's moving speed */
8789     ScrollStepSize = TILEX / player->move_delay_value;
8790
8791     screen_frame_counter = FrameCounter;
8792     ScreenMovDir = player->MovDir;
8793     ScreenMovPos = player->MovPos;
8794     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8795     return;
8796   }
8797   else if (!FrameReached(&screen_frame_counter, 1))
8798     return;
8799
8800   if (ScreenMovPos)
8801   {
8802     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8803     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8804     redraw_mask |= REDRAW_FIELD;
8805   }
8806   else
8807     ScreenMovDir = MV_NO_MOVING;
8808 }
8809
8810 void TestIfPlayerTouchesCustomElement(int x, int y)
8811 {
8812   static int xy[4][2] =
8813   {
8814     { 0, -1 },
8815     { -1, 0 },
8816     { +1, 0 },
8817     { 0, +1 }
8818   };
8819   static int trigger_sides[4][2] =
8820   {
8821     /* center side       border side */
8822     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8823     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8824     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8825     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8826   };
8827   static int touch_dir[4] =
8828   {
8829     MV_LEFT | MV_RIGHT,
8830     MV_UP   | MV_DOWN,
8831     MV_UP   | MV_DOWN,
8832     MV_LEFT | MV_RIGHT
8833   };
8834   int center_element = Feld[x][y];      /* should always be non-moving! */
8835   int i;
8836
8837   for (i = 0; i < NUM_DIRECTIONS; i++)
8838   {
8839     int xx = x + xy[i][0];
8840     int yy = y + xy[i][1];
8841     int center_side = trigger_sides[i][0];
8842     int border_side = trigger_sides[i][1];
8843     int border_element;
8844
8845     if (!IN_LEV_FIELD(xx, yy))
8846       continue;
8847
8848     if (IS_PLAYER(x, y))
8849     {
8850       struct PlayerInfo *player = PLAYERINFO(x, y);
8851
8852       if (game.engine_version < VERSION_IDENT(3,0,7,0))
8853         border_element = Feld[xx][yy];          /* may be moving! */
8854       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8855         border_element = Feld[xx][yy];
8856       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
8857         border_element = MovingOrBlocked2Element(xx, yy);
8858       else
8859         continue;               /* center and border element do not touch */
8860
8861       CheckTriggeredElementChangePlayer(xx, yy, border_element,
8862                                         CE_OTHER_GETS_TOUCHED,
8863                                         player->index_bit, border_side);
8864       CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8865                                player->index_bit, border_side);
8866     }
8867     else if (IS_PLAYER(xx, yy))
8868     {
8869       struct PlayerInfo *player = PLAYERINFO(xx, yy);
8870
8871       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8872       {
8873         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8874           continue;             /* center and border element do not touch */
8875       }
8876
8877       CheckTriggeredElementChangePlayer(x, y, center_element,
8878                                         CE_OTHER_GETS_TOUCHED,
8879                                         player->index_bit, center_side);
8880       CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8881                                player->index_bit, center_side);
8882
8883       break;
8884     }
8885   }
8886 }
8887
8888 void TestIfElementTouchesCustomElement(int x, int y)
8889 {
8890   static int xy[4][2] =
8891   {
8892     { 0, -1 },
8893     { -1, 0 },
8894     { +1, 0 },
8895     { 0, +1 }
8896   };
8897   static int trigger_sides[4][2] =
8898   {
8899     /* center side      border side */
8900     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8901     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8902     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8903     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8904   };
8905   static int touch_dir[4] =
8906   {
8907     MV_LEFT | MV_RIGHT,
8908     MV_UP   | MV_DOWN,
8909     MV_UP   | MV_DOWN,
8910     MV_LEFT | MV_RIGHT
8911   };
8912   boolean change_center_element = FALSE;
8913   int center_element_change_page = 0;
8914   int center_element = Feld[x][y];      /* should always be non-moving! */
8915   int i, j;
8916
8917   for (i = 0; i < NUM_DIRECTIONS; i++)
8918   {
8919     int xx = x + xy[i][0];
8920     int yy = y + xy[i][1];
8921     int center_side = trigger_sides[i][0];
8922     int border_side = trigger_sides[i][1];
8923     int border_element;
8924
8925     if (!IN_LEV_FIELD(xx, yy))
8926       continue;
8927
8928     if (game.engine_version < VERSION_IDENT(3,0,7,0))
8929       border_element = Feld[xx][yy];    /* may be moving! */
8930     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8931       border_element = Feld[xx][yy];
8932     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
8933       border_element = MovingOrBlocked2Element(xx, yy);
8934     else
8935       continue;                 /* center and border element do not touch */
8936
8937     /* check for change of center element (but change it only once) */
8938     if (IS_CUSTOM_ELEMENT(center_element) &&
8939         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8940         !change_center_element)
8941     {
8942       for (j = 0; j < element_info[center_element].num_change_pages; j++)
8943       {
8944         struct ElementChangeInfo *change =
8945           &element_info[center_element].change_page[j];
8946
8947         if (change->can_change &&
8948             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8949             change->trigger_side & border_side &&
8950 #if 1
8951             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8952 #else
8953             change->trigger_element == border_element
8954 #endif
8955             )
8956         {
8957           change_center_element = TRUE;
8958           center_element_change_page = j;
8959
8960           break;
8961         }
8962       }
8963     }
8964
8965     /* check for change of border element */
8966     if (IS_CUSTOM_ELEMENT(border_element) &&
8967         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8968     {
8969       for (j = 0; j < element_info[border_element].num_change_pages; j++)
8970       {
8971         struct ElementChangeInfo *change =
8972           &element_info[border_element].change_page[j];
8973
8974         if (change->can_change &&
8975             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8976             change->trigger_side & center_side &&
8977 #if 1
8978             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8979 #else
8980             change->trigger_element == center_element
8981 #endif
8982             )
8983         {
8984           CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
8985                                  j);
8986           break;
8987         }
8988       }
8989     }
8990   }
8991
8992   if (change_center_element)
8993     CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
8994                            center_element_change_page);
8995 }
8996
8997 void TestIfElementHitsCustomElement(int x, int y, int direction)
8998 {
8999   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9000   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9001   int hitx = x + dx, hity = y + dy;
9002   int hitting_element = Feld[x][y];
9003 #if 0
9004   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9005                         !IS_FREE(hitx, hity) &&
9006                         (!IS_MOVING(hitx, hity) ||
9007                          MovDir[hitx][hity] != direction ||
9008                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9009 #endif
9010
9011   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9012     return;
9013
9014 #if 0
9015   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9016     return;
9017 #endif
9018
9019   CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
9020                          direction);
9021
9022   if (IN_LEV_FIELD(hitx, hity))
9023   {
9024     int opposite_direction = MV_DIR_OPPOSITE(direction);
9025     int hitting_side = direction;
9026     int touched_side = opposite_direction;
9027     int touched_element = MovingOrBlocked2Element(hitx, hity);
9028 #if 1
9029     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9030                           MovDir[hitx][hity] != direction ||
9031                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9032
9033     object_hit = TRUE;
9034 #endif
9035
9036     if (object_hit)
9037     {
9038       int i;
9039
9040       CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
9041                              opposite_direction);
9042
9043       if (IS_CUSTOM_ELEMENT(hitting_element) &&
9044           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9045       {
9046         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9047         {
9048           struct ElementChangeInfo *change =
9049             &element_info[hitting_element].change_page[i];
9050
9051           if (change->can_change &&
9052               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9053               change->trigger_side & touched_side &&
9054           
9055 #if 1
9056               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9057 #else
9058               change->trigger_element == touched_element
9059 #endif
9060               )
9061           {
9062             CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
9063                                    i);
9064             break;
9065           }
9066         }
9067       }
9068
9069       if (IS_CUSTOM_ELEMENT(touched_element) &&
9070           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9071       {
9072         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9073         {
9074           struct ElementChangeInfo *change =
9075             &element_info[touched_element].change_page[i];
9076
9077           if (change->can_change &&
9078               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9079               change->trigger_side & hitting_side &&
9080 #if 1
9081               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9082 #else
9083               change->trigger_element == hitting_element
9084 #endif
9085               )
9086           {
9087             CheckElementChangePage(hitx, hity, touched_element,
9088                                    CE_OTHER_GETS_HIT, i);
9089             break;
9090           }
9091         }
9092       }
9093     }
9094   }
9095 }
9096
9097 #if 0
9098 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9099 {
9100   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9101   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9102   int hitx = x + dx, hity = y + dy;
9103   int hitting_element = Feld[x][y];
9104 #if 0
9105   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9106                         !IS_FREE(hitx, hity) &&
9107                         (!IS_MOVING(hitx, hity) ||
9108                          MovDir[hitx][hity] != direction ||
9109                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9110 #endif
9111
9112   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9113     return;
9114
9115 #if 0
9116   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9117     return;
9118 #endif
9119
9120   CheckElementChangeSide(x, y, hitting_element, EP_CAN_SMASH_EVERYTHING,
9121                          direction);
9122
9123   if (IN_LEV_FIELD(hitx, hity))
9124   {
9125     int opposite_direction = MV_DIR_OPPOSITE(direction);
9126     int hitting_side = direction;
9127     int touched_side = opposite_direction;
9128     int touched_element = MovingOrBlocked2Element(hitx, hity);
9129 #if 1
9130     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9131                           MovDir[hitx][hity] != direction ||
9132                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9133
9134     object_hit = TRUE;
9135 #endif
9136
9137     if (object_hit)
9138     {
9139       int i;
9140
9141       CheckElementChangeSide(hitx, hity, touched_element,
9142                              CE_SMASHED_BY_SOMETHING, opposite_direction);
9143
9144       if (IS_CUSTOM_ELEMENT(hitting_element) &&
9145           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9146       {
9147         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9148         {
9149           struct ElementChangeInfo *change =
9150             &element_info[hitting_element].change_page[i];
9151
9152           if (change->can_change &&
9153               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9154               change->trigger_side & touched_side &&
9155           
9156 #if 1
9157               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9158 #else
9159               change->trigger_element == touched_element
9160 #endif
9161               )
9162           {
9163             CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_SMASHING,
9164                                    i);
9165             break;
9166           }
9167         }
9168       }
9169
9170       if (IS_CUSTOM_ELEMENT(touched_element) &&
9171           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9172       {
9173         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9174         {
9175           struct ElementChangeInfo *change =
9176             &element_info[touched_element].change_page[i];
9177
9178           if (change->can_change &&
9179               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9180               change->trigger_side & hitting_side &&
9181 #if 1
9182               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9183 #else
9184               change->trigger_element == hitting_element
9185 #endif
9186               )
9187           {
9188             CheckElementChangePage(hitx, hity, touched_element,
9189                                    CE_OTHER_GETS_SMASHED, i);
9190             break;
9191           }
9192         }
9193       }
9194     }
9195   }
9196 }
9197 #endif
9198
9199 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9200 {
9201   int i, kill_x = -1, kill_y = -1;
9202   static int test_xy[4][2] =
9203   {
9204     { 0, -1 },
9205     { -1, 0 },
9206     { +1, 0 },
9207     { 0, +1 }
9208   };
9209   static int test_dir[4] =
9210   {
9211     MV_UP,
9212     MV_LEFT,
9213     MV_RIGHT,
9214     MV_DOWN
9215   };
9216
9217   for (i = 0; i < NUM_DIRECTIONS; i++)
9218   {
9219     int test_x, test_y, test_move_dir, test_element;
9220
9221     test_x = good_x + test_xy[i][0];
9222     test_y = good_y + test_xy[i][1];
9223     if (!IN_LEV_FIELD(test_x, test_y))
9224       continue;
9225
9226     test_move_dir =
9227       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9228
9229 #if 0
9230     test_element = Feld[test_x][test_y];
9231 #else
9232     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9233 #endif
9234
9235     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9236        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9237     */
9238     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9239         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
9240     {
9241       kill_x = test_x;
9242       kill_y = test_y;
9243       break;
9244     }
9245   }
9246
9247   if (kill_x != -1 || kill_y != -1)
9248   {
9249     if (IS_PLAYER(good_x, good_y))
9250     {
9251       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9252
9253       if (player->shield_deadly_time_left > 0)
9254         Bang(kill_x, kill_y);
9255       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9256         KillHero(player);
9257     }
9258     else
9259       Bang(good_x, good_y);
9260   }
9261 }
9262
9263 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9264 {
9265   int i, kill_x = -1, kill_y = -1;
9266   int bad_element = Feld[bad_x][bad_y];
9267   static int test_xy[4][2] =
9268   {
9269     { 0, -1 },
9270     { -1, 0 },
9271     { +1, 0 },
9272     { 0, +1 }
9273   };
9274   static int touch_dir[4] =
9275   {
9276     MV_LEFT | MV_RIGHT,
9277     MV_UP   | MV_DOWN,
9278     MV_UP   | MV_DOWN,
9279     MV_LEFT | MV_RIGHT
9280   };
9281   static int test_dir[4] =
9282   {
9283     MV_UP,
9284     MV_LEFT,
9285     MV_RIGHT,
9286     MV_DOWN
9287   };
9288
9289   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
9290     return;
9291
9292   for (i = 0; i < NUM_DIRECTIONS; i++)
9293   {
9294     int test_x, test_y, test_move_dir, test_element;
9295
9296     test_x = bad_x + test_xy[i][0];
9297     test_y = bad_y + test_xy[i][1];
9298     if (!IN_LEV_FIELD(test_x, test_y))
9299       continue;
9300
9301     test_move_dir =
9302       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9303
9304     test_element = Feld[test_x][test_y];
9305
9306     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9307        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9308     */
9309     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
9310         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
9311     {
9312       /* good thing is player or penguin that does not move away */
9313       if (IS_PLAYER(test_x, test_y))
9314       {
9315         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9316
9317         if (bad_element == EL_ROBOT && player->is_moving)
9318           continue;     /* robot does not kill player if he is moving */
9319
9320         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9321         {
9322           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9323             continue;           /* center and border element do not touch */
9324         }
9325
9326         kill_x = test_x;
9327         kill_y = test_y;
9328         break;
9329       }
9330       else if (test_element == EL_PENGUIN)
9331       {
9332         kill_x = test_x;
9333         kill_y = test_y;
9334         break;
9335       }
9336     }
9337   }
9338
9339   if (kill_x != -1 || kill_y != -1)
9340   {
9341     if (IS_PLAYER(kill_x, kill_y))
9342     {
9343       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9344
9345       if (player->shield_deadly_time_left > 0)
9346         Bang(bad_x, bad_y);
9347       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9348         KillHero(player);
9349     }
9350     else
9351       Bang(kill_x, kill_y);
9352   }
9353 }
9354
9355 void TestIfHeroTouchesBadThing(int x, int y)
9356 {
9357   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9358 }
9359
9360 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9361 {
9362   TestIfGoodThingHitsBadThing(x, y, move_dir);
9363 }
9364
9365 void TestIfBadThingTouchesHero(int x, int y)
9366 {
9367   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9368 }
9369
9370 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9371 {
9372   TestIfBadThingHitsGoodThing(x, y, move_dir);
9373 }
9374
9375 void TestIfFriendTouchesBadThing(int x, int y)
9376 {
9377   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9378 }
9379
9380 void TestIfBadThingTouchesFriend(int x, int y)
9381 {
9382   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9383 }
9384
9385 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9386 {
9387   int i, kill_x = bad_x, kill_y = bad_y;
9388   static int xy[4][2] =
9389   {
9390     { 0, -1 },
9391     { -1, 0 },
9392     { +1, 0 },
9393     { 0, +1 }
9394   };
9395
9396   for (i = 0; i < NUM_DIRECTIONS; i++)
9397   {
9398     int x, y, element;
9399
9400     x = bad_x + xy[i][0];
9401     y = bad_y + xy[i][1];
9402     if (!IN_LEV_FIELD(x, y))
9403       continue;
9404
9405     element = Feld[x][y];
9406     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9407         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9408     {
9409       kill_x = x;
9410       kill_y = y;
9411       break;
9412     }
9413   }
9414
9415   if (kill_x != bad_x || kill_y != bad_y)
9416     Bang(bad_x, bad_y);
9417 }
9418
9419 void KillHero(struct PlayerInfo *player)
9420 {
9421   int jx = player->jx, jy = player->jy;
9422
9423   if (!player->active)
9424     return;
9425
9426   /* remove accessible field at the player's position */
9427   Feld[jx][jy] = EL_EMPTY;
9428
9429   /* deactivate shield (else Bang()/Explode() would not work right) */
9430   player->shield_normal_time_left = 0;
9431   player->shield_deadly_time_left = 0;
9432
9433   Bang(jx, jy);
9434   BuryHero(player);
9435 }
9436
9437 static void KillHeroUnlessEnemyProtected(int x, int y)
9438 {
9439   if (!PLAYER_ENEMY_PROTECTED(x, y))
9440     KillHero(PLAYERINFO(x, y));
9441 }
9442
9443 static void KillHeroUnlessExplosionProtected(int x, int y)
9444 {
9445   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9446     KillHero(PLAYERINFO(x, y));
9447 }
9448
9449 void BuryHero(struct PlayerInfo *player)
9450 {
9451   int jx = player->jx, jy = player->jy;
9452
9453   if (!player->active)
9454     return;
9455
9456 #if 1
9457   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9458 #else
9459   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9460 #endif
9461   PlayLevelSound(jx, jy, SND_GAME_LOSING);
9462
9463   player->GameOver = TRUE;
9464   RemoveHero(player);
9465 }
9466
9467 void RemoveHero(struct PlayerInfo *player)
9468 {
9469   int jx = player->jx, jy = player->jy;
9470   int i, found = FALSE;
9471
9472   player->present = FALSE;
9473   player->active = FALSE;
9474
9475   if (!ExplodeField[jx][jy])
9476     StorePlayer[jx][jy] = 0;
9477
9478   for (i = 0; i < MAX_PLAYERS; i++)
9479     if (stored_player[i].active)
9480       found = TRUE;
9481
9482   if (!found)
9483     AllPlayersGone = TRUE;
9484
9485   ExitX = ZX = jx;
9486   ExitY = ZY = jy;
9487 }
9488
9489 /*
9490   =============================================================================
9491   checkDiagonalPushing()
9492   -----------------------------------------------------------------------------
9493   check if diagonal input device direction results in pushing of object
9494   (by checking if the alternative direction is walkable, diggable, ...)
9495   =============================================================================
9496 */
9497
9498 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9499                                     int x, int y, int real_dx, int real_dy)
9500 {
9501   int jx, jy, dx, dy, xx, yy;
9502
9503   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
9504     return TRUE;
9505
9506   /* diagonal direction: check alternative direction */
9507   jx = player->jx;
9508   jy = player->jy;
9509   dx = x - jx;
9510   dy = y - jy;
9511   xx = jx + (dx == 0 ? real_dx : 0);
9512   yy = jy + (dy == 0 ? real_dy : 0);
9513
9514   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9515 }
9516
9517 /*
9518   =============================================================================
9519   DigField()
9520   -----------------------------------------------------------------------------
9521   x, y:                 field next to player (non-diagonal) to try to dig to
9522   real_dx, real_dy:     direction as read from input device (can be diagonal)
9523   =============================================================================
9524 */
9525
9526 int DigField(struct PlayerInfo *player,
9527              int oldx, int oldy, int x, int y,
9528              int real_dx, int real_dy, int mode)
9529 {
9530   static int trigger_sides[4] =
9531   {
9532     CH_SIDE_RIGHT,      /* moving left  */
9533     CH_SIDE_LEFT,       /* moving right */
9534     CH_SIDE_BOTTOM,     /* moving up    */
9535     CH_SIDE_TOP,        /* moving down  */
9536   };
9537 #if 0
9538   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9539 #endif
9540   int jx = oldx, jy = oldy;
9541   int dx = x - jx, dy = y - jy;
9542   int nextx = x + dx, nexty = y + dy;
9543   int move_direction = (dx == -1 ? MV_LEFT :
9544                         dx == +1 ? MV_RIGHT :
9545                         dy == -1 ? MV_UP :
9546                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9547   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9548   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9549   int old_element = Feld[jx][jy];
9550   int element;
9551
9552   if (player->MovPos == 0)
9553   {
9554     player->is_digging = FALSE;
9555     player->is_collecting = FALSE;
9556   }
9557
9558   if (player->MovPos == 0)      /* last pushing move finished */
9559     player->is_pushing = FALSE;
9560
9561   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
9562   {
9563     player->is_switching = FALSE;
9564     player->push_delay = 0;
9565
9566     return MF_NO_ACTION;
9567   }
9568
9569   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9570     return MF_NO_ACTION;
9571
9572 #if 0
9573
9574 #if 0
9575   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9576 #else
9577   if (IS_TUBE(Feld[jx][jy]) ||
9578       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9579 #endif
9580   {
9581     int i = 0;
9582     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9583     int tube_leave_directions[][2] =
9584     {
9585       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9586       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
9587       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
9588       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
9589       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
9590       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
9591       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
9592       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
9593       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
9594       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
9595       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
9596       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9597     };
9598
9599     while (tube_leave_directions[i][0] != tube_element)
9600     {
9601       i++;
9602       if (tube_leave_directions[i][0] == -1)    /* should not happen */
9603         break;
9604     }
9605
9606     if (!(tube_leave_directions[i][1] & move_direction))
9607       return MF_NO_ACTION;      /* tube has no opening in this direction */
9608   }
9609
9610 #else
9611
9612   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9613     old_element = Back[jx][jy];
9614
9615 #endif
9616
9617   if (IS_WALKABLE(old_element) &&
9618       !(element_info[old_element].access_direction & move_direction))
9619     return MF_NO_ACTION;        /* field has no opening in this direction */
9620
9621   element = Feld[x][y];
9622
9623   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9624       game.engine_version >= VERSION_IDENT(2,2,0,0))
9625     return MF_NO_ACTION;
9626
9627   switch (element)
9628   {
9629     case EL_SP_PORT_LEFT:
9630     case EL_SP_PORT_RIGHT:
9631     case EL_SP_PORT_UP:
9632     case EL_SP_PORT_DOWN:
9633     case EL_SP_PORT_HORIZONTAL:
9634     case EL_SP_PORT_VERTICAL:
9635     case EL_SP_PORT_ANY:
9636     case EL_SP_GRAVITY_PORT_LEFT:
9637     case EL_SP_GRAVITY_PORT_RIGHT:
9638     case EL_SP_GRAVITY_PORT_UP:
9639     case EL_SP_GRAVITY_PORT_DOWN:
9640 #if 1
9641       if (!canEnterSupaplexPort(x, y, dx, dy))
9642         return MF_NO_ACTION;
9643 #else
9644       if ((dx == -1 &&
9645            element != EL_SP_PORT_LEFT &&
9646            element != EL_SP_GRAVITY_PORT_LEFT &&
9647            element != EL_SP_PORT_HORIZONTAL &&
9648            element != EL_SP_PORT_ANY) ||
9649           (dx == +1 &&
9650            element != EL_SP_PORT_RIGHT &&
9651            element != EL_SP_GRAVITY_PORT_RIGHT &&
9652            element != EL_SP_PORT_HORIZONTAL &&
9653            element != EL_SP_PORT_ANY) ||
9654           (dy == -1 &&
9655            element != EL_SP_PORT_UP &&
9656            element != EL_SP_GRAVITY_PORT_UP &&
9657            element != EL_SP_PORT_VERTICAL &&
9658            element != EL_SP_PORT_ANY) ||
9659           (dy == +1 &&
9660            element != EL_SP_PORT_DOWN &&
9661            element != EL_SP_GRAVITY_PORT_DOWN &&
9662            element != EL_SP_PORT_VERTICAL &&
9663            element != EL_SP_PORT_ANY) ||
9664           !IN_LEV_FIELD(nextx, nexty) ||
9665           !IS_FREE(nextx, nexty))
9666         return MF_NO_ACTION;
9667 #endif
9668
9669       if (element == EL_SP_GRAVITY_PORT_LEFT ||
9670           element == EL_SP_GRAVITY_PORT_RIGHT ||
9671           element == EL_SP_GRAVITY_PORT_UP ||
9672           element == EL_SP_GRAVITY_PORT_DOWN)
9673         game.gravity = !game.gravity;
9674
9675       /* automatically move to the next field with double speed */
9676       player->programmed_action = move_direction;
9677 #if 1
9678       if (player->move_delay_reset_counter == 0)
9679       {
9680         player->move_delay_reset_counter = 2;   /* two double speed steps */
9681
9682         DOUBLE_PLAYER_SPEED(player);
9683       }
9684 #else
9685       player->move_delay_reset_counter = 2;
9686
9687       DOUBLE_PLAYER_SPEED(player);
9688 #endif
9689
9690 #if 0
9691       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
9692 #endif
9693
9694       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9695       break;
9696
9697 #if 0
9698     case EL_TUBE_ANY:
9699     case EL_TUBE_VERTICAL:
9700     case EL_TUBE_HORIZONTAL:
9701     case EL_TUBE_VERTICAL_LEFT:
9702     case EL_TUBE_VERTICAL_RIGHT:
9703     case EL_TUBE_HORIZONTAL_UP:
9704     case EL_TUBE_HORIZONTAL_DOWN:
9705     case EL_TUBE_LEFT_UP:
9706     case EL_TUBE_LEFT_DOWN:
9707     case EL_TUBE_RIGHT_UP:
9708     case EL_TUBE_RIGHT_DOWN:
9709       {
9710         int i = 0;
9711         int tube_enter_directions[][2] =
9712         {
9713           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9714           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
9715           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
9716           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
9717           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
9718           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
9719           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
9720           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
9721           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
9722           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
9723           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
9724           { -1,                         MV_NO_MOVING                         }
9725         };
9726
9727         while (tube_enter_directions[i][0] != element)
9728         {
9729           i++;
9730           if (tube_enter_directions[i][0] == -1)        /* should not happen */
9731             break;
9732         }
9733
9734         if (!(tube_enter_directions[i][1] & move_direction))
9735           return MF_NO_ACTION;  /* tube has no opening in this direction */
9736
9737         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9738       }
9739       break;
9740 #endif
9741
9742     default:
9743
9744       if (IS_WALKABLE(element))
9745       {
9746         int sound_action = ACTION_WALKING;
9747
9748         if (!(element_info[element].access_direction & opposite_direction))
9749           return MF_NO_ACTION;  /* field not accessible from this direction */
9750
9751         if (element >= EL_GATE_1 && element <= EL_GATE_4)
9752         {
9753           if (!player->key[element - EL_GATE_1])
9754             return MF_NO_ACTION;
9755         }
9756         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9757         {
9758           if (!player->key[element - EL_GATE_1_GRAY])
9759             return MF_NO_ACTION;
9760         }
9761         else if (element == EL_EXIT_OPEN ||
9762                  element == EL_SP_EXIT_OPEN ||
9763                  element == EL_SP_EXIT_OPENING)
9764         {
9765           sound_action = ACTION_PASSING;        /* player is passing exit */
9766         }
9767         else if (element == EL_EMPTY)
9768         {
9769           sound_action = ACTION_MOVING;         /* nothing to walk on */
9770         }
9771
9772         /* play sound from background or player, whatever is available */
9773         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9774           PlayLevelSoundElementAction(x, y, element, sound_action);
9775         else
9776           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9777
9778         break;
9779       }
9780       else if (IS_PASSABLE(element))
9781       {
9782         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9783           return MF_NO_ACTION;
9784
9785         if (IS_CUSTOM_ELEMENT(element) &&
9786             !(element_info[element].access_direction & opposite_direction))
9787           return MF_NO_ACTION;  /* field not accessible from this direction */
9788
9789 #if 1
9790         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
9791           return MF_NO_ACTION;
9792 #endif
9793
9794         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9795         {
9796           if (!player->key[element - EL_EM_GATE_1])
9797             return MF_NO_ACTION;
9798         }
9799         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9800         {
9801           if (!player->key[element - EL_EM_GATE_1_GRAY])
9802             return MF_NO_ACTION;
9803         }
9804
9805         /* automatically move to the next field with double speed */
9806         player->programmed_action = move_direction;
9807 #if 1
9808         if (player->move_delay_reset_counter == 0)
9809         {
9810           player->move_delay_reset_counter = 2; /* two double speed steps */
9811
9812           DOUBLE_PLAYER_SPEED(player);
9813         }
9814 #else
9815         player->move_delay_reset_counter = 2;
9816
9817         DOUBLE_PLAYER_SPEED(player);
9818 #endif
9819
9820         PlayLevelSoundAction(x, y, ACTION_PASSING);
9821
9822         break;
9823       }
9824       else if (IS_DIGGABLE(element))
9825       {
9826         RemoveField(x, y);
9827
9828         if (mode != DF_SNAP)
9829         {
9830 #if 1
9831           GfxElement[x][y] = GFX_ELEMENT(element);
9832 #else
9833           GfxElement[x][y] =
9834             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9835 #endif
9836           player->is_digging = TRUE;
9837         }
9838
9839         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9840
9841         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9842                                           player->index_bit, CH_SIDE_ANY);
9843
9844 #if 1
9845         if (mode == DF_SNAP)
9846           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9847 #endif
9848
9849         break;
9850       }
9851       else if (IS_COLLECTIBLE(element))
9852       {
9853         RemoveField(x, y);
9854
9855         if (mode != DF_SNAP)
9856         {
9857           GfxElement[x][y] = element;
9858           player->is_collecting = TRUE;
9859         }
9860
9861         if (element == EL_SPEED_PILL)
9862           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9863         else if (element == EL_EXTRA_TIME && level.time > 0)
9864         {
9865           TimeLeft += 10;
9866           DrawGameValue_Time(TimeLeft);
9867         }
9868         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9869         {
9870           player->shield_normal_time_left += 10;
9871           if (element == EL_SHIELD_DEADLY)
9872             player->shield_deadly_time_left += 10;
9873         }
9874         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9875         {
9876           if (player->inventory_size < MAX_INVENTORY_SIZE)
9877             player->inventory_element[player->inventory_size++] = element;
9878
9879           DrawGameValue_Dynamite(local_player->inventory_size);
9880         }
9881         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9882         {
9883           player->dynabomb_count++;
9884           player->dynabombs_left++;
9885         }
9886         else if (element == EL_DYNABOMB_INCREASE_SIZE)
9887         {
9888           player->dynabomb_size++;
9889         }
9890         else if (element == EL_DYNABOMB_INCREASE_POWER)
9891         {
9892           player->dynabomb_xl = TRUE;
9893         }
9894         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9895                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9896         {
9897           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9898                         element - EL_KEY_1 : element - EL_EM_KEY_1);
9899
9900           player->key[key_nr] = TRUE;
9901
9902           DrawGameValue_Keys(player);
9903
9904           redraw_mask |= REDRAW_DOOR_1;
9905         }
9906         else if (IS_ENVELOPE(element))
9907         {
9908 #if 1
9909           player->show_envelope = element;
9910 #else
9911           ShowEnvelope(element - EL_ENVELOPE_1);
9912 #endif
9913         }
9914         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9915         {
9916           int i;
9917
9918           if (element_info[element].collect_count == 0)
9919             player->inventory_infinite_element = element;
9920           else
9921             for (i = 0; i < element_info[element].collect_count; i++)
9922               if (player->inventory_size < MAX_INVENTORY_SIZE)
9923                 player->inventory_element[player->inventory_size++] = element;
9924
9925           DrawGameValue_Dynamite(local_player->inventory_size);
9926         }
9927         else if (element_info[element].collect_count > 0)
9928         {
9929           local_player->gems_still_needed -=
9930             element_info[element].collect_count;
9931           if (local_player->gems_still_needed < 0)
9932             local_player->gems_still_needed = 0;
9933
9934           DrawGameValue_Emeralds(local_player->gems_still_needed);
9935         }
9936
9937         RaiseScoreElement(element);
9938         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9939
9940         CheckTriggeredElementChangePlayer(x, y, element,
9941                                           CE_OTHER_GETS_COLLECTED,
9942                                           player->index_bit, CH_SIDE_ANY);
9943
9944 #if 1
9945         if (mode == DF_SNAP)
9946           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9947 #endif
9948
9949         break;
9950       }
9951       else if (IS_PUSHABLE(element))
9952       {
9953         if (mode == DF_SNAP && element != EL_BD_ROCK)
9954           return MF_NO_ACTION;
9955
9956         if (CAN_FALL(element) && dy)
9957           return MF_NO_ACTION;
9958
9959         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9960             !(element == EL_SPRING && level.use_spring_bug))
9961           return MF_NO_ACTION;
9962
9963 #if 1
9964         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9965             ((move_direction & MV_VERTICAL &&
9966               ((element_info[element].move_pattern & MV_LEFT &&
9967                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9968                (element_info[element].move_pattern & MV_RIGHT &&
9969                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9970              (move_direction & MV_HORIZONTAL &&
9971               ((element_info[element].move_pattern & MV_UP &&
9972                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9973                (element_info[element].move_pattern & MV_DOWN &&
9974                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9975           return MF_NO_ACTION;
9976 #endif
9977
9978 #if 1
9979         /* do not push elements already moving away faster than player */
9980         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9981             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9982           return MF_NO_ACTION;
9983 #else
9984         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9985           return MF_NO_ACTION;
9986 #endif
9987
9988 #if 1
9989         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9990         {
9991           if (player->push_delay_value == -1)
9992             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9993         }
9994         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9995         {
9996           if (!player->is_pushing)
9997             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9998         }
9999
10000         /*
10001         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10002             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10003              !player_is_pushing))
10004           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10005         */
10006 #else
10007         if (!player->is_pushing &&
10008             game.engine_version >= VERSION_IDENT(2,2,0,7))
10009           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10010 #endif
10011
10012 #if 0
10013         printf("::: push delay: %ld [%d, %d] [%d]\n",
10014                player->push_delay_value, FrameCounter, game.engine_version,
10015                player->is_pushing);
10016 #endif
10017
10018         player->is_pushing = TRUE;
10019
10020         if (!(IN_LEV_FIELD(nextx, nexty) &&
10021               (IS_FREE(nextx, nexty) ||
10022                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10023                 IS_SB_ELEMENT(element)))))
10024           return MF_NO_ACTION;
10025
10026         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10027           return MF_NO_ACTION;
10028
10029         if (player->push_delay == 0)    /* new pushing; restart delay */
10030           player->push_delay = FrameCounter;
10031
10032         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10033             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10034             element != EL_SPRING && element != EL_BALLOON)
10035         {
10036           /* make sure that there is no move delay before next try to push */
10037           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10038             player->move_delay = INITIAL_MOVE_DELAY_OFF;
10039
10040           return MF_NO_ACTION;
10041         }
10042
10043 #if 0
10044         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10045 #endif
10046
10047         if (IS_SB_ELEMENT(element))
10048         {
10049           if (element == EL_SOKOBAN_FIELD_FULL)
10050           {
10051             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10052             local_player->sokobanfields_still_needed++;
10053           }
10054
10055           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10056           {
10057             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10058             local_player->sokobanfields_still_needed--;
10059           }
10060
10061           Feld[x][y] = EL_SOKOBAN_OBJECT;
10062
10063           if (Back[x][y] == Back[nextx][nexty])
10064             PlayLevelSoundAction(x, y, ACTION_PUSHING);
10065           else if (Back[x][y] != 0)
10066             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10067                                         ACTION_EMPTYING);
10068           else
10069             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10070                                         ACTION_FILLING);
10071
10072           if (local_player->sokobanfields_still_needed == 0 &&
10073               game.emulation == EMU_SOKOBAN)
10074           {
10075             player->LevelSolved = player->GameOver = TRUE;
10076             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10077           }
10078         }
10079         else
10080           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10081
10082         InitMovingField(x, y, move_direction);
10083         GfxAction[x][y] = ACTION_PUSHING;
10084
10085         if (mode == DF_SNAP)
10086           ContinueMoving(x, y);
10087         else
10088           MovPos[x][y] = (dx != 0 ? dx : dy);
10089
10090         Pushed[x][y] = TRUE;
10091         Pushed[nextx][nexty] = TRUE;
10092
10093         if (game.engine_version < VERSION_IDENT(2,2,0,7))
10094           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10095         else
10096           player->push_delay_value = -1;        /* get new value later */
10097
10098         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10099                                           player->index_bit, dig_side);
10100         CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10101                                  player->index_bit, dig_side);
10102
10103         break;
10104       }
10105       else if (IS_SWITCHABLE(element))
10106       {
10107         if (PLAYER_SWITCHING(player, x, y))
10108           return MF_ACTION;
10109
10110         player->is_switching = TRUE;
10111         player->switch_x = x;
10112         player->switch_y = y;
10113
10114         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10115
10116         if (element == EL_ROBOT_WHEEL)
10117         {
10118           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10119           ZX = x;
10120           ZY = y;
10121
10122           DrawLevelField(x, y);
10123         }
10124         else if (element == EL_SP_TERMINAL)
10125         {
10126           int xx, yy;
10127
10128           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10129           {
10130             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10131               Bang(xx, yy);
10132             else if (Feld[xx][yy] == EL_SP_TERMINAL)
10133               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10134           }
10135         }
10136         else if (IS_BELT_SWITCH(element))
10137         {
10138           ToggleBeltSwitch(x, y);
10139         }
10140         else if (element == EL_SWITCHGATE_SWITCH_UP ||
10141                  element == EL_SWITCHGATE_SWITCH_DOWN)
10142         {
10143           ToggleSwitchgateSwitch(x, y);
10144         }
10145         else if (element == EL_LIGHT_SWITCH ||
10146                  element == EL_LIGHT_SWITCH_ACTIVE)
10147         {
10148           ToggleLightSwitch(x, y);
10149
10150 #if 0
10151           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10152                          SND_LIGHT_SWITCH_ACTIVATING :
10153                          SND_LIGHT_SWITCH_DEACTIVATING);
10154 #endif
10155         }
10156         else if (element == EL_TIMEGATE_SWITCH)
10157         {
10158           ActivateTimegateSwitch(x, y);
10159         }
10160         else if (element == EL_BALLOON_SWITCH_LEFT ||
10161                  element == EL_BALLOON_SWITCH_RIGHT ||
10162                  element == EL_BALLOON_SWITCH_UP ||
10163                  element == EL_BALLOON_SWITCH_DOWN ||
10164                  element == EL_BALLOON_SWITCH_ANY)
10165         {
10166           if (element == EL_BALLOON_SWITCH_ANY)
10167             game.balloon_dir = move_direction;
10168           else
10169             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
10170                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10171                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
10172                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
10173                                 MV_NO_MOVING);
10174         }
10175         else if (element == EL_LAMP)
10176         {
10177           Feld[x][y] = EL_LAMP_ACTIVE;
10178           local_player->lights_still_needed--;
10179
10180           DrawLevelField(x, y);
10181         }
10182         else if (element == EL_TIME_ORB_FULL)
10183         {
10184           Feld[x][y] = EL_TIME_ORB_EMPTY;
10185           TimeLeft += 10;
10186           DrawGameValue_Time(TimeLeft);
10187
10188           DrawLevelField(x, y);
10189
10190 #if 0
10191           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10192 #endif
10193         }
10194
10195         return MF_ACTION;
10196       }
10197       else
10198       {
10199         if (!PLAYER_SWITCHING(player, x, y))
10200         {
10201           player->is_switching = TRUE;
10202           player->switch_x = x;
10203           player->switch_y = y;
10204
10205           CheckTriggeredElementChangePlayer(x, y, element,
10206                                             CE_OTHER_IS_SWITCHING,
10207                                             player->index_bit, dig_side);
10208           CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10209                                    player->index_bit, dig_side);
10210         }
10211
10212         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10213                                           player->index_bit, dig_side);
10214         CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10215                                  player->index_bit, dig_side);
10216       }
10217
10218       return MF_NO_ACTION;
10219   }
10220
10221   player->push_delay = 0;
10222
10223   if (Feld[x][y] != element)            /* really digged/collected something */
10224     player->is_collecting = !player->is_digging;
10225
10226   return MF_MOVING;
10227 }
10228
10229 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10230 {
10231   int jx = player->jx, jy = player->jy;
10232   int x = jx + dx, y = jy + dy;
10233   int snap_direction = (dx == -1 ? MV_LEFT :
10234                         dx == +1 ? MV_RIGHT :
10235                         dy == -1 ? MV_UP :
10236                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10237
10238 #if 0
10239   if (player->MovPos)
10240     return FALSE;
10241 #else
10242   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10243     return FALSE;
10244 #endif
10245
10246   if (!player->active || !IN_LEV_FIELD(x, y))
10247     return FALSE;
10248
10249   if (dx && dy)
10250     return FALSE;
10251
10252   if (!dx && !dy)
10253   {
10254     if (player->MovPos == 0)
10255       player->is_pushing = FALSE;
10256
10257     player->is_snapping = FALSE;
10258
10259     if (player->MovPos == 0)
10260     {
10261       player->is_moving = FALSE;
10262       player->is_digging = FALSE;
10263       player->is_collecting = FALSE;
10264     }
10265
10266     return FALSE;
10267   }
10268
10269   if (player->is_snapping)
10270     return FALSE;
10271
10272   player->MovDir = snap_direction;
10273
10274 #if 1
10275   if (player->MovPos == 0)
10276 #endif
10277   {
10278     player->is_moving = FALSE;
10279     player->is_digging = FALSE;
10280     player->is_collecting = FALSE;
10281   }
10282
10283   player->is_dropping = FALSE;
10284
10285   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10286     return FALSE;
10287
10288   player->is_snapping = TRUE;
10289
10290 #if 1
10291   if (player->MovPos == 0)
10292 #endif
10293   {
10294     player->is_moving = FALSE;
10295     player->is_digging = FALSE;
10296     player->is_collecting = FALSE;
10297   }
10298
10299   DrawLevelField(x, y);
10300   BackToFront();
10301
10302   return TRUE;
10303 }
10304
10305 boolean DropElement(struct PlayerInfo *player)
10306 {
10307   int jx = player->jx, jy = player->jy;
10308   int old_element = Feld[jx][jy];
10309   int new_element = (player->inventory_size > 0 ?
10310                      player->inventory_element[player->inventory_size - 1] :
10311                      player->inventory_infinite_element != EL_UNDEFINED ?
10312                      player->inventory_infinite_element :
10313                      player->dynabombs_left > 0 ?
10314                      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10315                      EL_UNDEFINED);
10316
10317   /* check if player is active, not moving and ready to drop */
10318   if (!player->active || player->MovPos || player->drop_delay > 0)
10319     return FALSE;
10320
10321   /* check if player has anything that can be dropped */
10322 #if 1
10323   if (new_element == EL_UNDEFINED)
10324     return FALSE;
10325 #else
10326   if (player->inventory_size == 0 &&
10327       player->inventory_infinite_element == EL_UNDEFINED &&
10328       player->dynabombs_left == 0)
10329     return FALSE;
10330 #endif
10331
10332   /* check if anything can be dropped at the current position */
10333   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10334     return FALSE;
10335
10336   /* collected custom elements can only be dropped on empty fields */
10337 #if 1
10338   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10339     return FALSE;
10340 #else
10341   if (player->inventory_size > 0 &&
10342       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10343       && old_element != EL_EMPTY)
10344     return FALSE;
10345 #endif
10346
10347   if (old_element != EL_EMPTY)
10348     Back[jx][jy] = old_element;         /* store old element on this field */
10349
10350   ResetGfxAnimation(jx, jy);
10351   ResetRandomAnimationValue(jx, jy);
10352
10353   if (player->inventory_size > 0 ||
10354       player->inventory_infinite_element != EL_UNDEFINED)
10355   {
10356     if (player->inventory_size > 0)
10357     {
10358       player->inventory_size--;
10359
10360 #if 0
10361       new_element = player->inventory_element[player->inventory_size];
10362 #endif
10363
10364       DrawGameValue_Dynamite(local_player->inventory_size);
10365
10366       if (new_element == EL_DYNAMITE)
10367         new_element = EL_DYNAMITE_ACTIVE;
10368       else if (new_element == EL_SP_DISK_RED)
10369         new_element = EL_SP_DISK_RED_ACTIVE;
10370     }
10371
10372     Feld[jx][jy] = new_element;
10373
10374     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10375       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10376
10377     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10378
10379 #if 1
10380     /* needed if previous element just changed to "empty" in the last frame */
10381     Changed[jx][jy] = 0;                /* allow another change */
10382 #endif
10383
10384     CheckTriggeredElementChangePlayer(jx, jy, new_element,
10385                                       CE_OTHER_GETS_DROPPED,
10386                                       player->index_bit, CH_SIDE_ANY);
10387     CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10388                              player->index_bit, CH_SIDE_ANY);
10389
10390     TestIfElementTouchesCustomElement(jx, jy);
10391   }
10392   else          /* player is dropping a dyna bomb */
10393   {
10394     player->dynabombs_left--;
10395
10396 #if 0
10397     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10398 #endif
10399
10400     Feld[jx][jy] = new_element;
10401
10402     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10403       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10404
10405     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10406   }
10407
10408
10409
10410 #if 1
10411
10412   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
10413   {
10414 #if 1
10415     InitField_WithBug1(jx, jy, FALSE);
10416 #else
10417     InitField(jx, jy, FALSE);
10418     if (CAN_MOVE(Feld[jx][jy]))
10419       InitMovDir(jx, jy);
10420 #endif
10421   }
10422
10423   new_element = Feld[jx][jy];
10424
10425   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10426       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10427   {
10428     int move_stepsize = element_info[new_element].move_stepsize;
10429     int direction, dx, dy, nextx, nexty;
10430
10431     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10432       MovDir[jx][jy] = player->MovDir;
10433
10434     direction = MovDir[jx][jy];
10435     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10436     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10437     nextx = jx + dx;
10438     nexty = jy + dy;
10439
10440     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10441     {
10442 #if 0
10443       WasJustMoving[jx][jy] = 3;
10444 #else
10445       InitMovingField(jx, jy, direction);
10446       ContinueMoving(jx, jy);
10447 #endif
10448     }
10449     else
10450     {
10451       Changed[jx][jy] = 0;              /* allow another change */
10452
10453 #if 1
10454       TestIfElementHitsCustomElement(jx, jy, direction);
10455 #else
10456       CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10457                              direction);
10458 #endif
10459     }
10460
10461     player->drop_delay = 2 * TILEX / move_stepsize + 1;
10462   }
10463
10464 #if 0
10465   player->drop_delay = 8 + 8 + 8;
10466 #endif
10467
10468 #endif
10469
10470   player->is_dropping = TRUE;
10471
10472
10473   return TRUE;
10474 }
10475
10476 /* ------------------------------------------------------------------------- */
10477 /* game sound playing functions                                              */
10478 /* ------------------------------------------------------------------------- */
10479
10480 static int *loop_sound_frame = NULL;
10481 static int *loop_sound_volume = NULL;
10482
10483 void InitPlayLevelSound()
10484 {
10485   int num_sounds = getSoundListSize();
10486
10487   checked_free(loop_sound_frame);
10488   checked_free(loop_sound_volume);
10489
10490   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
10491   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10492 }
10493
10494 static void PlayLevelSound(int x, int y, int nr)
10495 {
10496   int sx = SCREENX(x), sy = SCREENY(y);
10497   int volume, stereo_position;
10498   int max_distance = 8;
10499   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10500
10501   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10502       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10503     return;
10504
10505   if (!IN_LEV_FIELD(x, y) ||
10506       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10507       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10508     return;
10509
10510   volume = SOUND_MAX_VOLUME;
10511
10512   if (!IN_SCR_FIELD(sx, sy))
10513   {
10514     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10515     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10516
10517     volume -= volume * (dx > dy ? dx : dy) / max_distance;
10518   }
10519
10520   stereo_position = (SOUND_MAX_LEFT +
10521                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10522                      (SCR_FIELDX + 2 * max_distance));
10523
10524   if (IS_LOOP_SOUND(nr))
10525   {
10526     /* This assures that quieter loop sounds do not overwrite louder ones,
10527        while restarting sound volume comparison with each new game frame. */
10528
10529     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10530       return;
10531
10532     loop_sound_volume[nr] = volume;
10533     loop_sound_frame[nr] = FrameCounter;
10534   }
10535
10536   PlaySoundExt(nr, volume, stereo_position, type);
10537 }
10538
10539 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10540 {
10541   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10542                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
10543                  y < LEVELY(BY1) ? LEVELY(BY1) :
10544                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
10545                  sound_action);
10546 }
10547
10548 static void PlayLevelSoundAction(int x, int y, int action)
10549 {
10550   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10551 }
10552
10553 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10554 {
10555   int sound_effect = element_info[element].sound[action];
10556
10557   if (sound_effect != SND_UNDEFINED)
10558     PlayLevelSound(x, y, sound_effect);
10559 }
10560
10561 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10562                                               int action)
10563 {
10564   int sound_effect = element_info[element].sound[action];
10565
10566   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10567     PlayLevelSound(x, y, sound_effect);
10568 }
10569
10570 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10571 {
10572   int sound_effect = element_info[Feld[x][y]].sound[action];
10573
10574   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10575     PlayLevelSound(x, y, sound_effect);
10576 }
10577
10578 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10579 {
10580   int sound_effect = element_info[Feld[x][y]].sound[action];
10581
10582   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10583     StopSound(sound_effect);
10584 }
10585
10586 static void PlayLevelMusic()
10587 {
10588   if (levelset.music[level_nr] != MUS_UNDEFINED)
10589     PlayMusic(levelset.music[level_nr]);        /* from config file */
10590   else
10591     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
10592 }
10593
10594 void RaiseScore(int value)
10595 {
10596   local_player->score += value;
10597
10598   DrawGameValue_Score(local_player->score);
10599 }
10600
10601 void RaiseScoreElement(int element)
10602 {
10603   switch(element)
10604   {
10605     case EL_EMERALD:
10606     case EL_BD_DIAMOND:
10607     case EL_EMERALD_YELLOW:
10608     case EL_EMERALD_RED:
10609     case EL_EMERALD_PURPLE:
10610     case EL_SP_INFOTRON:
10611       RaiseScore(level.score[SC_EMERALD]);
10612       break;
10613     case EL_DIAMOND:
10614       RaiseScore(level.score[SC_DIAMOND]);
10615       break;
10616     case EL_CRYSTAL:
10617       RaiseScore(level.score[SC_CRYSTAL]);
10618       break;
10619     case EL_PEARL:
10620       RaiseScore(level.score[SC_PEARL]);
10621       break;
10622     case EL_BUG:
10623     case EL_BD_BUTTERFLY:
10624     case EL_SP_ELECTRON:
10625       RaiseScore(level.score[SC_BUG]);
10626       break;
10627     case EL_SPACESHIP:
10628     case EL_BD_FIREFLY:
10629     case EL_SP_SNIKSNAK:
10630       RaiseScore(level.score[SC_SPACESHIP]);
10631       break;
10632     case EL_YAMYAM:
10633     case EL_DARK_YAMYAM:
10634       RaiseScore(level.score[SC_YAMYAM]);
10635       break;
10636     case EL_ROBOT:
10637       RaiseScore(level.score[SC_ROBOT]);
10638       break;
10639     case EL_PACMAN:
10640       RaiseScore(level.score[SC_PACMAN]);
10641       break;
10642     case EL_NUT:
10643       RaiseScore(level.score[SC_NUT]);
10644       break;
10645     case EL_DYNAMITE:
10646     case EL_SP_DISK_RED:
10647     case EL_DYNABOMB_INCREASE_NUMBER:
10648     case EL_DYNABOMB_INCREASE_SIZE:
10649     case EL_DYNABOMB_INCREASE_POWER:
10650       RaiseScore(level.score[SC_DYNAMITE]);
10651       break;
10652     case EL_SHIELD_NORMAL:
10653     case EL_SHIELD_DEADLY:
10654       RaiseScore(level.score[SC_SHIELD]);
10655       break;
10656     case EL_EXTRA_TIME:
10657       RaiseScore(level.score[SC_TIME_BONUS]);
10658       break;
10659     case EL_KEY_1:
10660     case EL_KEY_2:
10661     case EL_KEY_3:
10662     case EL_KEY_4:
10663       RaiseScore(level.score[SC_KEY]);
10664       break;
10665     default:
10666       RaiseScore(element_info[element].collect_score);
10667       break;
10668   }
10669 }
10670
10671 void RequestQuitGame(boolean ask_if_really_quit)
10672 {
10673   if (AllPlayersGone ||
10674       !ask_if_really_quit ||
10675       level_editor_test_game ||
10676       Request("Do you really want to quit the game ?",
10677               REQ_ASK | REQ_STAY_CLOSED))
10678   {
10679 #if defined(PLATFORM_UNIX)
10680     if (options.network)
10681       SendToServer_StopPlaying();
10682     else
10683 #endif
10684     {
10685       game_status = GAME_MODE_MAIN;
10686       DrawMainMenu();
10687     }
10688   }
10689   else
10690   {
10691
10692 #if 1
10693     if (tape.playing && tape.index_search)
10694     {
10695       SetDrawDeactivationMask(REDRAW_NONE);
10696       audio.sound_deactivated = FALSE;
10697     }
10698 #endif
10699
10700     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10701
10702 #if 1
10703     if (tape.playing && tape.index_search)
10704     {
10705       SetDrawDeactivationMask(REDRAW_FIELD);
10706       audio.sound_deactivated = TRUE;
10707     }
10708 #endif
10709
10710   }
10711 }
10712
10713
10714 /* ---------- new game button stuff ---------------------------------------- */
10715
10716 /* graphic position values for game buttons */
10717 #define GAME_BUTTON_XSIZE       30
10718 #define GAME_BUTTON_YSIZE       30
10719 #define GAME_BUTTON_XPOS        5
10720 #define GAME_BUTTON_YPOS        215
10721 #define SOUND_BUTTON_XPOS       5
10722 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10723
10724 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10725 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10726 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10727 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10728 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10729 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10730
10731 static struct
10732 {
10733   int x, y;
10734   int gadget_id;
10735   char *infotext;
10736 } gamebutton_info[NUM_GAME_BUTTONS] =
10737 {
10738   {
10739     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
10740     GAME_CTRL_ID_STOP,
10741     "stop game"
10742   },
10743   {
10744     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
10745     GAME_CTRL_ID_PAUSE,
10746     "pause game"
10747   },
10748   {
10749     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
10750     GAME_CTRL_ID_PLAY,
10751     "play game"
10752   },
10753   {
10754     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
10755     SOUND_CTRL_ID_MUSIC,
10756     "background music on/off"
10757   },
10758   {
10759     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
10760     SOUND_CTRL_ID_LOOPS,
10761     "sound loops on/off"
10762   },
10763   {
10764     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
10765     SOUND_CTRL_ID_SIMPLE,
10766     "normal sounds on/off"
10767   }
10768 };
10769
10770 void CreateGameButtons()
10771 {
10772   int i;
10773
10774   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10775   {
10776     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10777     struct GadgetInfo *gi;
10778     int button_type;
10779     boolean checked;
10780     unsigned long event_mask;
10781     int gd_xoffset, gd_yoffset;
10782     int gd_x1, gd_x2, gd_y1, gd_y2;
10783     int id = i;
10784
10785     gd_xoffset = gamebutton_info[i].x;
10786     gd_yoffset = gamebutton_info[i].y;
10787     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10788     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10789
10790     if (id == GAME_CTRL_ID_STOP ||
10791         id == GAME_CTRL_ID_PAUSE ||
10792         id == GAME_CTRL_ID_PLAY)
10793     {
10794       button_type = GD_TYPE_NORMAL_BUTTON;
10795       checked = FALSE;
10796       event_mask = GD_EVENT_RELEASED;
10797       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10798       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10799     }
10800     else
10801     {
10802       button_type = GD_TYPE_CHECK_BUTTON;
10803       checked =
10804         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10805          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10806          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10807       event_mask = GD_EVENT_PRESSED;
10808       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
10809       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10810     }
10811
10812     gi = CreateGadget(GDI_CUSTOM_ID, id,
10813                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
10814                       GDI_X, DX + gd_xoffset,
10815                       GDI_Y, DY + gd_yoffset,
10816                       GDI_WIDTH, GAME_BUTTON_XSIZE,
10817                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
10818                       GDI_TYPE, button_type,
10819                       GDI_STATE, GD_BUTTON_UNPRESSED,
10820                       GDI_CHECKED, checked,
10821                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10822                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10823                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10824                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10825                       GDI_EVENT_MASK, event_mask,
10826                       GDI_CALLBACK_ACTION, HandleGameButtons,
10827                       GDI_END);
10828
10829     if (gi == NULL)
10830       Error(ERR_EXIT, "cannot create gadget");
10831
10832     game_gadget[id] = gi;
10833   }
10834 }
10835
10836 void FreeGameButtons()
10837 {
10838   int i;
10839
10840   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10841     FreeGadget(game_gadget[i]);
10842 }
10843
10844 static void MapGameButtons()
10845 {
10846   int i;
10847
10848   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10849     MapGadget(game_gadget[i]);
10850 }
10851
10852 void UnmapGameButtons()
10853 {
10854   int i;
10855
10856   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10857     UnmapGadget(game_gadget[i]);
10858 }
10859
10860 static void HandleGameButtons(struct GadgetInfo *gi)
10861 {
10862   int id = gi->custom_id;
10863
10864   if (game_status != GAME_MODE_PLAYING)
10865     return;
10866
10867   switch (id)
10868   {
10869     case GAME_CTRL_ID_STOP:
10870       RequestQuitGame(TRUE);
10871       break;
10872
10873     case GAME_CTRL_ID_PAUSE:
10874       if (options.network)
10875       {
10876 #if defined(PLATFORM_UNIX)
10877         if (tape.pausing)
10878           SendToServer_ContinuePlaying();
10879         else
10880           SendToServer_PausePlaying();
10881 #endif
10882       }
10883       else
10884         TapeTogglePause(TAPE_TOGGLE_MANUAL);
10885       break;
10886
10887     case GAME_CTRL_ID_PLAY:
10888       if (tape.pausing)
10889       {
10890 #if defined(PLATFORM_UNIX)
10891         if (options.network)
10892           SendToServer_ContinuePlaying();
10893         else
10894 #endif
10895         {
10896           tape.pausing = FALSE;
10897           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10898         }
10899       }
10900       break;
10901
10902     case SOUND_CTRL_ID_MUSIC:
10903       if (setup.sound_music)
10904       { 
10905         setup.sound_music = FALSE;
10906         FadeMusic();
10907       }
10908       else if (audio.music_available)
10909       { 
10910         setup.sound = setup.sound_music = TRUE;
10911
10912         SetAudioMode(setup.sound);
10913
10914         PlayLevelMusic();
10915       }
10916       break;
10917
10918     case SOUND_CTRL_ID_LOOPS:
10919       if (setup.sound_loops)
10920         setup.sound_loops = FALSE;
10921       else if (audio.loops_available)
10922       {
10923         setup.sound = setup.sound_loops = TRUE;
10924         SetAudioMode(setup.sound);
10925       }
10926       break;
10927
10928     case SOUND_CTRL_ID_SIMPLE:
10929       if (setup.sound_simple)
10930         setup.sound_simple = FALSE;
10931       else if (audio.sound_available)
10932       {
10933         setup.sound = setup.sound_simple = TRUE;
10934         SetAudioMode(setup.sound);
10935       }
10936       break;
10937
10938     default:
10939       break;
10940   }
10941 }