rnd-20040310-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 #if 0
3951   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3952            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3953   {
3954     TestIfBadThingTouchesOtherBadThing(x, y);
3955
3956     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3957       MovDir[x][y] = left_dir;
3958     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3959       MovDir[x][y] = right_dir;
3960
3961     if ((element == EL_SPACESHIP ||
3962          element == EL_SP_SNIKSNAK ||
3963          element == EL_SP_ELECTRON)
3964         && MovDir[x][y] != old_move_dir)
3965       MovDelay[x][y] = 9;
3966     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
3967       MovDelay[x][y] = 1;
3968   }
3969 #else
3970   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
3971   {
3972     TestIfBadThingTouchesOtherBadThing(x, y);
3973
3974     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3975       MovDir[x][y] = left_dir;
3976     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3977       MovDir[x][y] = right_dir;
3978
3979     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
3980       MovDelay[x][y] = 9;
3981     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
3982       MovDelay[x][y] = 1;
3983   }
3984   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3985   {
3986     TestIfBadThingTouchesOtherBadThing(x, y);
3987
3988     if (ELEMENT_CAN_ENTER_FIELD_GENERIC(element, left_x, left_y, 0))
3989       MovDir[x][y] = left_dir;
3990     else if (!ELEMENT_CAN_ENTER_FIELD_GENERIC(element, move_x, move_y, 0))
3991       MovDir[x][y] = right_dir;
3992
3993     if (MovDir[x][y] != old_move_dir)
3994       MovDelay[x][y] = 9;
3995   }
3996 #endif
3997   else if (element == EL_YAMYAM)
3998   {
3999     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
4000     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
4001
4002     if (can_turn_left && can_turn_right)
4003       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4004     else if (can_turn_left)
4005       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4006     else if (can_turn_right)
4007       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4008     else
4009       MovDir[x][y] = back_dir;
4010
4011     MovDelay[x][y] = 16 + 16 * RND(3);
4012   }
4013   else if (element == EL_DARK_YAMYAM)
4014   {
4015     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
4016     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
4017
4018     if (can_turn_left && can_turn_right)
4019       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4020     else if (can_turn_left)
4021       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4022     else if (can_turn_right)
4023       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4024     else
4025       MovDir[x][y] = back_dir;
4026
4027     MovDelay[x][y] = 16 + 16 * RND(3);
4028   }
4029   else if (element == EL_PACMAN)
4030   {
4031     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
4032     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
4033
4034     if (can_turn_left && can_turn_right)
4035       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4036     else if (can_turn_left)
4037       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4038     else if (can_turn_right)
4039       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4040     else
4041       MovDir[x][y] = back_dir;
4042
4043     MovDelay[x][y] = 6 + RND(40);
4044   }
4045   else if (element == EL_PIG)
4046   {
4047     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(left_x, left_y);
4048     boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
4049     boolean can_move_on    = PIG_CAN_ENTER_FIELD(move_x, move_y);
4050     boolean should_turn_left, should_turn_right, should_move_on;
4051     int rnd_value = 24;
4052     int rnd = RND(rnd_value);
4053
4054     should_turn_left = (can_turn_left &&
4055                         (!can_move_on ||
4056                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4057                                                    y + back_dy + left_dy)));
4058     should_turn_right = (can_turn_right &&
4059                          (!can_move_on ||
4060                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4061                                                     y + back_dy + right_dy)));
4062     should_move_on = (can_move_on &&
4063                       (!can_turn_left ||
4064                        !can_turn_right ||
4065                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4066                                                  y + move_dy + left_dy) ||
4067                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4068                                                  y + move_dy + right_dy)));
4069
4070     if (should_turn_left || should_turn_right || should_move_on)
4071     {
4072       if (should_turn_left && should_turn_right && should_move_on)
4073         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
4074                         rnd < 2 * rnd_value / 3 ? right_dir :
4075                         old_move_dir);
4076       else if (should_turn_left && should_turn_right)
4077         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4078       else if (should_turn_left && should_move_on)
4079         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4080       else if (should_turn_right && should_move_on)
4081         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4082       else if (should_turn_left)
4083         MovDir[x][y] = left_dir;
4084       else if (should_turn_right)
4085         MovDir[x][y] = right_dir;
4086       else if (should_move_on)
4087         MovDir[x][y] = old_move_dir;
4088     }
4089     else if (can_move_on && rnd > rnd_value / 8)
4090       MovDir[x][y] = old_move_dir;
4091     else if (can_turn_left && can_turn_right)
4092       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4093     else if (can_turn_left && rnd > rnd_value / 8)
4094       MovDir[x][y] = left_dir;
4095     else if (can_turn_right && rnd > rnd_value/8)
4096       MovDir[x][y] = right_dir;
4097     else
4098       MovDir[x][y] = back_dir;
4099
4100     xx = x + move_xy[MovDir[x][y]].x;
4101     yy = y + move_xy[MovDir[x][y]].y;
4102
4103     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4104       MovDir[x][y] = old_move_dir;
4105
4106     MovDelay[x][y] = 0;
4107   }
4108   else if (element == EL_DRAGON)
4109   {
4110     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
4111     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
4112     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
4113     int rnd_value = 24;
4114     int rnd = RND(rnd_value);
4115
4116 #if 0
4117     if (FrameCounter < 1 && x == 0 && y == 29)
4118       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4119 #endif
4120
4121     if (can_move_on && rnd > rnd_value / 8)
4122       MovDir[x][y] = old_move_dir;
4123     else if (can_turn_left && can_turn_right)
4124       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4125     else if (can_turn_left && rnd > rnd_value / 8)
4126       MovDir[x][y] = left_dir;
4127     else if (can_turn_right && rnd > rnd_value / 8)
4128       MovDir[x][y] = right_dir;
4129     else
4130       MovDir[x][y] = back_dir;
4131
4132     xx = x + move_xy[MovDir[x][y]].x;
4133     yy = y + move_xy[MovDir[x][y]].y;
4134
4135 #if 0
4136     if (FrameCounter < 1 && x == 0 && y == 29)
4137       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4138              xx, yy, Feld[xx][yy],
4139              FrameCounter);
4140 #endif
4141
4142 #if 1
4143     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4144       MovDir[x][y] = old_move_dir;
4145 #else
4146     if (!IS_FREE(xx, yy))
4147       MovDir[x][y] = old_move_dir;
4148 #endif
4149
4150 #if 0
4151     if (FrameCounter < 1 && x == 0 && y == 29)
4152       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4153 #endif
4154
4155     MovDelay[x][y] = 0;
4156   }
4157   else if (element == EL_MOLE)
4158   {
4159     boolean can_move_on =
4160       (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4161                             IS_AMOEBOID(Feld[move_x][move_y]) ||
4162                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4163     if (!can_move_on)
4164     {
4165       boolean can_turn_left =
4166         (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4167                               IS_AMOEBOID(Feld[left_x][left_y])));
4168
4169       boolean can_turn_right =
4170         (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4171                               IS_AMOEBOID(Feld[right_x][right_y])));
4172
4173       if (can_turn_left && can_turn_right)
4174         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4175       else if (can_turn_left)
4176         MovDir[x][y] = left_dir;
4177       else
4178         MovDir[x][y] = right_dir;
4179     }
4180
4181     if (MovDir[x][y] != old_move_dir)
4182       MovDelay[x][y] = 9;
4183   }
4184   else if (element == EL_BALLOON)
4185   {
4186     MovDir[x][y] = game.balloon_dir;
4187     MovDelay[x][y] = 0;
4188   }
4189   else if (element == EL_SPRING)
4190   {
4191 #if 0
4192     if (MovDir[x][y] & MV_HORIZONTAL &&
4193         !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4194       MovDir[x][y] = MV_NO_MOVING;
4195 #else
4196     if (MovDir[x][y] & MV_HORIZONTAL &&
4197         (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4198          SPRING_CAN_ENTER_FIELD(x, y + 1)))
4199       MovDir[x][y] = MV_NO_MOVING;
4200 #endif
4201
4202     MovDelay[x][y] = 0;
4203   }
4204   else if (element == EL_ROBOT ||
4205            element == EL_SATELLITE ||
4206            element == EL_PENGUIN)
4207   {
4208     int attr_x = -1, attr_y = -1;
4209
4210     if (AllPlayersGone)
4211     {
4212       attr_x = ExitX;
4213       attr_y = ExitY;
4214     }
4215     else
4216     {
4217       int i;
4218
4219       for (i = 0; i < MAX_PLAYERS; i++)
4220       {
4221         struct PlayerInfo *player = &stored_player[i];
4222         int jx = player->jx, jy = player->jy;
4223
4224         if (!player->active)
4225           continue;
4226
4227         if (attr_x == -1 ||
4228             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4229         {
4230           attr_x = jx;
4231           attr_y = jy;
4232         }
4233       }
4234     }
4235
4236     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4237     {
4238       attr_x = ZX;
4239       attr_y = ZY;
4240     }
4241
4242     if (element == EL_PENGUIN)
4243     {
4244       int i;
4245       static int xy[4][2] =
4246       {
4247         { 0, -1 },
4248         { -1, 0 },
4249         { +1, 0 },
4250         { 0, +1 }
4251       };
4252
4253       for (i = 0; i < NUM_DIRECTIONS; i++)
4254       {
4255         int ex = x + xy[i][0];
4256         int ey = y + xy[i][1];
4257
4258         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4259         {
4260           attr_x = ex;
4261           attr_y = ey;
4262           break;
4263         }
4264       }
4265     }
4266
4267     MovDir[x][y] = MV_NO_MOVING;
4268     if (attr_x < x)
4269       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4270     else if (attr_x > x)
4271       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4272     if (attr_y < y)
4273       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4274     else if (attr_y > y)
4275       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4276
4277     if (element == EL_ROBOT)
4278     {
4279       int newx, newy;
4280
4281       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4282         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4283       Moving2Blocked(x, y, &newx, &newy);
4284
4285       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4286         MovDelay[x][y] = 8 + 8 * !RND(3);
4287       else
4288         MovDelay[x][y] = 16;
4289     }
4290     else if (element == EL_PENGUIN)
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 (PENGUIN_CAN_ENTER_FIELD(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 (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4313           return;
4314
4315         MovDir[x][y] = old_move_dir;
4316         return;
4317       }
4318     }
4319     else        /* (element == EL_SATELLITE) */
4320     {
4321       int newx, newy;
4322
4323       MovDelay[x][y] = 1;
4324
4325       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4326       {
4327         boolean first_horiz = RND(2);
4328         int new_move_dir = MovDir[x][y];
4329
4330         MovDir[x][y] =
4331           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4332         Moving2Blocked(x, y, &newx, &newy);
4333
4334         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4335           return;
4336
4337         MovDir[x][y] =
4338           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4339         Moving2Blocked(x, y, &newx, &newy);
4340
4341         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4342           return;
4343
4344         MovDir[x][y] = old_move_dir;
4345         return;
4346       }
4347     }
4348   }
4349   else if (move_pattern == MV_TURNING_LEFT ||
4350            move_pattern == MV_TURNING_RIGHT ||
4351            move_pattern == MV_TURNING_LEFT_RIGHT ||
4352            move_pattern == MV_TURNING_RIGHT_LEFT ||
4353            move_pattern == MV_TURNING_RANDOM ||
4354            move_pattern == MV_ALL_DIRECTIONS)
4355   {
4356     boolean can_turn_left =
4357       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4358     boolean can_turn_right =
4359       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4360
4361     if (move_pattern == MV_TURNING_LEFT)
4362       MovDir[x][y] = left_dir;
4363     else if (move_pattern == MV_TURNING_RIGHT)
4364       MovDir[x][y] = right_dir;
4365     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4366       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4367     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4368       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4369     else if (move_pattern == MV_TURNING_RANDOM)
4370       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4371                       can_turn_right && !can_turn_left ? right_dir :
4372                       RND(2) ? left_dir : right_dir);
4373     else if (can_turn_left && can_turn_right)
4374       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4375     else if (can_turn_left)
4376       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4377     else if (can_turn_right)
4378       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4379     else
4380       MovDir[x][y] = back_dir;
4381
4382     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4383   }
4384   else if (move_pattern == MV_HORIZONTAL ||
4385            move_pattern == MV_VERTICAL)
4386   {
4387     if (move_pattern & old_move_dir)
4388       MovDir[x][y] = back_dir;
4389     else if (move_pattern == MV_HORIZONTAL)
4390       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4391     else if (move_pattern == MV_VERTICAL)
4392       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4393
4394     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4395   }
4396   else if (move_pattern & MV_ANY_DIRECTION)
4397   {
4398     MovDir[x][y] = move_pattern;
4399     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4400   }
4401   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4402   {
4403     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4404       MovDir[x][y] = left_dir;
4405     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4406       MovDir[x][y] = right_dir;
4407
4408     if (MovDir[x][y] != old_move_dir)
4409       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4410   }
4411   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4412   {
4413     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4414       MovDir[x][y] = right_dir;
4415     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4416       MovDir[x][y] = left_dir;
4417
4418     if (MovDir[x][y] != old_move_dir)
4419       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4420   }
4421   else if (move_pattern == MV_TOWARDS_PLAYER ||
4422            move_pattern == MV_AWAY_FROM_PLAYER)
4423   {
4424     int attr_x = -1, attr_y = -1;
4425     int newx, newy;
4426     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4427
4428     if (AllPlayersGone)
4429     {
4430       attr_x = ExitX;
4431       attr_y = ExitY;
4432     }
4433     else
4434     {
4435       int i;
4436
4437       for (i = 0; i < MAX_PLAYERS; i++)
4438       {
4439         struct PlayerInfo *player = &stored_player[i];
4440         int jx = player->jx, jy = player->jy;
4441
4442         if (!player->active)
4443           continue;
4444
4445         if (attr_x == -1 ||
4446             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4447         {
4448           attr_x = jx;
4449           attr_y = jy;
4450         }
4451       }
4452     }
4453
4454     MovDir[x][y] = MV_NO_MOVING;
4455     if (attr_x < x)
4456       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4457     else if (attr_x > x)
4458       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4459     if (attr_y < y)
4460       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4461     else if (attr_y > y)
4462       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4463
4464     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4465
4466     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4467     {
4468       boolean first_horiz = RND(2);
4469       int new_move_dir = MovDir[x][y];
4470
4471       MovDir[x][y] =
4472         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4473       Moving2Blocked(x, y, &newx, &newy);
4474
4475       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4476         return;
4477
4478       MovDir[x][y] =
4479         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4480       Moving2Blocked(x, y, &newx, &newy);
4481
4482       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4483         return;
4484
4485       MovDir[x][y] = old_move_dir;
4486     }
4487   }
4488   else if (move_pattern == MV_WHEN_PUSHED ||
4489            move_pattern == MV_WHEN_DROPPED)
4490   {
4491     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4492       MovDir[x][y] = MV_NO_MOVING;
4493
4494     MovDelay[x][y] = 0;
4495   }
4496   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4497   {
4498     static int test_xy[7][2] =
4499     {
4500       { 0, -1 },
4501       { -1, 0 },
4502       { +1, 0 },
4503       { 0, +1 },
4504       { 0, -1 },
4505       { -1, 0 },
4506       { +1, 0 },
4507     };
4508     static int test_dir[7] =
4509     {
4510       MV_UP,
4511       MV_LEFT,
4512       MV_RIGHT,
4513       MV_DOWN,
4514       MV_UP,
4515       MV_LEFT,
4516       MV_RIGHT,
4517     };
4518     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4519     int move_preference = -1000000;     /* start with very low preference */
4520     int new_move_dir = MV_NO_MOVING;
4521     int start_test = RND(4);
4522     int i;
4523
4524     for (i = 0; i < NUM_DIRECTIONS; i++)
4525     {
4526       int move_dir = test_dir[start_test + i];
4527       int move_dir_preference;
4528
4529       xx = x + test_xy[start_test + i][0];
4530       yy = y + test_xy[start_test + i][1];
4531
4532       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4533           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4534       {
4535         new_move_dir = move_dir;
4536
4537         break;
4538       }
4539
4540       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4541         continue;
4542
4543       move_dir_preference = -1 * RunnerVisit[xx][yy];
4544       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4545         move_dir_preference = PlayerVisit[xx][yy];
4546
4547       if (move_dir_preference > move_preference)
4548       {
4549         /* prefer field that has not been visited for the longest time */
4550         move_preference = move_dir_preference;
4551         new_move_dir = move_dir;
4552       }
4553       else if (move_dir_preference == move_preference &&
4554                move_dir == old_move_dir)
4555       {
4556         /* prefer last direction when all directions are preferred equally */
4557         move_preference = move_dir_preference;
4558         new_move_dir = move_dir;
4559       }
4560     }
4561
4562     MovDir[x][y] = new_move_dir;
4563     if (old_move_dir != new_move_dir)
4564       MovDelay[x][y] = 9;
4565   }
4566 }
4567
4568 static void TurnRound(int x, int y)
4569 {
4570   int direction = MovDir[x][y];
4571
4572 #if 0
4573   GfxDir[x][y] = MovDir[x][y];
4574 #endif
4575
4576   TurnRoundExt(x, y);
4577
4578 #if 1
4579   GfxDir[x][y] = MovDir[x][y];
4580 #endif
4581
4582   if (direction != MovDir[x][y])
4583     GfxFrame[x][y] = 0;
4584
4585 #if 1
4586   if (MovDelay[x][y])
4587     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4588 #else
4589   if (MovDelay[x][y])
4590     GfxAction[x][y] = ACTION_WAITING;
4591 #endif
4592 }
4593
4594 static boolean JustBeingPushed(int x, int y)
4595 {
4596   int i;
4597
4598   for (i = 0; i < MAX_PLAYERS; i++)
4599   {
4600     struct PlayerInfo *player = &stored_player[i];
4601
4602     if (player->active && player->is_pushing && player->MovPos)
4603     {
4604       int next_jx = player->jx + (player->jx - player->last_jx);
4605       int next_jy = player->jy + (player->jy - player->last_jy);
4606
4607       if (x == next_jx && y == next_jy)
4608         return TRUE;
4609     }
4610   }
4611
4612   return FALSE;
4613 }
4614
4615 void StartMoving(int x, int y)
4616 {
4617 #if 0
4618   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4619 #endif
4620   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4621   int element = Feld[x][y];
4622
4623   if (Stop[x][y])
4624     return;
4625
4626 #if 1
4627   if (MovDelay[x][y] == 0)
4628     GfxAction[x][y] = ACTION_DEFAULT;
4629 #else
4630   /* !!! this should be handled more generic (not only for mole) !!! */
4631   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4632     GfxAction[x][y] = ACTION_DEFAULT;
4633 #endif
4634
4635   if (CAN_FALL(element) && y < lev_fieldy - 1)
4636   {
4637     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
4638         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4639       if (JustBeingPushed(x, y))
4640         return;
4641
4642     if (element == EL_QUICKSAND_FULL)
4643     {
4644       if (IS_FREE(x, y + 1))
4645       {
4646         InitMovingField(x, y, MV_DOWN);
4647         started_moving = TRUE;
4648
4649         Feld[x][y] = EL_QUICKSAND_EMPTYING;
4650         Store[x][y] = EL_ROCK;
4651 #if 1
4652         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4653 #else
4654         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4655 #endif
4656       }
4657       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4658       {
4659         if (!MovDelay[x][y])
4660           MovDelay[x][y] = TILEY + 1;
4661
4662         if (MovDelay[x][y])
4663         {
4664           MovDelay[x][y]--;
4665           if (MovDelay[x][y])
4666             return;
4667         }
4668
4669         Feld[x][y] = EL_QUICKSAND_EMPTY;
4670         Feld[x][y + 1] = EL_QUICKSAND_FULL;
4671         Store[x][y + 1] = Store[x][y];
4672         Store[x][y] = 0;
4673 #if 1
4674         PlayLevelSoundAction(x, y, ACTION_FILLING);
4675 #else
4676         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4677 #endif
4678       }
4679     }
4680     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4681              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4682     {
4683       InitMovingField(x, y, MV_DOWN);
4684       started_moving = TRUE;
4685
4686       Feld[x][y] = EL_QUICKSAND_FILLING;
4687       Store[x][y] = element;
4688 #if 1
4689       PlayLevelSoundAction(x, y, ACTION_FILLING);
4690 #else
4691       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4692 #endif
4693     }
4694     else if (element == EL_MAGIC_WALL_FULL)
4695     {
4696       if (IS_FREE(x, y + 1))
4697       {
4698         InitMovingField(x, y, MV_DOWN);
4699         started_moving = TRUE;
4700
4701         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4702         Store[x][y] = EL_CHANGED(Store[x][y]);
4703       }
4704       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4705       {
4706         if (!MovDelay[x][y])
4707           MovDelay[x][y] = TILEY/4 + 1;
4708
4709         if (MovDelay[x][y])
4710         {
4711           MovDelay[x][y]--;
4712           if (MovDelay[x][y])
4713             return;
4714         }
4715
4716         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4717         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4718         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4719         Store[x][y] = 0;
4720       }
4721     }
4722     else if (element == EL_BD_MAGIC_WALL_FULL)
4723     {
4724       if (IS_FREE(x, y + 1))
4725       {
4726         InitMovingField(x, y, MV_DOWN);
4727         started_moving = TRUE;
4728
4729         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4730         Store[x][y] = EL_CHANGED2(Store[x][y]);
4731       }
4732       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4733       {
4734         if (!MovDelay[x][y])
4735           MovDelay[x][y] = TILEY/4 + 1;
4736
4737         if (MovDelay[x][y])
4738         {
4739           MovDelay[x][y]--;
4740           if (MovDelay[x][y])
4741             return;
4742         }
4743
4744         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4745         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4746         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4747         Store[x][y] = 0;
4748       }
4749     }
4750     else if (CAN_PASS_MAGIC_WALL(element) &&
4751              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4752               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4753     {
4754       InitMovingField(x, y, MV_DOWN);
4755       started_moving = TRUE;
4756
4757       Feld[x][y] =
4758         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4759          EL_BD_MAGIC_WALL_FILLING);
4760       Store[x][y] = element;
4761     }
4762 #if 0
4763     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4764 #else
4765     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4766 #endif
4767     {
4768       SplashAcid(x, y + 1);
4769
4770       InitMovingField(x, y, MV_DOWN);
4771       started_moving = TRUE;
4772
4773       Store[x][y] = EL_ACID;
4774 #if 0
4775       /* !!! TEST !!! better use "_FALLING" etc. !!! */
4776       GfxAction[x][y + 1] = ACTION_ACTIVE;
4777 #endif
4778     }
4779 #if 1
4780     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4781               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4782               (Feld[x][y + 1] == EL_BLOCKED)) ||
4783              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4784               CAN_SMASH(element) && WasJustFalling[x][y] &&
4785               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4786
4787 #else
4788 #if 1
4789     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4790              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4791              WasJustMoving[x][y] && !Pushed[x][y + 1])
4792 #else
4793     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4794              WasJustMoving[x][y])
4795 #endif
4796 #endif
4797
4798     {
4799       /* this is needed for a special case not covered by calling "Impact()"
4800          from "ContinueMoving()": if an element moves to a tile directly below
4801          another element which was just falling on that tile (which was empty
4802          in the previous frame), the falling element above would just stop
4803          instead of smashing the element below (in previous version, the above
4804          element was just checked for "moving" instead of "falling", resulting
4805          in incorrect smashes caused by horizontal movement of the above
4806          element; also, the case of the player being the element to smash was
4807          simply not covered here... :-/ ) */
4808
4809 #if 0
4810       WasJustMoving[x][y] = 0;
4811       WasJustFalling[x][y] = 0;
4812 #endif
4813
4814       Impact(x, y);
4815     }
4816     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4817     {
4818       if (MovDir[x][y] == MV_NO_MOVING)
4819       {
4820         InitMovingField(x, y, MV_DOWN);
4821         started_moving = TRUE;
4822       }
4823     }
4824     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4825     {
4826       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4827         MovDir[x][y] = MV_DOWN;
4828
4829       InitMovingField(x, y, MV_DOWN);
4830       started_moving = TRUE;
4831     }
4832     else if (element == EL_AMOEBA_DROP)
4833     {
4834       Feld[x][y] = EL_AMOEBA_GROWING;
4835       Store[x][y] = EL_AMOEBA_WET;
4836     }
4837     /* Store[x][y + 1] must be zero, because:
4838        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4839     */
4840 #if 0
4841 #if OLD_GAME_BEHAVIOUR
4842     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4843 #else
4844     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4845              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4846              element != EL_DX_SUPABOMB)
4847 #endif
4848 #else
4849     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4850               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4851              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4852              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4853 #endif
4854     {
4855       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
4856                                 (IS_FREE(x - 1, y + 1) ||
4857                                  Feld[x - 1][y + 1] == EL_ACID));
4858       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4859                                 (IS_FREE(x + 1, y + 1) ||
4860                                  Feld[x + 1][y + 1] == EL_ACID));
4861       boolean can_fall_any  = (can_fall_left || can_fall_right);
4862       boolean can_fall_both = (can_fall_left && can_fall_right);
4863
4864       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4865       {
4866         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4867
4868         if (slippery_type == SLIPPERY_ONLY_LEFT)
4869           can_fall_right = FALSE;
4870         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4871           can_fall_left = FALSE;
4872         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4873           can_fall_right = FALSE;
4874         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4875           can_fall_left = FALSE;
4876
4877         can_fall_any  = (can_fall_left || can_fall_right);
4878         can_fall_both = (can_fall_left && can_fall_right);
4879       }
4880
4881       if (can_fall_any)
4882       {
4883         if (can_fall_both &&
4884             (game.emulation != EMU_BOULDERDASH &&
4885              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4886           can_fall_left = !(can_fall_right = RND(2));
4887
4888         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4889         started_moving = TRUE;
4890       }
4891     }
4892 #if 0
4893     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4894 #else
4895     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4896 #endif
4897     {
4898       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4899       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4900       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4901       int belt_dir = game.belt_dir[belt_nr];
4902
4903       if ((belt_dir == MV_LEFT  && left_is_free) ||
4904           (belt_dir == MV_RIGHT && right_is_free))
4905       {
4906 #if 1
4907         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4908 #endif
4909
4910         InitMovingField(x, y, belt_dir);
4911         started_moving = TRUE;
4912
4913 #if 1
4914         Pushed[x][y] = TRUE;
4915         Pushed[nextx][y] = TRUE;
4916 #endif
4917
4918         GfxAction[x][y] = ACTION_DEFAULT;
4919       }
4920       else
4921       {
4922         MovDir[x][y] = 0;       /* if element was moving, stop it */
4923       }
4924     }
4925   }
4926
4927   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4928   if (CAN_MOVE(element) && !started_moving)
4929   {
4930     int move_pattern = element_info[element].move_pattern;
4931     int newx, newy;
4932
4933     Moving2Blocked(x, y, &newx, &newy);
4934
4935 #if 1
4936     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4937       return;
4938 #else
4939     if ((element == EL_SATELLITE ||
4940          element == EL_BALLOON ||
4941          element == EL_SPRING)
4942         && JustBeingPushed(x, y))
4943       return;
4944 #endif
4945
4946 #if 1
4947     if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4948         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4949         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4950     {
4951 #if 0
4952       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4953              element, element_info[element].token_name,
4954              WasJustMoving[x][y],
4955              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4956              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4957              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4958              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4959 #endif
4960
4961 #if 1
4962       WasJustMoving[x][y] = 0;
4963 #endif
4964
4965       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4966
4967 #if 0
4968       if (Feld[x][y] != element)        /* element has changed */
4969       {
4970         element = Feld[x][y];
4971         move_pattern = element_info[element].move_pattern;
4972
4973         if (!CAN_MOVE(element))
4974           return;
4975       }
4976 #else
4977       if (Feld[x][y] != element)        /* element has changed */
4978         return;
4979 #endif
4980     }
4981 #endif
4982
4983 #if 0
4984 #if 0
4985     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4986       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
4987 #else
4988     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4989     {
4990       Moving2Blocked(x, y, &newx, &newy);
4991       if (Feld[newx][newy] == EL_BLOCKED)
4992         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
4993     }
4994 #endif
4995 #endif
4996
4997 #if 0
4998     if (FrameCounter < 1 && x == 0 && y == 29)
4999       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5000 #endif
5001
5002     if (!MovDelay[x][y])        /* start new movement phase */
5003     {
5004       /* all objects that can change their move direction after each step
5005          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5006
5007       if (element != EL_YAMYAM &&
5008           element != EL_DARK_YAMYAM &&
5009           element != EL_PACMAN &&
5010           !(move_pattern & MV_ANY_DIRECTION) &&
5011           move_pattern != MV_TURNING_LEFT &&
5012           move_pattern != MV_TURNING_RIGHT &&
5013           move_pattern != MV_TURNING_LEFT_RIGHT &&
5014           move_pattern != MV_TURNING_RIGHT_LEFT &&
5015           move_pattern != MV_TURNING_RANDOM)
5016       {
5017         TurnRound(x, y);
5018
5019 #if 0
5020         if (FrameCounter < 1 && x == 0 && y == 29)
5021           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5022 #endif
5023
5024         if (MovDelay[x][y] && (element == EL_BUG ||
5025                                element == EL_SPACESHIP ||
5026                                element == EL_SP_SNIKSNAK ||
5027                                element == EL_SP_ELECTRON ||
5028                                element == EL_MOLE))
5029           DrawLevelField(x, y);
5030       }
5031     }
5032
5033     if (MovDelay[x][y])         /* wait some time before next movement */
5034     {
5035       MovDelay[x][y]--;
5036
5037 #if 0
5038       if (element == EL_YAMYAM)
5039       {
5040         printf("::: %d\n",
5041                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5042         DrawLevelElementAnimation(x, y, element);
5043       }
5044 #endif
5045
5046       if (MovDelay[x][y])       /* element still has to wait some time */
5047       {
5048 #if 0
5049         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5050         ResetGfxAnimation(x, y);
5051 #endif
5052
5053 #if 0
5054         if (GfxAction[x][y] != ACTION_WAITING)
5055           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5056
5057         GfxAction[x][y] = ACTION_WAITING;
5058 #endif
5059       }
5060
5061       if (element == EL_ROBOT ||
5062 #if 0
5063           element == EL_PACMAN ||
5064 #endif
5065           element == EL_YAMYAM ||
5066           element == EL_DARK_YAMYAM)
5067       {
5068 #if 0
5069         DrawLevelElementAnimation(x, y, element);
5070 #else
5071         DrawLevelElementAnimationIfNeeded(x, y, element);
5072 #endif
5073         PlayLevelSoundAction(x, y, ACTION_WAITING);
5074       }
5075       else if (element == EL_SP_ELECTRON)
5076         DrawLevelElementAnimationIfNeeded(x, y, element);
5077       else if (element == EL_DRAGON)
5078       {
5079         int i;
5080         int dir = MovDir[x][y];
5081         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5082         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5083         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
5084                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
5085                        dir == MV_UP     ? IMG_FLAMES_1_UP :
5086                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5087         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5088
5089 #if 0
5090         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5091 #endif
5092
5093         GfxAction[x][y] = ACTION_ATTACKING;
5094
5095         if (IS_PLAYER(x, y))
5096           DrawPlayerField(x, y);
5097         else
5098           DrawLevelField(x, y);
5099
5100         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5101
5102         for (i = 1; i <= 3; i++)
5103         {
5104           int xx = x + i * dx;
5105           int yy = y + i * dy;
5106           int sx = SCREENX(xx);
5107           int sy = SCREENY(yy);
5108           int flame_graphic = graphic + (i - 1);
5109
5110           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5111             break;
5112
5113           if (MovDelay[x][y])
5114           {
5115             int flamed = MovingOrBlocked2Element(xx, yy);
5116
5117             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5118               Bang(xx, yy);
5119             else
5120               RemoveMovingField(xx, yy);
5121
5122             Feld[xx][yy] = EL_FLAMES;
5123             if (IN_SCR_FIELD(sx, sy))
5124             {
5125               DrawLevelFieldCrumbledSand(xx, yy);
5126               DrawGraphic(sx, sy, flame_graphic, frame);
5127             }
5128           }
5129           else
5130           {
5131             if (Feld[xx][yy] == EL_FLAMES)
5132               Feld[xx][yy] = EL_EMPTY;
5133             DrawLevelField(xx, yy);
5134           }
5135         }
5136       }
5137
5138       if (MovDelay[x][y])       /* element still has to wait some time */
5139       {
5140         PlayLevelSoundAction(x, y, ACTION_WAITING);
5141
5142         return;
5143       }
5144
5145 #if 0
5146       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5147          for all other elements GfxAction will be set by InitMovingField() */
5148       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5149         GfxAction[x][y] = ACTION_MOVING;
5150 #endif
5151     }
5152
5153     /* now make next step */
5154
5155     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5156
5157     if (DONT_COLLIDE_WITH(element) &&
5158         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5159         !PLAYER_ENEMY_PROTECTED(newx, newy))
5160     {
5161 #if 1
5162       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5163
5164       return;
5165 #else
5166       /* player killed by element which is deadly when colliding with */
5167       MovDir[x][y] = 0;
5168       KillHero(PLAYERINFO(newx, newy));
5169       return;
5170 #endif
5171
5172     }
5173 #if 1
5174 #if 1
5175     else if (CAN_MOVE_INTO_ACID(element) &&
5176              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5177              (MovDir[x][y] == MV_DOWN ||
5178               game.engine_version > VERSION_IDENT(3,0,8,0)))
5179 #else
5180     else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5181              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5182 #endif
5183 #else
5184
5185     else if ((element == EL_PENGUIN ||
5186               element == EL_ROBOT ||
5187               element == EL_SATELLITE ||
5188               element == EL_BALLOON ||
5189               IS_CUSTOM_ELEMENT(element)) &&
5190              IN_LEV_FIELD(newx, newy) &&
5191              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5192 #endif
5193     {
5194       SplashAcid(newx, newy);
5195       Store[x][y] = EL_ACID;
5196     }
5197     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5198     {
5199       if (Feld[newx][newy] == EL_EXIT_OPEN)
5200       {
5201 #if 1
5202         RemoveField(x, y);
5203         DrawLevelField(x, y);
5204 #else
5205         Feld[x][y] = EL_EMPTY;
5206         DrawLevelField(x, y);
5207 #endif
5208
5209         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5210         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5211           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5212
5213         local_player->friends_still_needed--;
5214         if (!local_player->friends_still_needed &&
5215             !local_player->GameOver && AllPlayersGone)
5216           local_player->LevelSolved = local_player->GameOver = TRUE;
5217
5218         return;
5219       }
5220       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5221       {
5222         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5223           DrawLevelField(newx, newy);
5224         else
5225           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5226       }
5227       else if (!IS_FREE(newx, newy))
5228       {
5229         GfxAction[x][y] = ACTION_WAITING;
5230
5231         if (IS_PLAYER(x, y))
5232           DrawPlayerField(x, y);
5233         else
5234           DrawLevelField(x, y);
5235
5236         return;
5237       }
5238     }
5239     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5240     {
5241       if (IS_FOOD_PIG(Feld[newx][newy]))
5242       {
5243         if (IS_MOVING(newx, newy))
5244           RemoveMovingField(newx, newy);
5245         else
5246         {
5247           Feld[newx][newy] = EL_EMPTY;
5248           DrawLevelField(newx, newy);
5249         }
5250
5251         PlayLevelSound(x, y, SND_PIG_DIGGING);
5252       }
5253       else if (!IS_FREE(newx, newy))
5254       {
5255         if (IS_PLAYER(x, y))
5256           DrawPlayerField(x, y);
5257         else
5258           DrawLevelField(x, y);
5259
5260         return;
5261       }
5262     }
5263
5264 #if 1
5265
5266     /*
5267     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5268     */
5269
5270     else if (IS_CUSTOM_ELEMENT(element) &&
5271              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5272
5273 #if 0
5274  &&
5275              !IS_FREE(newx, newy)
5276 #endif
5277
5278 )
5279     {
5280       int new_element = Feld[newx][newy];
5281
5282 #if 0
5283       printf("::: '%s' digs '%s' [%d]\n",
5284              element_info[element].token_name,
5285              element_info[Feld[newx][newy]].token_name,
5286              StorePlayer[newx][newy]);
5287 #endif
5288
5289       if (!IS_FREE(newx, newy))
5290       {
5291         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5292                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5293                       ACTION_BREAKING);
5294
5295         /* no element can dig solid indestructible elements */
5296         if (IS_INDESTRUCTIBLE(new_element) &&
5297             !IS_DIGGABLE(new_element) &&
5298             !IS_COLLECTIBLE(new_element))
5299           return;
5300
5301         if (AmoebaNr[newx][newy] &&
5302             (new_element == EL_AMOEBA_FULL ||
5303              new_element == EL_BD_AMOEBA ||
5304              new_element == EL_AMOEBA_GROWING))
5305         {
5306           AmoebaCnt[AmoebaNr[newx][newy]]--;
5307           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5308         }
5309
5310         if (IS_MOVING(newx, newy))
5311           RemoveMovingField(newx, newy);
5312         else
5313         {
5314           RemoveField(newx, newy);
5315           DrawLevelField(newx, newy);
5316         }
5317
5318         PlayLevelSoundAction(x, y, action);
5319       }
5320
5321       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5322         element_info[element].can_leave_element = TRUE;
5323
5324       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5325       {
5326         RunnerVisit[x][y] = FrameCounter;
5327         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5328       }
5329     }
5330
5331 #endif
5332
5333     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5334     {
5335       if (!IS_FREE(newx, newy))
5336       {
5337         if (IS_PLAYER(x, y))
5338           DrawPlayerField(x, y);
5339         else
5340           DrawLevelField(x, y);
5341
5342         return;
5343       }
5344       else
5345       {
5346         boolean wanna_flame = !RND(10);
5347         int dx = newx - x, dy = newy - y;
5348         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5349         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5350         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5351                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5352         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5353                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5354
5355         if ((wanna_flame ||
5356              IS_CLASSIC_ENEMY(element1) ||
5357              IS_CLASSIC_ENEMY(element2)) &&
5358             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5359             element1 != EL_FLAMES && element2 != EL_FLAMES)
5360         {
5361 #if 1
5362           ResetGfxAnimation(x, y);
5363           GfxAction[x][y] = ACTION_ATTACKING;
5364 #endif
5365
5366           if (IS_PLAYER(x, y))
5367             DrawPlayerField(x, y);
5368           else
5369             DrawLevelField(x, y);
5370
5371           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5372
5373           MovDelay[x][y] = 50;
5374
5375           Feld[newx][newy] = EL_FLAMES;
5376           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5377             Feld[newx1][newy1] = EL_FLAMES;
5378           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5379             Feld[newx2][newy2] = EL_FLAMES;
5380
5381           return;
5382         }
5383       }
5384     }
5385     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5386              Feld[newx][newy] == EL_DIAMOND)
5387     {
5388       if (IS_MOVING(newx, newy))
5389         RemoveMovingField(newx, newy);
5390       else
5391       {
5392         Feld[newx][newy] = EL_EMPTY;
5393         DrawLevelField(newx, newy);
5394       }
5395
5396       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5397     }
5398     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5399              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5400     {
5401       if (AmoebaNr[newx][newy])
5402       {
5403         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5404         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5405             Feld[newx][newy] == EL_BD_AMOEBA)
5406           AmoebaCnt[AmoebaNr[newx][newy]]--;
5407       }
5408
5409 #if 0
5410       /* !!! test !!! */
5411       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5412 #else
5413       if (IS_MOVING(newx, newy))
5414 #endif
5415       {
5416         RemoveMovingField(newx, newy);
5417       }
5418       else
5419       {
5420         Feld[newx][newy] = EL_EMPTY;
5421         DrawLevelField(newx, newy);
5422       }
5423
5424       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5425     }
5426     else if ((element == EL_PACMAN || element == EL_MOLE)
5427              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5428     {
5429       if (AmoebaNr[newx][newy])
5430       {
5431         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5432         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5433             Feld[newx][newy] == EL_BD_AMOEBA)
5434           AmoebaCnt[AmoebaNr[newx][newy]]--;
5435       }
5436
5437       if (element == EL_MOLE)
5438       {
5439         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5440         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5441
5442         ResetGfxAnimation(x, y);
5443         GfxAction[x][y] = ACTION_DIGGING;
5444         DrawLevelField(x, y);
5445
5446         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5447
5448         return;                         /* wait for shrinking amoeba */
5449       }
5450       else      /* element == EL_PACMAN */
5451       {
5452         Feld[newx][newy] = EL_EMPTY;
5453         DrawLevelField(newx, newy);
5454         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5455       }
5456     }
5457     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5458              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5459               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5460     {
5461       /* wait for shrinking amoeba to completely disappear */
5462       return;
5463     }
5464     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5465     {
5466       /* object was running against a wall */
5467
5468       TurnRound(x, y);
5469
5470 #if 1
5471       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5472         DrawLevelElementAnimation(x, y, element);
5473 #else
5474       if (element == EL_BUG ||
5475           element == EL_SPACESHIP ||
5476           element == EL_SP_SNIKSNAK)
5477         DrawLevelField(x, y);
5478       else if (element == EL_MOLE)
5479         DrawLevelField(x, y);
5480       else if (element == EL_BD_BUTTERFLY ||
5481                element == EL_BD_FIREFLY)
5482         DrawLevelElementAnimationIfNeeded(x, y, element);
5483       else if (element == EL_SATELLITE)
5484         DrawLevelElementAnimationIfNeeded(x, y, element);
5485       else if (element == EL_SP_ELECTRON)
5486         DrawLevelElementAnimationIfNeeded(x, y, element);
5487 #endif
5488
5489       if (DONT_TOUCH(element))
5490         TestIfBadThingTouchesHero(x, y);
5491
5492 #if 0
5493       PlayLevelSoundAction(x, y, ACTION_WAITING);
5494 #endif
5495
5496       return;
5497     }
5498
5499     InitMovingField(x, y, MovDir[x][y]);
5500
5501     PlayLevelSoundAction(x, y, ACTION_MOVING);
5502   }
5503
5504   if (MovDir[x][y])
5505     ContinueMoving(x, y);
5506 }
5507
5508 void ContinueMoving(int x, int y)
5509 {
5510   int element = Feld[x][y];
5511   struct ElementInfo *ei = &element_info[element];
5512   int direction = MovDir[x][y];
5513   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5514   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5515   int newx = x + dx, newy = y + dy;
5516 #if 0
5517   int nextx = newx + dx, nexty = newy + dy;
5518 #endif
5519 #if 1
5520   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
5521   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5522 #else
5523   boolean pushed_by_player = Pushed[x][y];
5524 #endif
5525
5526   MovPos[x][y] += getElementMoveStepsize(x, y);
5527
5528 #if 0
5529   if (pushed_by_player && IS_PLAYER(x, y))
5530   {
5531     /* special case: moving object pushed by player */
5532     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5533   }
5534 #else
5535   if (pushed_by_player) /* special case: moving object pushed by player */
5536     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5537 #endif
5538
5539   if (ABS(MovPos[x][y]) < TILEX)
5540   {
5541     DrawLevelField(x, y);
5542
5543     return;     /* element is still moving */
5544   }
5545
5546   /* element reached destination field */
5547
5548   Feld[x][y] = EL_EMPTY;
5549   Feld[newx][newy] = element;
5550   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
5551
5552   if (element == EL_MOLE)
5553   {
5554     Feld[x][y] = EL_SAND;
5555
5556     DrawLevelFieldCrumbledSandNeighbours(x, y);
5557   }
5558   else if (element == EL_QUICKSAND_FILLING)
5559   {
5560     element = Feld[newx][newy] = get_next_element(element);
5561     Store[newx][newy] = Store[x][y];
5562   }
5563   else if (element == EL_QUICKSAND_EMPTYING)
5564   {
5565     Feld[x][y] = get_next_element(element);
5566     element = Feld[newx][newy] = Store[x][y];
5567   }
5568   else if (element == EL_MAGIC_WALL_FILLING)
5569   {
5570     element = Feld[newx][newy] = get_next_element(element);
5571     if (!game.magic_wall_active)
5572       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5573     Store[newx][newy] = Store[x][y];
5574   }
5575   else if (element == EL_MAGIC_WALL_EMPTYING)
5576   {
5577     Feld[x][y] = get_next_element(element);
5578     if (!game.magic_wall_active)
5579       Feld[x][y] = EL_MAGIC_WALL_DEAD;
5580     element = Feld[newx][newy] = Store[x][y];
5581   }
5582   else if (element == EL_BD_MAGIC_WALL_FILLING)
5583   {
5584     element = Feld[newx][newy] = get_next_element(element);
5585     if (!game.magic_wall_active)
5586       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5587     Store[newx][newy] = Store[x][y];
5588   }
5589   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5590   {
5591     Feld[x][y] = get_next_element(element);
5592     if (!game.magic_wall_active)
5593       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5594     element = Feld[newx][newy] = Store[x][y];
5595   }
5596   else if (element == EL_AMOEBA_DROPPING)
5597   {
5598     Feld[x][y] = get_next_element(element);
5599     element = Feld[newx][newy] = Store[x][y];
5600   }
5601   else if (element == EL_SOKOBAN_OBJECT)
5602   {
5603     if (Back[x][y])
5604       Feld[x][y] = Back[x][y];
5605
5606     if (Back[newx][newy])
5607       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5608
5609     Back[x][y] = Back[newx][newy] = 0;
5610   }
5611   else if (Store[x][y] == EL_ACID)
5612   {
5613     element = Feld[newx][newy] = EL_ACID;
5614   }
5615
5616   Store[x][y] = 0;
5617   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5618   MovDelay[newx][newy] = 0;
5619
5620   /* copy element change control values to new field */
5621   ChangeDelay[newx][newy] = ChangeDelay[x][y];
5622   ChangePage[newx][newy] = ChangePage[x][y];
5623   Changed[newx][newy] = Changed[x][y];
5624   ChangeEvent[newx][newy] = ChangeEvent[x][y];
5625
5626   ChangeDelay[x][y] = 0;
5627   ChangePage[x][y] = -1;
5628   Changed[x][y] = CE_BITMASK_DEFAULT;
5629   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5630
5631   /* copy animation control values to new field */
5632   GfxFrame[newx][newy]  = GfxFrame[x][y];
5633   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
5634   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
5635   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
5636
5637   Pushed[x][y] = Pushed[newx][newy] = FALSE;
5638
5639   ResetGfxAnimation(x, y);      /* reset animation values for old field */
5640
5641 #if 1
5642   /* some elements can leave other elements behind after moving */
5643   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5644       ei->move_leave_element != EL_EMPTY &&
5645       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5646        ei->can_leave_element_last))
5647   {
5648     Feld[x][y] = ei->move_leave_element;
5649     InitField(x, y, FALSE);
5650
5651     if (GFX_CRUMBLED(Feld[x][y]))
5652       DrawLevelFieldCrumbledSandNeighbours(x, y);
5653   }
5654
5655   ei->can_leave_element_last = ei->can_leave_element;
5656   ei->can_leave_element = FALSE;
5657 #endif
5658
5659 #if 0
5660   /* 2.1.1 (does not work correctly for spring) */
5661   if (!CAN_MOVE(element))
5662     MovDir[newx][newy] = 0;
5663 #else
5664
5665 #if 0
5666   /* (does not work for falling objects that slide horizontally) */
5667   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5668     MovDir[newx][newy] = 0;
5669 #else
5670   /*
5671   if (!CAN_MOVE(element) ||
5672       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5673     MovDir[newx][newy] = 0;
5674   */
5675
5676   if (!CAN_MOVE(element) ||
5677       (CAN_FALL(element) && direction == MV_DOWN))
5678     GfxDir[x][y] = MovDir[newx][newy] = 0;
5679
5680 #endif
5681 #endif
5682
5683   DrawLevelField(x, y);
5684   DrawLevelField(newx, newy);
5685
5686   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
5687
5688   /* prevent pushed element from moving on in pushed direction */
5689   if (pushed_by_player && CAN_MOVE(element) &&
5690       element_info[element].move_pattern & MV_ANY_DIRECTION &&
5691       !(element_info[element].move_pattern & direction))
5692     TurnRound(newx, newy);
5693
5694 #if 1
5695   /* prevent elements on conveyor belt from moving on in last direction */
5696   if (pushed_by_conveyor && CAN_FALL(element) &&
5697       direction & MV_HORIZONTAL)
5698     MovDir[newx][newy] = 0;
5699 #endif
5700
5701   if (!pushed_by_player)
5702   {
5703     WasJustMoving[newx][newy] = 3;
5704
5705     if (CAN_FALL(element) && direction == MV_DOWN)
5706       WasJustFalling[newx][newy] = 3;
5707   }
5708
5709   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
5710   {
5711     TestIfBadThingTouchesHero(newx, newy);
5712     TestIfBadThingTouchesFriend(newx, newy);
5713
5714     if (!IS_CUSTOM_ELEMENT(element))
5715       TestIfBadThingTouchesOtherBadThing(newx, newy);
5716   }
5717   else if (element == EL_PENGUIN)
5718     TestIfFriendTouchesBadThing(newx, newy);
5719
5720   if (CAN_FALL(element) && direction == MV_DOWN &&
5721       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5722     Impact(x, newy);
5723
5724 #if 1
5725   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
5726 #endif
5727
5728 #if 0
5729   if (ChangePage[newx][newy] != -1)                     /* delayed change */
5730     ChangeElement(newx, newy, ChangePage[newx][newy]);
5731 #endif
5732
5733 #if 1
5734
5735   TestIfElementHitsCustomElement(newx, newy, direction);
5736
5737 #else
5738
5739   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5740   {
5741     int hitting_element = Feld[newx][newy];
5742
5743     /* !!! fix side (direction) orientation here and elsewhere !!! */
5744     CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
5745                            direction);
5746
5747 #if 0
5748     if (IN_LEV_FIELD(nextx, nexty))
5749     {
5750       int opposite_direction = MV_DIR_OPPOSITE(direction);
5751       int hitting_side = direction;
5752       int touched_side = opposite_direction;
5753       int touched_element = MovingOrBlocked2Element(nextx, nexty);
5754       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5755                             MovDir[nextx][nexty] != direction ||
5756                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5757
5758       if (object_hit)
5759       {
5760         int i;
5761
5762         CheckElementChangeSide(nextx, nexty, touched_element,
5763                                CE_HIT_BY_SOMETHING, opposite_direction);
5764
5765         if (IS_CUSTOM_ELEMENT(hitting_element) &&
5766             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5767         {
5768           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5769           {
5770             struct ElementChangeInfo *change =
5771               &element_info[hitting_element].change_page[i];
5772
5773             if (change->can_change &&
5774                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5775                 change->trigger_side & touched_side &&
5776                 change->trigger_element == touched_element)
5777             {
5778               CheckElementChangePage(newx, newy, hitting_element,
5779                                      CE_OTHER_IS_HITTING, i);
5780               break;
5781             }
5782           }
5783         }
5784
5785         if (IS_CUSTOM_ELEMENT(touched_element) &&
5786             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5787         {
5788           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5789           {
5790             struct ElementChangeInfo *change =
5791               &element_info[touched_element].change_page[i];
5792
5793             if (change->can_change &&
5794                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5795                 change->trigger_side & hitting_side &&
5796                 change->trigger_element == hitting_element)
5797             {
5798               CheckElementChangePage(nextx, nexty, touched_element,
5799                                      CE_OTHER_GETS_HIT, i);
5800               break;
5801             }
5802           }
5803         }
5804       }
5805     }
5806 #endif
5807   }
5808 #endif
5809
5810   TestIfPlayerTouchesCustomElement(newx, newy);
5811   TestIfElementTouchesCustomElement(newx, newy);
5812 }
5813
5814 int AmoebeNachbarNr(int ax, int ay)
5815 {
5816   int i;
5817   int element = Feld[ax][ay];
5818   int group_nr = 0;
5819   static int xy[4][2] =
5820   {
5821     { 0, -1 },
5822     { -1, 0 },
5823     { +1, 0 },
5824     { 0, +1 }
5825   };
5826
5827   for (i = 0; i < NUM_DIRECTIONS; i++)
5828   {
5829     int x = ax + xy[i][0];
5830     int y = ay + xy[i][1];
5831
5832     if (!IN_LEV_FIELD(x, y))
5833       continue;
5834
5835     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5836       group_nr = AmoebaNr[x][y];
5837   }
5838
5839   return group_nr;
5840 }
5841
5842 void AmoebenVereinigen(int ax, int ay)
5843 {
5844   int i, x, y, xx, yy;
5845   int new_group_nr = AmoebaNr[ax][ay];
5846   static int xy[4][2] =
5847   {
5848     { 0, -1 },
5849     { -1, 0 },
5850     { +1, 0 },
5851     { 0, +1 }
5852   };
5853
5854   if (new_group_nr == 0)
5855     return;
5856
5857   for (i = 0; i < NUM_DIRECTIONS; i++)
5858   {
5859     x = ax + xy[i][0];
5860     y = ay + xy[i][1];
5861
5862     if (!IN_LEV_FIELD(x, y))
5863       continue;
5864
5865     if ((Feld[x][y] == EL_AMOEBA_FULL ||
5866          Feld[x][y] == EL_BD_AMOEBA ||
5867          Feld[x][y] == EL_AMOEBA_DEAD) &&
5868         AmoebaNr[x][y] != new_group_nr)
5869     {
5870       int old_group_nr = AmoebaNr[x][y];
5871
5872       if (old_group_nr == 0)
5873         return;
5874
5875       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5876       AmoebaCnt[old_group_nr] = 0;
5877       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5878       AmoebaCnt2[old_group_nr] = 0;
5879
5880       for (yy = 0; yy < lev_fieldy; yy++)
5881       {
5882         for (xx = 0; xx < lev_fieldx; xx++)
5883         {
5884           if (AmoebaNr[xx][yy] == old_group_nr)
5885             AmoebaNr[xx][yy] = new_group_nr;
5886         }
5887       }
5888     }
5889   }
5890 }
5891
5892 void AmoebeUmwandeln(int ax, int ay)
5893 {
5894   int i, x, y;
5895
5896   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5897   {
5898     int group_nr = AmoebaNr[ax][ay];
5899
5900 #ifdef DEBUG
5901     if (group_nr == 0)
5902     {
5903       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5904       printf("AmoebeUmwandeln(): This should never happen!\n");
5905       return;
5906     }
5907 #endif
5908
5909     for (y = 0; y < lev_fieldy; y++)
5910     {
5911       for (x = 0; x < lev_fieldx; x++)
5912       {
5913         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5914         {
5915           AmoebaNr[x][y] = 0;
5916           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5917         }
5918       }
5919     }
5920     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5921                             SND_AMOEBA_TURNING_TO_GEM :
5922                             SND_AMOEBA_TURNING_TO_ROCK));
5923     Bang(ax, ay);
5924   }
5925   else
5926   {
5927     static int xy[4][2] =
5928     {
5929       { 0, -1 },
5930       { -1, 0 },
5931       { +1, 0 },
5932       { 0, +1 }
5933     };
5934
5935     for (i = 0; i < NUM_DIRECTIONS; i++)
5936     {
5937       x = ax + xy[i][0];
5938       y = ay + xy[i][1];
5939
5940       if (!IN_LEV_FIELD(x, y))
5941         continue;
5942
5943       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5944       {
5945         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5946                               SND_AMOEBA_TURNING_TO_GEM :
5947                               SND_AMOEBA_TURNING_TO_ROCK));
5948         Bang(x, y);
5949       }
5950     }
5951   }
5952 }
5953
5954 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5955 {
5956   int x, y;
5957   int group_nr = AmoebaNr[ax][ay];
5958   boolean done = FALSE;
5959
5960 #ifdef DEBUG
5961   if (group_nr == 0)
5962   {
5963     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5964     printf("AmoebeUmwandelnBD(): This should never happen!\n");
5965     return;
5966   }
5967 #endif
5968
5969   for (y = 0; y < lev_fieldy; y++)
5970   {
5971     for (x = 0; x < lev_fieldx; x++)
5972     {
5973       if (AmoebaNr[x][y] == group_nr &&
5974           (Feld[x][y] == EL_AMOEBA_DEAD ||
5975            Feld[x][y] == EL_BD_AMOEBA ||
5976            Feld[x][y] == EL_AMOEBA_GROWING))
5977       {
5978         AmoebaNr[x][y] = 0;
5979         Feld[x][y] = new_element;
5980         InitField(x, y, FALSE);
5981         DrawLevelField(x, y);
5982         done = TRUE;
5983       }
5984     }
5985   }
5986
5987   if (done)
5988     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5989                             SND_BD_AMOEBA_TURNING_TO_ROCK :
5990                             SND_BD_AMOEBA_TURNING_TO_GEM));
5991 }
5992
5993 void AmoebeWaechst(int x, int y)
5994 {
5995   static unsigned long sound_delay = 0;
5996   static unsigned long sound_delay_value = 0;
5997
5998   if (!MovDelay[x][y])          /* start new growing cycle */
5999   {
6000     MovDelay[x][y] = 7;
6001
6002     if (DelayReached(&sound_delay, sound_delay_value))
6003     {
6004 #if 1
6005       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6006 #else
6007       if (Store[x][y] == EL_BD_AMOEBA)
6008         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6009       else
6010         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6011 #endif
6012       sound_delay_value = 30;
6013     }
6014   }
6015
6016   if (MovDelay[x][y])           /* wait some time before growing bigger */
6017   {
6018     MovDelay[x][y]--;
6019     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6020     {
6021       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6022                                            6 - MovDelay[x][y]);
6023
6024       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6025     }
6026
6027     if (!MovDelay[x][y])
6028     {
6029       Feld[x][y] = Store[x][y];
6030       Store[x][y] = 0;
6031       DrawLevelField(x, y);
6032     }
6033   }
6034 }
6035
6036 void AmoebaDisappearing(int x, int y)
6037 {
6038   static unsigned long sound_delay = 0;
6039   static unsigned long sound_delay_value = 0;
6040
6041   if (!MovDelay[x][y])          /* start new shrinking cycle */
6042   {
6043     MovDelay[x][y] = 7;
6044
6045     if (DelayReached(&sound_delay, sound_delay_value))
6046       sound_delay_value = 30;
6047   }
6048
6049   if (MovDelay[x][y])           /* wait some time before shrinking */
6050   {
6051     MovDelay[x][y]--;
6052     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6053     {
6054       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6055                                            6 - MovDelay[x][y]);
6056
6057       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6058     }
6059
6060     if (!MovDelay[x][y])
6061     {
6062       Feld[x][y] = EL_EMPTY;
6063       DrawLevelField(x, y);
6064
6065       /* don't let mole enter this field in this cycle;
6066          (give priority to objects falling to this field from above) */
6067       Stop[x][y] = TRUE;
6068     }
6069   }
6070 }
6071
6072 void AmoebeAbleger(int ax, int ay)
6073 {
6074   int i;
6075   int element = Feld[ax][ay];
6076   int graphic = el2img(element);
6077   int newax = ax, neway = ay;
6078   static int xy[4][2] =
6079   {
6080     { 0, -1 },
6081     { -1, 0 },
6082     { +1, 0 },
6083     { 0, +1 }
6084   };
6085
6086   if (!level.amoeba_speed)
6087   {
6088     Feld[ax][ay] = EL_AMOEBA_DEAD;
6089     DrawLevelField(ax, ay);
6090     return;
6091   }
6092
6093   if (IS_ANIMATED(graphic))
6094     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6095
6096   if (!MovDelay[ax][ay])        /* start making new amoeba field */
6097     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6098
6099   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
6100   {
6101     MovDelay[ax][ay]--;
6102     if (MovDelay[ax][ay])
6103       return;
6104   }
6105
6106   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6107   {
6108     int start = RND(4);
6109     int x = ax + xy[start][0];
6110     int y = ay + xy[start][1];
6111
6112     if (!IN_LEV_FIELD(x, y))
6113       return;
6114
6115     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6116     if (IS_FREE(x, y) ||
6117         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6118     {
6119       newax = x;
6120       neway = y;
6121     }
6122
6123     if (newax == ax && neway == ay)
6124       return;
6125   }
6126   else                          /* normal or "filled" (BD style) amoeba */
6127   {
6128     int start = RND(4);
6129     boolean waiting_for_player = FALSE;
6130
6131     for (i = 0; i < NUM_DIRECTIONS; i++)
6132     {
6133       int j = (start + i) % 4;
6134       int x = ax + xy[j][0];
6135       int y = ay + xy[j][1];
6136
6137       if (!IN_LEV_FIELD(x, y))
6138         continue;
6139
6140       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6141       if (IS_FREE(x, y) ||
6142           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6143       {
6144         newax = x;
6145         neway = y;
6146         break;
6147       }
6148       else if (IS_PLAYER(x, y))
6149         waiting_for_player = TRUE;
6150     }
6151
6152     if (newax == ax && neway == ay)             /* amoeba cannot grow */
6153     {
6154       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6155       {
6156         Feld[ax][ay] = EL_AMOEBA_DEAD;
6157         DrawLevelField(ax, ay);
6158         AmoebaCnt[AmoebaNr[ax][ay]]--;
6159
6160         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
6161         {
6162           if (element == EL_AMOEBA_FULL)
6163             AmoebeUmwandeln(ax, ay);
6164           else if (element == EL_BD_AMOEBA)
6165             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6166         }
6167       }
6168       return;
6169     }
6170     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6171     {
6172       /* amoeba gets larger by growing in some direction */
6173
6174       int new_group_nr = AmoebaNr[ax][ay];
6175
6176 #ifdef DEBUG
6177   if (new_group_nr == 0)
6178   {
6179     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6180     printf("AmoebeAbleger(): This should never happen!\n");
6181     return;
6182   }
6183 #endif
6184
6185       AmoebaNr[newax][neway] = new_group_nr;
6186       AmoebaCnt[new_group_nr]++;
6187       AmoebaCnt2[new_group_nr]++;
6188
6189       /* if amoeba touches other amoeba(s) after growing, unify them */
6190       AmoebenVereinigen(newax, neway);
6191
6192       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6193       {
6194         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6195         return;
6196       }
6197     }
6198   }
6199
6200   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6201       (neway == lev_fieldy - 1 && newax != ax))
6202   {
6203     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6204     Store[newax][neway] = element;
6205   }
6206   else if (neway == ay)
6207   {
6208     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6209 #if 1
6210     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6211 #else
6212     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6213 #endif
6214   }
6215   else
6216   {
6217     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6218     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6219     Store[ax][ay] = EL_AMOEBA_DROP;
6220     ContinueMoving(ax, ay);
6221     return;
6222   }
6223
6224   DrawLevelField(newax, neway);
6225 }
6226
6227 void Life(int ax, int ay)
6228 {
6229   int x1, y1, x2, y2;
6230   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6231   int life_time = 40;
6232   int element = Feld[ax][ay];
6233   int graphic = el2img(element);
6234   boolean changed = FALSE;
6235
6236   if (IS_ANIMATED(graphic))
6237     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6238
6239   if (Stop[ax][ay])
6240     return;
6241
6242   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6243     MovDelay[ax][ay] = life_time;
6244
6245   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6246   {
6247     MovDelay[ax][ay]--;
6248     if (MovDelay[ax][ay])
6249       return;
6250   }
6251
6252   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6253   {
6254     int xx = ax+x1, yy = ay+y1;
6255     int nachbarn = 0;
6256
6257     if (!IN_LEV_FIELD(xx, yy))
6258       continue;
6259
6260     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6261     {
6262       int x = xx+x2, y = yy+y2;
6263
6264       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6265         continue;
6266
6267       if (((Feld[x][y] == element ||
6268             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6269            !Stop[x][y]) ||
6270           (IS_FREE(x, y) && Stop[x][y]))
6271         nachbarn++;
6272     }
6273
6274     if (xx == ax && yy == ay)           /* field in the middle */
6275     {
6276       if (nachbarn < life[0] || nachbarn > life[1])
6277       {
6278         Feld[xx][yy] = EL_EMPTY;
6279         if (!Stop[xx][yy])
6280           DrawLevelField(xx, yy);
6281         Stop[xx][yy] = TRUE;
6282         changed = TRUE;
6283       }
6284     }
6285     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6286     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6287     {                                   /* free border field */
6288       if (nachbarn >= life[2] && nachbarn <= life[3])
6289       {
6290         Feld[xx][yy] = element;
6291         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6292         if (!Stop[xx][yy])
6293           DrawLevelField(xx, yy);
6294         Stop[xx][yy] = TRUE;
6295         changed = TRUE;
6296       }
6297     }
6298   }
6299
6300   if (changed)
6301     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6302                    SND_GAME_OF_LIFE_GROWING);
6303 }
6304
6305 static void InitRobotWheel(int x, int y)
6306 {
6307   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6308 }
6309
6310 static void RunRobotWheel(int x, int y)
6311 {
6312   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6313 }
6314
6315 static void StopRobotWheel(int x, int y)
6316 {
6317   if (ZX == x && ZY == y)
6318     ZX = ZY = -1;
6319 }
6320
6321 static void InitTimegateWheel(int x, int y)
6322 {
6323   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6324 }
6325
6326 static void RunTimegateWheel(int x, int y)
6327 {
6328   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6329 }
6330
6331 void CheckExit(int x, int y)
6332 {
6333   if (local_player->gems_still_needed > 0 ||
6334       local_player->sokobanfields_still_needed > 0 ||
6335       local_player->lights_still_needed > 0)
6336   {
6337     int element = Feld[x][y];
6338     int graphic = el2img(element);
6339
6340     if (IS_ANIMATED(graphic))
6341       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6342
6343     return;
6344   }
6345
6346   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6347     return;
6348
6349   Feld[x][y] = EL_EXIT_OPENING;
6350
6351   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6352 }
6353
6354 void CheckExitSP(int x, int y)
6355 {
6356   if (local_player->gems_still_needed > 0)
6357   {
6358     int element = Feld[x][y];
6359     int graphic = el2img(element);
6360
6361     if (IS_ANIMATED(graphic))
6362       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6363
6364     return;
6365   }
6366
6367   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6368     return;
6369
6370   Feld[x][y] = EL_SP_EXIT_OPENING;
6371
6372   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6373 }
6374
6375 static void CloseAllOpenTimegates()
6376 {
6377   int x, y;
6378
6379   for (y = 0; y < lev_fieldy; y++)
6380   {
6381     for (x = 0; x < lev_fieldx; x++)
6382     {
6383       int element = Feld[x][y];
6384
6385       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6386       {
6387         Feld[x][y] = EL_TIMEGATE_CLOSING;
6388 #if 1
6389         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6390 #else
6391         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6392 #endif
6393       }
6394     }
6395   }
6396 }
6397
6398 void EdelsteinFunkeln(int x, int y)
6399 {
6400   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6401     return;
6402
6403   if (Feld[x][y] == EL_BD_DIAMOND)
6404     return;
6405
6406   if (MovDelay[x][y] == 0)      /* next animation frame */
6407     MovDelay[x][y] = 11 * !SimpleRND(500);
6408
6409   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6410   {
6411     MovDelay[x][y]--;
6412
6413     if (setup.direct_draw && MovDelay[x][y])
6414       SetDrawtoField(DRAW_BUFFERED);
6415
6416     DrawLevelElementAnimation(x, y, Feld[x][y]);
6417
6418     if (MovDelay[x][y] != 0)
6419     {
6420       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6421                                            10 - MovDelay[x][y]);
6422
6423       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6424
6425       if (setup.direct_draw)
6426       {
6427         int dest_x, dest_y;
6428
6429         dest_x = FX + SCREENX(x) * TILEX;
6430         dest_y = FY + SCREENY(y) * TILEY;
6431
6432         BlitBitmap(drawto_field, window,
6433                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6434         SetDrawtoField(DRAW_DIRECT);
6435       }
6436     }
6437   }
6438 }
6439
6440 void MauerWaechst(int x, int y)
6441 {
6442   int delay = 6;
6443
6444   if (!MovDelay[x][y])          /* next animation frame */
6445     MovDelay[x][y] = 3 * delay;
6446
6447   if (MovDelay[x][y])           /* wait some time before next frame */
6448   {
6449     MovDelay[x][y]--;
6450
6451     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6452     {
6453       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6454       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6455
6456       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6457     }
6458
6459     if (!MovDelay[x][y])
6460     {
6461       if (MovDir[x][y] == MV_LEFT)
6462       {
6463         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6464           DrawLevelField(x - 1, y);
6465       }
6466       else if (MovDir[x][y] == MV_RIGHT)
6467       {
6468         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6469           DrawLevelField(x + 1, y);
6470       }
6471       else if (MovDir[x][y] == MV_UP)
6472       {
6473         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6474           DrawLevelField(x, y - 1);
6475       }
6476       else
6477       {
6478         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6479           DrawLevelField(x, y + 1);
6480       }
6481
6482       Feld[x][y] = Store[x][y];
6483       Store[x][y] = 0;
6484       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6485       DrawLevelField(x, y);
6486     }
6487   }
6488 }
6489
6490 void MauerAbleger(int ax, int ay)
6491 {
6492   int element = Feld[ax][ay];
6493   int graphic = el2img(element);
6494   boolean oben_frei = FALSE, unten_frei = FALSE;
6495   boolean links_frei = FALSE, rechts_frei = FALSE;
6496   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6497   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6498   boolean new_wall = FALSE;
6499
6500   if (IS_ANIMATED(graphic))
6501     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6502
6503   if (!MovDelay[ax][ay])        /* start building new wall */
6504     MovDelay[ax][ay] = 6;
6505
6506   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6507   {
6508     MovDelay[ax][ay]--;
6509     if (MovDelay[ax][ay])
6510       return;
6511   }
6512
6513   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6514     oben_frei = TRUE;
6515   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6516     unten_frei = TRUE;
6517   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6518     links_frei = TRUE;
6519   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6520     rechts_frei = TRUE;
6521
6522   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6523       element == EL_EXPANDABLE_WALL_ANY)
6524   {
6525     if (oben_frei)
6526     {
6527       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6528       Store[ax][ay-1] = element;
6529       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6530       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6531         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6532                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6533       new_wall = TRUE;
6534     }
6535     if (unten_frei)
6536     {
6537       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6538       Store[ax][ay+1] = element;
6539       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6540       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6541         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6542                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6543       new_wall = TRUE;
6544     }
6545   }
6546
6547   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6548       element == EL_EXPANDABLE_WALL_ANY ||
6549       element == EL_EXPANDABLE_WALL)
6550   {
6551     if (links_frei)
6552     {
6553       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6554       Store[ax-1][ay] = element;
6555       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6556       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6557         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6558                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6559       new_wall = TRUE;
6560     }
6561
6562     if (rechts_frei)
6563     {
6564       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6565       Store[ax+1][ay] = element;
6566       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6567       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6568         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6569                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6570       new_wall = TRUE;
6571     }
6572   }
6573
6574   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6575     DrawLevelField(ax, ay);
6576
6577   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6578     oben_massiv = TRUE;
6579   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6580     unten_massiv = TRUE;
6581   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6582     links_massiv = TRUE;
6583   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6584     rechts_massiv = TRUE;
6585
6586   if (((oben_massiv && unten_massiv) ||
6587        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6588        element == EL_EXPANDABLE_WALL) &&
6589       ((links_massiv && rechts_massiv) ||
6590        element == EL_EXPANDABLE_WALL_VERTICAL))
6591     Feld[ax][ay] = EL_WALL;
6592
6593   if (new_wall)
6594 #if 1
6595     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6596 #else
6597     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6598 #endif
6599 }
6600
6601 void CheckForDragon(int x, int y)
6602 {
6603   int i, j;
6604   boolean dragon_found = FALSE;
6605   static int xy[4][2] =
6606   {
6607     { 0, -1 },
6608     { -1, 0 },
6609     { +1, 0 },
6610     { 0, +1 }
6611   };
6612
6613   for (i = 0; i < NUM_DIRECTIONS; i++)
6614   {
6615     for (j = 0; j < 4; j++)
6616     {
6617       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6618
6619       if (IN_LEV_FIELD(xx, yy) &&
6620           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6621       {
6622         if (Feld[xx][yy] == EL_DRAGON)
6623           dragon_found = TRUE;
6624       }
6625       else
6626         break;
6627     }
6628   }
6629
6630   if (!dragon_found)
6631   {
6632     for (i = 0; i < NUM_DIRECTIONS; i++)
6633     {
6634       for (j = 0; j < 3; j++)
6635       {
6636         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6637   
6638         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6639         {
6640           Feld[xx][yy] = EL_EMPTY;
6641           DrawLevelField(xx, yy);
6642         }
6643         else
6644           break;
6645       }
6646     }
6647   }
6648 }
6649
6650 static void InitBuggyBase(int x, int y)
6651 {
6652   int element = Feld[x][y];
6653   int activating_delay = FRAMES_PER_SECOND / 4;
6654
6655   ChangeDelay[x][y] =
6656     (element == EL_SP_BUGGY_BASE ?
6657      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6658      element == EL_SP_BUGGY_BASE_ACTIVATING ?
6659      activating_delay :
6660      element == EL_SP_BUGGY_BASE_ACTIVE ?
6661      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6662 }
6663
6664 static void WarnBuggyBase(int x, int y)
6665 {
6666   int i;
6667   static int xy[4][2] =
6668   {
6669     { 0, -1 },
6670     { -1, 0 },
6671     { +1, 0 },
6672     { 0, +1 }
6673   };
6674
6675   for (i = 0; i < NUM_DIRECTIONS; i++)
6676   {
6677     int xx = x + xy[i][0], yy = y + xy[i][1];
6678
6679     if (IS_PLAYER(xx, yy))
6680     {
6681       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6682
6683       break;
6684     }
6685   }
6686 }
6687
6688 static void InitTrap(int x, int y)
6689 {
6690   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6691 }
6692
6693 static void ActivateTrap(int x, int y)
6694 {
6695   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6696 }
6697
6698 static void ChangeActiveTrap(int x, int y)
6699 {
6700   int graphic = IMG_TRAP_ACTIVE;
6701
6702   /* if new animation frame was drawn, correct crumbled sand border */
6703   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6704     DrawLevelFieldCrumbledSand(x, y);
6705 }
6706
6707 static void ChangeElementNowExt(int x, int y, int target_element)
6708 {
6709   int previous_move_direction = MovDir[x][y];
6710
6711   /* check if element under player changes from accessible to unaccessible
6712      (needed for special case of dropping element which then changes) */
6713   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6714       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6715   {
6716     Bang(x, y);
6717     return;
6718   }
6719
6720   RemoveField(x, y);
6721   Feld[x][y] = target_element;
6722
6723   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6724
6725   ResetGfxAnimation(x, y);
6726   ResetRandomAnimationValue(x, y);
6727
6728   if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6729     MovDir[x][y] = previous_move_direction;
6730
6731 #if 1
6732   InitField_WithBug1(x, y, FALSE);
6733 #else
6734   InitField(x, y, FALSE);
6735   if (CAN_MOVE(Feld[x][y]))
6736     InitMovDir(x, y);
6737 #endif
6738
6739   DrawLevelField(x, y);
6740
6741   if (GFX_CRUMBLED(Feld[x][y]))
6742     DrawLevelFieldCrumbledSandNeighbours(x, y);
6743
6744   TestIfBadThingTouchesHero(x, y);
6745   TestIfPlayerTouchesCustomElement(x, y);
6746   TestIfElementTouchesCustomElement(x, y);
6747
6748   if (ELEM_IS_PLAYER(target_element))
6749     RelocatePlayer(x, y, target_element);
6750 }
6751
6752 static boolean ChangeElementNow(int x, int y, int element, int page)
6753 {
6754   struct ElementChangeInfo *change = &element_info[element].change_page[page];
6755
6756   /* always use default change event to prevent running into a loop */
6757   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6758     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6759
6760   /* do not change already changed elements with same change event */
6761 #if 0
6762   if (Changed[x][y] & ChangeEvent[x][y])
6763     return FALSE;
6764 #else
6765   if (Changed[x][y])
6766     return FALSE;
6767 #endif
6768
6769   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6770
6771   CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
6772
6773   if (change->explode)
6774   {
6775     Bang(x, y);
6776
6777     return TRUE;
6778   }
6779
6780   if (change->use_content)
6781   {
6782     boolean complete_change = TRUE;
6783     boolean can_change[3][3];
6784     int xx, yy;
6785
6786     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6787     {
6788       boolean half_destructible;
6789       int ex = x + xx - 1;
6790       int ey = y + yy - 1;
6791       int e;
6792
6793       can_change[xx][yy] = TRUE;
6794
6795       if (ex == x && ey == y)   /* do not check changing element itself */
6796         continue;
6797
6798       if (change->content[xx][yy] == EL_EMPTY_SPACE)
6799       {
6800         can_change[xx][yy] = FALSE;     /* do not change empty borders */
6801
6802         continue;
6803       }
6804
6805       if (!IN_LEV_FIELD(ex, ey))
6806       {
6807         can_change[xx][yy] = FALSE;
6808         complete_change = FALSE;
6809
6810         continue;
6811       }
6812
6813       e = Feld[ex][ey];
6814
6815       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6816         e = MovingOrBlocked2Element(ex, ey);
6817
6818       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6819
6820       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
6821           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6822           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6823       {
6824         can_change[xx][yy] = FALSE;
6825         complete_change = FALSE;
6826       }
6827     }
6828
6829     if (!change->only_complete || complete_change)
6830     {
6831       boolean something_has_changed = FALSE;
6832
6833       if (change->only_complete && change->use_random_change &&
6834           RND(100) < change->random)
6835         return FALSE;
6836
6837       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6838       {
6839         int ex = x + xx - 1;
6840         int ey = y + yy - 1;
6841
6842         if (can_change[xx][yy] && (!change->use_random_change ||
6843                                    RND(100) < change->random))
6844         {
6845           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6846             RemoveMovingField(ex, ey);
6847
6848           ChangeEvent[ex][ey] = ChangeEvent[x][y];
6849
6850           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6851
6852           something_has_changed = TRUE;
6853
6854           /* for symmetry reasons, freeze newly created border elements */
6855           if (ex != x || ey != y)
6856             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
6857         }
6858       }
6859
6860       if (something_has_changed)
6861         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6862     }
6863   }
6864   else
6865   {
6866     ChangeElementNowExt(x, y, change->target_element);
6867
6868     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6869   }
6870
6871   return TRUE;
6872 }
6873
6874 static void ChangeElement(int x, int y, int page)
6875 {
6876   int element = MovingOrBlocked2Element(x, y);
6877   struct ElementInfo *ei = &element_info[element];
6878   struct ElementChangeInfo *change = &ei->change_page[page];
6879
6880 #if 0
6881 #ifdef DEBUG
6882   if (!CAN_CHANGE(element))
6883   {
6884     printf("\n\n");
6885     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6886            x, y, element, element_info[element].token_name);
6887     printf("ChangeElement(): This should never happen!\n");
6888     printf("\n\n");
6889   }
6890 #endif
6891 #endif
6892
6893   if (ChangeDelay[x][y] == 0)           /* initialize element change */
6894   {
6895     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
6896                          RND(change->delay_random * change->delay_frames)) + 1;
6897
6898     ResetGfxAnimation(x, y);
6899     ResetRandomAnimationValue(x, y);
6900
6901     if (change->pre_change_function)
6902       change->pre_change_function(x, y);
6903   }
6904
6905   ChangeDelay[x][y]--;
6906
6907   if (ChangeDelay[x][y] != 0)           /* continue element change */
6908   {
6909     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6910
6911     if (IS_ANIMATED(graphic))
6912       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6913
6914     if (change->change_function)
6915       change->change_function(x, y);
6916   }
6917   else                                  /* finish element change */
6918   {
6919     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
6920     {
6921       page = ChangePage[x][y];
6922       ChangePage[x][y] = -1;
6923     }
6924
6925 #if 0
6926     if (IS_MOVING(x, y) && !change->explode)
6927 #else
6928     if (IS_MOVING(x, y))                /* never change a running system ;-) */
6929 #endif
6930     {
6931       ChangeDelay[x][y] = 1;            /* try change after next move step */
6932       ChangePage[x][y] = page;          /* remember page to use for change */
6933
6934       return;
6935     }
6936
6937     if (ChangeElementNow(x, y, element, page))
6938     {
6939       if (change->post_change_function)
6940         change->post_change_function(x, y);
6941     }
6942   }
6943 }
6944
6945 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
6946                                               int trigger_element,
6947                                               int trigger_event,
6948                                               int trigger_player,
6949                                               int trigger_side,
6950                                               int trigger_page)
6951 {
6952   int i, j, x, y;
6953
6954   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6955     return FALSE;
6956
6957   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6958   {
6959     int element = EL_CUSTOM_START + i;
6960
6961     boolean change_element = FALSE;
6962     int page = 0;
6963
6964     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6965       continue;
6966
6967     for (j = 0; j < element_info[element].num_change_pages; j++)
6968     {
6969       struct ElementChangeInfo *change = &element_info[element].change_page[j];
6970
6971       if (change->can_change &&
6972           change->events & CH_EVENT_BIT(trigger_event) &&
6973           change->trigger_side & trigger_side &&
6974           change->trigger_player & trigger_player &&
6975           change->trigger_page & (1 << trigger_page) &&
6976           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
6977       {
6978 #if 0
6979         if (!(change->events & CH_EVENT_BIT(trigger_event)))
6980           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6981                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6982 #endif
6983
6984         change_element = TRUE;
6985         page = j;
6986
6987         break;
6988       }
6989     }
6990
6991     if (!change_element)
6992       continue;
6993
6994     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6995     {
6996 #if 0
6997       if (x == lx && y == ly)   /* do not change trigger element itself */
6998         continue;
6999 #endif
7000
7001       if (Feld[x][y] == element)
7002       {
7003         ChangeDelay[x][y] = 1;
7004         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7005         ChangeElement(x, y, page);
7006       }
7007     }
7008   }
7009
7010   return TRUE;
7011 }
7012
7013 static boolean CheckElementChangeExt(int x, int y,
7014                                      int element,
7015                                      int trigger_event,
7016                                      int trigger_player,
7017                                      int trigger_side,
7018                                      int trigger_page)
7019 {
7020   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7021     return FALSE;
7022
7023   if (Feld[x][y] == EL_BLOCKED)
7024   {
7025     Blocked2Moving(x, y, &x, &y);
7026     element = Feld[x][y];
7027   }
7028
7029 #if 1
7030   if (trigger_page < 0)
7031   {
7032     boolean change_element = FALSE;
7033     int i;
7034
7035     for (i = 0; i < element_info[element].num_change_pages; i++)
7036     {
7037       struct ElementChangeInfo *change = &element_info[element].change_page[i];
7038
7039       if (change->can_change &&
7040           change->events & CH_EVENT_BIT(trigger_event) &&
7041           change->trigger_side & trigger_side &&
7042           change->trigger_player & trigger_player)
7043       {
7044         change_element = TRUE;
7045         trigger_page = i;
7046
7047         break;
7048       }
7049     }
7050
7051     if (!change_element)
7052       return FALSE;
7053   }
7054
7055 #else
7056
7057   /* !!! this check misses pages with same event, but different side !!! */
7058
7059   if (trigger_page < 0)
7060     trigger_page = element_info[element].event_page_nr[trigger_event];
7061
7062   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7063     return FALSE;
7064 #endif
7065
7066   ChangeDelay[x][y] = 1;
7067   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7068   ChangeElement(x, y, trigger_page);
7069
7070   return TRUE;
7071 }
7072
7073 static void PlayPlayerSound(struct PlayerInfo *player)
7074 {
7075   int jx = player->jx, jy = player->jy;
7076   int element = player->element_nr;
7077   int last_action = player->last_action_waiting;
7078   int action = player->action_waiting;
7079
7080   if (player->is_waiting)
7081   {
7082     if (action != last_action)
7083       PlayLevelSoundElementAction(jx, jy, element, action);
7084     else
7085       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7086   }
7087   else
7088   {
7089     if (action != last_action)
7090       StopSound(element_info[element].sound[last_action]);
7091
7092     if (last_action == ACTION_SLEEPING)
7093       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7094   }
7095 }
7096
7097 static void PlayAllPlayersSound()
7098 {
7099   int i;
7100
7101   for (i = 0; i < MAX_PLAYERS; i++)
7102     if (stored_player[i].active)
7103       PlayPlayerSound(&stored_player[i]);
7104 }
7105
7106 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7107 {
7108   boolean last_waiting = player->is_waiting;
7109   int move_dir = player->MovDir;
7110
7111   player->last_action_waiting = player->action_waiting;
7112
7113   if (is_waiting)
7114   {
7115     if (!last_waiting)          /* not waiting -> waiting */
7116     {
7117       player->is_waiting = TRUE;
7118
7119       player->frame_counter_bored =
7120         FrameCounter +
7121         game.player_boring_delay_fixed +
7122         SimpleRND(game.player_boring_delay_random);
7123       player->frame_counter_sleeping =
7124         FrameCounter +
7125         game.player_sleeping_delay_fixed +
7126         SimpleRND(game.player_sleeping_delay_random);
7127
7128       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7129     }
7130
7131     if (game.player_sleeping_delay_fixed +
7132         game.player_sleeping_delay_random > 0 &&
7133         player->anim_delay_counter == 0 &&
7134         player->post_delay_counter == 0 &&
7135         FrameCounter >= player->frame_counter_sleeping)
7136       player->is_sleeping = TRUE;
7137     else if (game.player_boring_delay_fixed +
7138              game.player_boring_delay_random > 0 &&
7139              FrameCounter >= player->frame_counter_bored)
7140       player->is_bored = TRUE;
7141
7142     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7143                               player->is_bored ? ACTION_BORING :
7144                               ACTION_WAITING);
7145
7146     if (player->is_sleeping)
7147     {
7148       if (player->num_special_action_sleeping > 0)
7149       {
7150         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7151         {
7152           int last_special_action = player->special_action_sleeping;
7153           int num_special_action = player->num_special_action_sleeping;
7154           int special_action =
7155             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7156              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7157              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7158              last_special_action + 1 : ACTION_SLEEPING);
7159           int special_graphic =
7160             el_act_dir2img(player->element_nr, special_action, move_dir);
7161
7162           player->anim_delay_counter =
7163             graphic_info[special_graphic].anim_delay_fixed +
7164             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7165           player->post_delay_counter =
7166             graphic_info[special_graphic].post_delay_fixed +
7167             SimpleRND(graphic_info[special_graphic].post_delay_random);
7168
7169           player->special_action_sleeping = special_action;
7170         }
7171
7172         if (player->anim_delay_counter > 0)
7173         {
7174           player->action_waiting = player->special_action_sleeping;
7175           player->anim_delay_counter--;
7176         }
7177         else if (player->post_delay_counter > 0)
7178         {
7179           player->post_delay_counter--;
7180         }
7181       }
7182     }
7183     else if (player->is_bored)
7184     {
7185       if (player->num_special_action_bored > 0)
7186       {
7187         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7188         {
7189           int special_action =
7190             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7191           int special_graphic =
7192             el_act_dir2img(player->element_nr, special_action, move_dir);
7193
7194           player->anim_delay_counter =
7195             graphic_info[special_graphic].anim_delay_fixed +
7196             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7197           player->post_delay_counter =
7198             graphic_info[special_graphic].post_delay_fixed +
7199             SimpleRND(graphic_info[special_graphic].post_delay_random);
7200
7201           player->special_action_bored = special_action;
7202         }
7203
7204         if (player->anim_delay_counter > 0)
7205         {
7206           player->action_waiting = player->special_action_bored;
7207           player->anim_delay_counter--;
7208         }
7209         else if (player->post_delay_counter > 0)
7210         {
7211           player->post_delay_counter--;
7212         }
7213       }
7214     }
7215   }
7216   else if (last_waiting)        /* waiting -> not waiting */
7217   {
7218     player->is_waiting = FALSE;
7219     player->is_bored = FALSE;
7220     player->is_sleeping = FALSE;
7221
7222     player->frame_counter_bored = -1;
7223     player->frame_counter_sleeping = -1;
7224
7225     player->anim_delay_counter = 0;
7226     player->post_delay_counter = 0;
7227
7228     player->action_waiting = ACTION_DEFAULT;
7229
7230     player->special_action_bored = ACTION_DEFAULT;
7231     player->special_action_sleeping = ACTION_DEFAULT;
7232   }
7233 }
7234
7235 #if 1
7236 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7237 {
7238 #if 0
7239   static byte stored_player_action[MAX_PLAYERS];
7240   static int num_stored_actions = 0;
7241 #endif
7242   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7243   int left      = player_action & JOY_LEFT;
7244   int right     = player_action & JOY_RIGHT;
7245   int up        = player_action & JOY_UP;
7246   int down      = player_action & JOY_DOWN;
7247   int button1   = player_action & JOY_BUTTON_1;
7248   int button2   = player_action & JOY_BUTTON_2;
7249   int dx        = (left ? -1    : right ? 1     : 0);
7250   int dy        = (up   ? -1    : down  ? 1     : 0);
7251
7252 #if 0
7253   stored_player_action[player->index_nr] = 0;
7254   num_stored_actions++;
7255 #endif
7256
7257 #if 0
7258   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7259 #endif
7260
7261   if (!player->active || tape.pausing)
7262     return 0;
7263
7264 #if 0
7265   printf("::: [%d %d %d %d] [%d %d]\n",
7266          left, right, up, down, button1, button2);
7267 #endif
7268
7269   if (player_action)
7270   {
7271 #if 0
7272     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7273 #endif
7274
7275 #if 0
7276     /* !!! TEST !!! */
7277     if (player->MovPos == 0)
7278       CheckGravityMovement(player);
7279 #endif
7280     if (button1)
7281       snapped = SnapField(player, dx, dy);
7282     else
7283     {
7284       if (button2)
7285         dropped = DropElement(player);
7286
7287       moved = MovePlayer(player, dx, dy);
7288     }
7289
7290     if (tape.single_step && tape.recording && !tape.pausing)
7291     {
7292       if (button1 || (dropped && !moved))
7293       {
7294         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7295         SnapField(player, 0, 0);                /* stop snapping */
7296       }
7297     }
7298
7299     SetPlayerWaiting(player, FALSE);
7300
7301 #if 1
7302     return player_action;
7303 #else
7304     stored_player_action[player->index_nr] = player_action;
7305 #endif
7306   }
7307   else
7308   {
7309 #if 0
7310     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7311 #endif
7312
7313     /* no actions for this player (no input at player's configured device) */
7314
7315     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7316     SnapField(player, 0, 0);
7317     CheckGravityMovementWhenNotMoving(player);
7318
7319     if (player->MovPos == 0)
7320       SetPlayerWaiting(player, TRUE);
7321
7322     if (player->MovPos == 0)    /* needed for tape.playing */
7323       player->is_moving = FALSE;
7324
7325     player->is_dropping = FALSE;
7326
7327     return 0;
7328   }
7329
7330 #if 0
7331   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7332   {
7333     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7334
7335     TapeRecordAction(stored_player_action);
7336     num_stored_actions = 0;
7337   }
7338 #endif
7339 }
7340
7341 #else
7342
7343 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7344 {
7345   static byte stored_player_action[MAX_PLAYERS];
7346   static int num_stored_actions = 0;
7347   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7348   int left      = player_action & JOY_LEFT;
7349   int right     = player_action & JOY_RIGHT;
7350   int up        = player_action & JOY_UP;
7351   int down      = player_action & JOY_DOWN;
7352   int button1   = player_action & JOY_BUTTON_1;
7353   int button2   = player_action & JOY_BUTTON_2;
7354   int dx        = (left ? -1    : right ? 1     : 0);
7355   int dy        = (up   ? -1    : down  ? 1     : 0);
7356
7357   stored_player_action[player->index_nr] = 0;
7358   num_stored_actions++;
7359
7360   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7361
7362   if (!player->active || tape.pausing)
7363     return;
7364
7365   if (player_action)
7366   {
7367     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7368
7369     if (button1)
7370       snapped = SnapField(player, dx, dy);
7371     else
7372     {
7373       if (button2)
7374         dropped = DropElement(player);
7375
7376       moved = MovePlayer(player, dx, dy);
7377     }
7378
7379     if (tape.single_step && tape.recording && !tape.pausing)
7380     {
7381       if (button1 || (dropped && !moved))
7382       {
7383         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7384         SnapField(player, 0, 0);                /* stop snapping */
7385       }
7386     }
7387
7388     stored_player_action[player->index_nr] = player_action;
7389   }
7390   else
7391   {
7392     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7393
7394     /* no actions for this player (no input at player's configured device) */
7395
7396     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7397     SnapField(player, 0, 0);
7398     CheckGravityMovementWhenNotMoving(player);
7399
7400     if (player->MovPos == 0)
7401       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7402
7403     if (player->MovPos == 0)    /* needed for tape.playing */
7404       player->is_moving = FALSE;
7405   }
7406
7407   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7408   {
7409     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7410
7411     TapeRecordAction(stored_player_action);
7412     num_stored_actions = 0;
7413   }
7414 }
7415 #endif
7416
7417 void GameActions()
7418 {
7419   static unsigned long action_delay = 0;
7420   unsigned long action_delay_value;
7421   int magic_wall_x = 0, magic_wall_y = 0;
7422   int i, x, y, element, graphic;
7423   byte *recorded_player_action;
7424   byte summarized_player_action = 0;
7425 #if 1
7426   byte tape_action[MAX_PLAYERS];
7427 #endif
7428
7429   if (game_status != GAME_MODE_PLAYING)
7430     return;
7431
7432   action_delay_value =
7433     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7434
7435   if (tape.playing && tape.index_search && !tape.pausing)
7436     action_delay_value = 0;
7437
7438   /* ---------- main game synchronization point ---------- */
7439
7440   WaitUntilDelayReached(&action_delay, action_delay_value);
7441
7442   if (network_playing && !network_player_action_received)
7443   {
7444     /*
7445 #ifdef DEBUG
7446     printf("DEBUG: try to get network player actions in time\n");
7447 #endif
7448     */
7449
7450 #if defined(PLATFORM_UNIX)
7451     /* last chance to get network player actions without main loop delay */
7452     HandleNetworking();
7453 #endif
7454
7455     if (game_status != GAME_MODE_PLAYING)
7456       return;
7457
7458     if (!network_player_action_received)
7459     {
7460       /*
7461 #ifdef DEBUG
7462       printf("DEBUG: failed to get network player actions in time\n");
7463 #endif
7464       */
7465       return;
7466     }
7467   }
7468
7469   if (tape.pausing)
7470     return;
7471
7472 #if 0
7473   printf("::: getting new tape action [%d]\n", FrameCounter);
7474 #endif
7475
7476   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7477
7478 #if 1
7479   if (recorded_player_action != NULL)
7480     for (i = 0; i < MAX_PLAYERS; i++)
7481       stored_player[i].action = recorded_player_action[i];
7482 #endif
7483
7484   for (i = 0; i < MAX_PLAYERS; i++)
7485   {
7486     summarized_player_action |= stored_player[i].action;
7487
7488     if (!network_playing)
7489       stored_player[i].effective_action = stored_player[i].action;
7490   }
7491
7492 #if defined(PLATFORM_UNIX)
7493   if (network_playing)
7494     SendToServer_MovePlayer(summarized_player_action);
7495 #endif
7496
7497   if (!options.network && !setup.team_mode)
7498     local_player->effective_action = summarized_player_action;
7499
7500 #if 1
7501   for (i = 0; i < MAX_PLAYERS; i++)
7502   {
7503     tape_action[i] = stored_player[i].effective_action;
7504
7505     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7506       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7507   }
7508
7509   /* only save actions from input devices, but not programmed actions */
7510   if (tape.recording)
7511     TapeRecordAction(tape_action);
7512 #endif
7513
7514   for (i = 0; i < MAX_PLAYERS; i++)
7515   {
7516     int actual_player_action = stored_player[i].effective_action;
7517
7518 #if 1
7519     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7520        - rnd_equinox_tetrachloride 048
7521        - rnd_equinox_tetrachloride_ii 096
7522        - rnd_emanuel_schmieg 002
7523     */
7524     if (stored_player[i].MovPos == 0)
7525       CheckGravityMovement(&stored_player[i]);
7526 #endif
7527
7528 #if 1
7529     /* overwrite programmed action with tape action */
7530     if (stored_player[i].programmed_action)
7531       actual_player_action = stored_player[i].programmed_action;
7532 #endif
7533
7534     if (recorded_player_action)
7535     {
7536 #if 0
7537       if (stored_player[i].programmed_action &&
7538           stored_player[i].programmed_action != recorded_player_action[i])
7539         printf("::: %d: %d <-> %d\n", i,
7540                stored_player[i].programmed_action, recorded_player_action[i]);
7541 #endif
7542
7543 #if 0
7544       actual_player_action = recorded_player_action[i];
7545 #endif
7546     }
7547
7548 #if 0
7549     /* overwrite tape action with programmed action */
7550     if (stored_player[i].programmed_action)
7551       actual_player_action = stored_player[i].programmed_action;
7552 #endif
7553
7554 #if 0
7555     if (i == 0)
7556       printf("::: action: %d: %x [%d]\n",
7557              stored_player[i].MovPos, actual_player_action, FrameCounter);
7558 #endif
7559
7560 #if 1
7561     PlayerActions(&stored_player[i], actual_player_action);
7562 #else
7563     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7564
7565     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7566       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7567 #endif
7568
7569     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7570   }
7571
7572 #if 0
7573   if (tape.recording)
7574     TapeRecordAction(tape_action);
7575 #endif
7576
7577   network_player_action_received = FALSE;
7578
7579   ScrollScreen(NULL, SCROLL_GO_ON);
7580
7581 #if 0
7582   FrameCounter++;
7583   TimeFrames++;
7584
7585   for (i = 0; i < MAX_PLAYERS; i++)
7586     stored_player[i].Frame++;
7587 #endif
7588
7589 #if 1
7590   /* for downwards compatibility, the following code emulates a fixed bug that
7591      occured when pushing elements (causing elements that just made their last
7592      pushing step to already (if possible) make their first falling step in the
7593      same game frame, which is bad); this code is also needed to use the famous
7594      "spring push bug" which is used in older levels and might be wanted to be
7595      used also in newer levels, but in this case the buggy pushing code is only
7596      affecting the "spring" element and no other elements */
7597
7598 #if 1
7599   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7600 #else
7601   if (game.engine_version < VERSION_IDENT(2,2,0,7))
7602 #endif
7603   {
7604     for (i = 0; i < MAX_PLAYERS; i++)
7605     {
7606       struct PlayerInfo *player = &stored_player[i];
7607       int x = player->jx;
7608       int y = player->jy;
7609
7610 #if 1
7611       if (player->active && player->is_pushing && player->is_moving &&
7612           IS_MOVING(x, y) &&
7613           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7614            Feld[x][y] == EL_SPRING))
7615 #else
7616       if (player->active && player->is_pushing && player->is_moving &&
7617           IS_MOVING(x, y))
7618 #endif
7619       {
7620         ContinueMoving(x, y);
7621
7622         /* continue moving after pushing (this is actually a bug) */
7623         if (!IS_MOVING(x, y))
7624         {
7625           Stop[x][y] = FALSE;
7626         }
7627       }
7628     }
7629   }
7630 #endif
7631
7632   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7633   {
7634     Changed[x][y] = CE_BITMASK_DEFAULT;
7635     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7636
7637 #if DEBUG
7638     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7639     {
7640       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7641       printf("GameActions(): This should never happen!\n");
7642
7643       ChangePage[x][y] = -1;
7644     }
7645 #endif
7646
7647     Stop[x][y] = FALSE;
7648     if (WasJustMoving[x][y] > 0)
7649       WasJustMoving[x][y]--;
7650     if (WasJustFalling[x][y] > 0)
7651       WasJustFalling[x][y]--;
7652
7653     GfxFrame[x][y]++;
7654
7655 #if 1
7656     /* reset finished pushing action (not done in ContinueMoving() to allow
7657        continous pushing animation for elements with zero push delay) */
7658     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7659     {
7660       ResetGfxAnimation(x, y);
7661       DrawLevelField(x, y);
7662     }
7663 #endif
7664
7665 #if DEBUG
7666     if (IS_BLOCKED(x, y))
7667     {
7668       int oldx, oldy;
7669
7670       Blocked2Moving(x, y, &oldx, &oldy);
7671       if (!IS_MOVING(oldx, oldy))
7672       {
7673         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7674         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7675         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7676         printf("GameActions(): This should never happen!\n");
7677       }
7678     }
7679 #endif
7680   }
7681
7682   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7683   {
7684     element = Feld[x][y];
7685 #if 1
7686     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7687 #else
7688     graphic = el2img(element);
7689 #endif
7690
7691 #if 0
7692     if (element == -1)
7693     {
7694       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7695
7696       element = graphic = 0;
7697     }
7698 #endif
7699
7700     if (graphic_info[graphic].anim_global_sync)
7701       GfxFrame[x][y] = FrameCounter;
7702
7703     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7704         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7705       ResetRandomAnimationValue(x, y);
7706
7707     SetRandomAnimationValue(x, y);
7708
7709 #if 1
7710     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7711 #endif
7712
7713     if (IS_INACTIVE(element))
7714     {
7715       if (IS_ANIMATED(graphic))
7716         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7717
7718       continue;
7719     }
7720
7721 #if 1
7722     /* this may take place after moving, so 'element' may have changed */
7723 #if 0
7724     if (IS_CHANGING(x, y))
7725 #else
7726     if (IS_CHANGING(x, y) &&
7727         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7728 #endif
7729     {
7730 #if 0
7731       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7732                     element_info[element].event_page_nr[CE_DELAY]);
7733 #else
7734       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7735 #endif
7736
7737       element = Feld[x][y];
7738       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7739     }
7740 #endif
7741
7742     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7743     {
7744       StartMoving(x, y);
7745
7746 #if 1
7747       element = Feld[x][y];
7748       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7749 #if 0
7750       if (element == EL_MOLE)
7751         printf("::: %d, %d, %d [%d]\n",
7752                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7753                GfxAction[x][y]);
7754 #endif
7755 #if 0
7756       if (element == EL_YAMYAM)
7757         printf("::: %d, %d, %d\n",
7758                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7759 #endif
7760 #endif
7761
7762       if (IS_ANIMATED(graphic) &&
7763           !IS_MOVING(x, y) &&
7764           !Stop[x][y])
7765       {
7766         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7767
7768 #if 0
7769         if (element == EL_BUG)
7770           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7771 #endif
7772
7773 #if 0
7774         if (element == EL_MOLE)
7775           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7776 #endif
7777       }
7778
7779       if (IS_GEM(element) || element == EL_SP_INFOTRON)
7780         EdelsteinFunkeln(x, y);
7781     }
7782     else if ((element == EL_ACID ||
7783               element == EL_EXIT_OPEN ||
7784               element == EL_SP_EXIT_OPEN ||
7785               element == EL_SP_TERMINAL ||
7786               element == EL_SP_TERMINAL_ACTIVE ||
7787               element == EL_EXTRA_TIME ||
7788               element == EL_SHIELD_NORMAL ||
7789               element == EL_SHIELD_DEADLY) &&
7790              IS_ANIMATED(graphic))
7791       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7792     else if (IS_MOVING(x, y))
7793       ContinueMoving(x, y);
7794     else if (IS_ACTIVE_BOMB(element))
7795       CheckDynamite(x, y);
7796 #if 0
7797     else if (element == EL_EXPLOSION && !game.explosions_delayed)
7798       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7799 #endif
7800     else if (element == EL_AMOEBA_GROWING)
7801       AmoebeWaechst(x, y);
7802     else if (element == EL_AMOEBA_SHRINKING)
7803       AmoebaDisappearing(x, y);
7804
7805 #if !USE_NEW_AMOEBA_CODE
7806     else if (IS_AMOEBALIVE(element))
7807       AmoebeAbleger(x, y);
7808 #endif
7809
7810     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7811       Life(x, y);
7812     else if (element == EL_EXIT_CLOSED)
7813       CheckExit(x, y);
7814     else if (element == EL_SP_EXIT_CLOSED)
7815       CheckExitSP(x, y);
7816     else if (element == EL_EXPANDABLE_WALL_GROWING)
7817       MauerWaechst(x, y);
7818     else if (element == EL_EXPANDABLE_WALL ||
7819              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7820              element == EL_EXPANDABLE_WALL_VERTICAL ||
7821              element == EL_EXPANDABLE_WALL_ANY)
7822       MauerAbleger(x, y);
7823     else if (element == EL_FLAMES)
7824       CheckForDragon(x, y);
7825 #if 0
7826     else if (IS_AUTO_CHANGING(element))
7827       ChangeElement(x, y);
7828 #endif
7829     else if (element == EL_EXPLOSION)
7830       ; /* drawing of correct explosion animation is handled separately */
7831     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7832       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7833
7834 #if 0
7835     /* this may take place after moving, so 'element' may have changed */
7836     if (IS_AUTO_CHANGING(Feld[x][y]))
7837       ChangeElement(x, y);
7838 #endif
7839
7840     if (IS_BELT_ACTIVE(element))
7841       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7842
7843     if (game.magic_wall_active)
7844     {
7845       int jx = local_player->jx, jy = local_player->jy;
7846
7847       /* play the element sound at the position nearest to the player */
7848       if ((element == EL_MAGIC_WALL_FULL ||
7849            element == EL_MAGIC_WALL_ACTIVE ||
7850            element == EL_MAGIC_WALL_EMPTYING ||
7851            element == EL_BD_MAGIC_WALL_FULL ||
7852            element == EL_BD_MAGIC_WALL_ACTIVE ||
7853            element == EL_BD_MAGIC_WALL_EMPTYING) &&
7854           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7855       {
7856         magic_wall_x = x;
7857         magic_wall_y = y;
7858       }
7859     }
7860   }
7861
7862 #if USE_NEW_AMOEBA_CODE
7863   /* new experimental amoeba growth stuff */
7864 #if 1
7865   if (!(FrameCounter % 8))
7866 #endif
7867   {
7868     static unsigned long random = 1684108901;
7869
7870     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7871     {
7872 #if 0
7873       x = (random >> 10) % lev_fieldx;
7874       y = (random >> 20) % lev_fieldy;
7875 #else
7876       x = RND(lev_fieldx);
7877       y = RND(lev_fieldy);
7878 #endif
7879       element = Feld[x][y];
7880
7881       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7882       if (!IS_PLAYER(x,y) &&
7883           (element == EL_EMPTY ||
7884            element == EL_SAND ||
7885            element == EL_QUICKSAND_EMPTY ||
7886            element == EL_ACID_SPLASH_LEFT ||
7887            element == EL_ACID_SPLASH_RIGHT))
7888       {
7889         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7890             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7891             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7892             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7893           Feld[x][y] = EL_AMOEBA_DROP;
7894       }
7895
7896       random = random * 129 + 1;
7897     }
7898   }
7899 #endif
7900
7901 #if 0
7902   if (game.explosions_delayed)
7903 #endif
7904   {
7905     game.explosions_delayed = FALSE;
7906
7907     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7908     {
7909       element = Feld[x][y];
7910
7911       if (ExplodeField[x][y])
7912         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7913       else if (element == EL_EXPLOSION)
7914         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7915
7916       ExplodeField[x][y] = EX_NO_EXPLOSION;
7917     }
7918
7919     game.explosions_delayed = TRUE;
7920   }
7921
7922   if (game.magic_wall_active)
7923   {
7924     if (!(game.magic_wall_time_left % 4))
7925     {
7926       int element = Feld[magic_wall_x][magic_wall_y];
7927
7928       if (element == EL_BD_MAGIC_WALL_FULL ||
7929           element == EL_BD_MAGIC_WALL_ACTIVE ||
7930           element == EL_BD_MAGIC_WALL_EMPTYING)
7931         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7932       else
7933         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7934     }
7935
7936     if (game.magic_wall_time_left > 0)
7937     {
7938       game.magic_wall_time_left--;
7939       if (!game.magic_wall_time_left)
7940       {
7941         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7942         {
7943           element = Feld[x][y];
7944
7945           if (element == EL_MAGIC_WALL_ACTIVE ||
7946               element == EL_MAGIC_WALL_FULL)
7947           {
7948             Feld[x][y] = EL_MAGIC_WALL_DEAD;
7949             DrawLevelField(x, y);
7950           }
7951           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7952                    element == EL_BD_MAGIC_WALL_FULL)
7953           {
7954             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7955             DrawLevelField(x, y);
7956           }
7957         }
7958
7959         game.magic_wall_active = FALSE;
7960       }
7961     }
7962   }
7963
7964   if (game.light_time_left > 0)
7965   {
7966     game.light_time_left--;
7967
7968     if (game.light_time_left == 0)
7969       RedrawAllLightSwitchesAndInvisibleElements();
7970   }
7971
7972   if (game.timegate_time_left > 0)
7973   {
7974     game.timegate_time_left--;
7975
7976     if (game.timegate_time_left == 0)
7977       CloseAllOpenTimegates();
7978   }
7979
7980   for (i = 0; i < MAX_PLAYERS; i++)
7981   {
7982     struct PlayerInfo *player = &stored_player[i];
7983
7984     if (SHIELD_ON(player))
7985     {
7986       if (player->shield_deadly_time_left)
7987         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7988       else if (player->shield_normal_time_left)
7989         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7990     }
7991   }
7992
7993   if (TimeFrames >= FRAMES_PER_SECOND)
7994   {
7995     TimeFrames = 0;
7996     TapeTime++;
7997
7998     if (!level.use_step_counter)
7999     {
8000       TimePlayed++;
8001
8002       for (i = 0; i < MAX_PLAYERS; i++)
8003       {
8004         struct PlayerInfo *player = &stored_player[i];
8005
8006         if (SHIELD_ON(player))
8007         {
8008           player->shield_normal_time_left--;
8009
8010           if (player->shield_deadly_time_left > 0)
8011             player->shield_deadly_time_left--;
8012         }
8013       }
8014
8015       if (TimeLeft > 0)
8016       {
8017         TimeLeft--;
8018
8019         if (TimeLeft <= 10 && setup.time_limit)
8020           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8021
8022         DrawGameValue_Time(TimeLeft);
8023
8024         if (!TimeLeft && setup.time_limit)
8025           for (i = 0; i < MAX_PLAYERS; i++)
8026             KillHero(&stored_player[i]);
8027       }
8028       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8029         DrawGameValue_Time(TimePlayed);
8030     }
8031
8032     if (tape.recording || tape.playing)
8033       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8034   }
8035
8036   DrawAllPlayers();
8037   PlayAllPlayersSound();
8038
8039   if (options.debug)                    /* calculate frames per second */
8040   {
8041     static unsigned long fps_counter = 0;
8042     static int fps_frames = 0;
8043     unsigned long fps_delay_ms = Counter() - fps_counter;
8044
8045     fps_frames++;
8046
8047     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
8048     {
8049       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8050
8051       fps_frames = 0;
8052       fps_counter = Counter();
8053     }
8054
8055     redraw_mask |= REDRAW_FPS;
8056   }
8057
8058 #if 0
8059   if (stored_player[0].jx != stored_player[0].last_jx ||
8060       stored_player[0].jy != stored_player[0].last_jy)
8061     printf("::: %d, %d, %d, %d, %d\n",
8062            stored_player[0].MovDir,
8063            stored_player[0].MovPos,
8064            stored_player[0].GfxPos,
8065            stored_player[0].Frame,
8066            stored_player[0].StepFrame);
8067 #endif
8068
8069 #if 1
8070   FrameCounter++;
8071   TimeFrames++;
8072
8073   for (i = 0; i < MAX_PLAYERS; i++)
8074   {
8075     int move_frames =
8076       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
8077
8078     stored_player[i].Frame += move_frames;
8079
8080     if (stored_player[i].MovPos != 0)
8081       stored_player[i].StepFrame += move_frames;
8082
8083     if (stored_player[i].drop_delay > 0)
8084       stored_player[i].drop_delay--;
8085   }
8086 #endif
8087
8088 #if 1
8089   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8090   {
8091     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8092
8093     local_player->show_envelope = 0;
8094   }
8095 #endif
8096 }
8097
8098 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8099 {
8100   int min_x = x, min_y = y, max_x = x, max_y = y;
8101   int i;
8102
8103   for (i = 0; i < MAX_PLAYERS; i++)
8104   {
8105     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8106
8107     if (!stored_player[i].active || &stored_player[i] == player)
8108       continue;
8109
8110     min_x = MIN(min_x, jx);
8111     min_y = MIN(min_y, jy);
8112     max_x = MAX(max_x, jx);
8113     max_y = MAX(max_y, jy);
8114   }
8115
8116   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8117 }
8118
8119 static boolean AllPlayersInVisibleScreen()
8120 {
8121   int i;
8122
8123   for (i = 0; i < MAX_PLAYERS; i++)
8124   {
8125     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8126
8127     if (!stored_player[i].active)
8128       continue;
8129
8130     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8131       return FALSE;
8132   }
8133
8134   return TRUE;
8135 }
8136
8137 void ScrollLevel(int dx, int dy)
8138 {
8139   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8140   int x, y;
8141
8142   BlitBitmap(drawto_field, drawto_field,
8143              FX + TILEX * (dx == -1) - softscroll_offset,
8144              FY + TILEY * (dy == -1) - softscroll_offset,
8145              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8146              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8147              FX + TILEX * (dx == 1) - softscroll_offset,
8148              FY + TILEY * (dy == 1) - softscroll_offset);
8149
8150   if (dx)
8151   {
8152     x = (dx == 1 ? BX1 : BX2);
8153     for (y = BY1; y <= BY2; y++)
8154       DrawScreenField(x, y);
8155   }
8156
8157   if (dy)
8158   {
8159     y = (dy == 1 ? BY1 : BY2);
8160     for (x = BX1; x <= BX2; x++)
8161       DrawScreenField(x, y);
8162   }
8163
8164   redraw_mask |= REDRAW_FIELD;
8165 }
8166
8167 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8168 {
8169   int nextx = x + dx, nexty = y + dy;
8170   int element = Feld[x][y];
8171
8172   if ((dx == -1 &&
8173        element != EL_SP_PORT_LEFT &&
8174        element != EL_SP_GRAVITY_PORT_LEFT &&
8175        element != EL_SP_PORT_HORIZONTAL &&
8176        element != EL_SP_PORT_ANY) ||
8177       (dx == +1 &&
8178        element != EL_SP_PORT_RIGHT &&
8179        element != EL_SP_GRAVITY_PORT_RIGHT &&
8180        element != EL_SP_PORT_HORIZONTAL &&
8181        element != EL_SP_PORT_ANY) ||
8182       (dy == -1 &&
8183        element != EL_SP_PORT_UP &&
8184        element != EL_SP_GRAVITY_PORT_UP &&
8185        element != EL_SP_PORT_VERTICAL &&
8186        element != EL_SP_PORT_ANY) ||
8187       (dy == +1 &&
8188        element != EL_SP_PORT_DOWN &&
8189        element != EL_SP_GRAVITY_PORT_DOWN &&
8190        element != EL_SP_PORT_VERTICAL &&
8191        element != EL_SP_PORT_ANY) ||
8192       !IN_LEV_FIELD(nextx, nexty) ||
8193       !IS_FREE(nextx, nexty))
8194     return FALSE;
8195
8196   return TRUE;
8197 }
8198
8199 static void CheckGravityMovement(struct PlayerInfo *player)
8200 {
8201   if (game.gravity && !player->programmed_action)
8202   {
8203     int move_dir_horizontal = player->action & MV_HORIZONTAL;
8204     int move_dir_vertical   = player->action & MV_VERTICAL;
8205     int move_dir =
8206       (player->last_move_dir & MV_HORIZONTAL ?
8207        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8208        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8209     int jx = player->jx, jy = player->jy;
8210     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8211     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8212     int new_jx = jx + dx, new_jy = jy + dy;
8213     boolean player_is_snapping = player->action & JOY_BUTTON_1;
8214 #if 1
8215     boolean player_can_fall_down =
8216       (IN_LEV_FIELD(jx, jy + 1) &&
8217        (IS_FREE(jx, jy + 1) ||
8218         (Feld[jx][jy + 1] == EL_ACID && level.player_can_fall_into_acid)));
8219 #else
8220     boolean player_can_fall_down =
8221       (IN_LEV_FIELD(jx, jy + 1) &&
8222        (IS_FREE(jx, jy + 1)));
8223 #endif
8224     boolean player_is_moving_to_valid_field =
8225       (
8226 #if 1
8227        !player_is_snapping &&
8228 #endif
8229        IN_LEV_FIELD(new_jx, new_jy) &&
8230        (Feld[new_jx][new_jy] == EL_SP_BASE ||
8231         Feld[new_jx][new_jy] == EL_SAND ||
8232         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8233          canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8234     /* !!! extend EL_SAND to anything diggable !!! */
8235
8236     boolean player_is_standing_on_valid_field =
8237       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8238        (IS_WALKABLE(Feld[jx][jy]) &&
8239         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8240
8241 #if 0
8242     printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
8243            player_can_fall_down,
8244            player_is_standing_on_valid_field,
8245            player_is_moving_to_valid_field,
8246            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
8247 #endif
8248
8249     if (player_can_fall_down &&
8250         !player_is_standing_on_valid_field &&
8251         !player_is_moving_to_valid_field)
8252     {
8253 #if 0
8254       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8255              jx, jy, FrameCounter);
8256 #endif
8257
8258       player->programmed_action = MV_DOWN;
8259     }
8260   }
8261 }
8262
8263 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8264 {
8265 #if 1
8266   return CheckGravityMovement(player);
8267 #endif
8268
8269   if (game.gravity && !player->programmed_action)
8270   {
8271     int jx = player->jx, jy = player->jy;
8272     boolean field_under_player_is_free =
8273       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8274     boolean player_is_standing_on_valid_field =
8275       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8276        (IS_WALKABLE(Feld[jx][jy]) &&
8277         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8278
8279     if (field_under_player_is_free && !player_is_standing_on_valid_field)
8280       player->programmed_action = MV_DOWN;
8281   }
8282 }
8283
8284 /*
8285   MovePlayerOneStep()
8286   -----------------------------------------------------------------------------
8287   dx, dy:               direction (non-diagonal) to try to move the player to
8288   real_dx, real_dy:     direction as read from input device (can be diagonal)
8289 */
8290
8291 boolean MovePlayerOneStep(struct PlayerInfo *player,
8292                           int dx, int dy, int real_dx, int real_dy)
8293 {
8294 #if 0
8295   static int trigger_sides[4][2] =
8296   {
8297     /* enter side        leave side */
8298     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
8299     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
8300     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
8301     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
8302   };
8303   int move_direction = (dx == -1 ? MV_LEFT :
8304                         dx == +1 ? MV_RIGHT :
8305                         dy == -1 ? MV_UP :
8306                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8307   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8308   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8309 #endif
8310   int jx = player->jx, jy = player->jy;
8311   int new_jx = jx + dx, new_jy = jy + dy;
8312   int element;
8313   int can_move;
8314
8315   if (!player->active || (!dx && !dy))
8316     return MF_NO_ACTION;
8317
8318   player->MovDir = (dx < 0 ? MV_LEFT :
8319                     dx > 0 ? MV_RIGHT :
8320                     dy < 0 ? MV_UP :
8321                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
8322
8323   if (!IN_LEV_FIELD(new_jx, new_jy))
8324     return MF_NO_ACTION;
8325
8326   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8327     return MF_NO_ACTION;
8328
8329 #if 0
8330   element = MovingOrBlocked2Element(new_jx, new_jy);
8331 #else
8332   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8333 #endif
8334
8335   if (DONT_RUN_INTO(element))
8336   {
8337     if (element == EL_ACID && dx == 0 && dy == 1)
8338     {
8339       SplashAcid(new_jx, new_jy);
8340       Feld[jx][jy] = EL_PLAYER_1;
8341       InitMovingField(jx, jy, MV_DOWN);
8342       Store[jx][jy] = EL_ACID;
8343       ContinueMoving(jx, jy);
8344       BuryHero(player);
8345     }
8346     else
8347       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8348
8349     return MF_MOVING;
8350   }
8351
8352   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8353   if (can_move != MF_MOVING)
8354     return can_move;
8355
8356   /* check if DigField() has caused relocation of the player */
8357   if (player->jx != jx || player->jy != jy)
8358     return MF_NO_ACTION;
8359
8360   StorePlayer[jx][jy] = 0;
8361   player->last_jx = jx;
8362   player->last_jy = jy;
8363   player->jx = new_jx;
8364   player->jy = new_jy;
8365   StorePlayer[new_jx][new_jy] = player->element_nr;
8366
8367   player->MovPos =
8368     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8369
8370   player->step_counter++;
8371
8372   player->drop_delay = 0;
8373
8374   PlayerVisit[jx][jy] = FrameCounter;
8375
8376   ScrollPlayer(player, SCROLL_INIT);
8377
8378 #if 0
8379   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8380   {
8381     CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8382                                     leave_side);
8383     CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8384   }
8385
8386   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8387   {
8388     CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8389                                     CE_OTHER_GETS_ENTERED, enter_side);
8390     CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8391                            CE_ENTERED_BY_PLAYER, enter_side);
8392   }
8393 #endif
8394
8395   return MF_MOVING;
8396 }
8397
8398 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8399 {
8400   int jx = player->jx, jy = player->jy;
8401   int old_jx = jx, old_jy = jy;
8402   int moved = MF_NO_ACTION;
8403
8404 #if 1
8405   if (!player->active)
8406     return FALSE;
8407
8408   if (!dx && !dy)
8409   {
8410     if (player->MovPos == 0)
8411     {
8412       player->is_moving = FALSE;
8413       player->is_digging = FALSE;
8414       player->is_collecting = FALSE;
8415       player->is_snapping = FALSE;
8416       player->is_pushing = FALSE;
8417     }
8418
8419     return FALSE;
8420   }
8421 #else
8422   if (!player->active || (!dx && !dy))
8423     return FALSE;
8424 #endif
8425
8426 #if 0
8427   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8428       !tape.playing)
8429     return FALSE;
8430 #else
8431
8432 #if 1
8433   if (!FrameReached(&player->move_delay, player->move_delay_value))
8434     return FALSE;
8435 #else
8436   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8437       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8438     return FALSE;
8439 #endif
8440
8441 #endif
8442
8443   /* remove the last programmed player action */
8444   player->programmed_action = 0;
8445
8446   if (player->MovPos)
8447   {
8448     /* should only happen if pre-1.2 tape recordings are played */
8449     /* this is only for backward compatibility */
8450
8451     int original_move_delay_value = player->move_delay_value;
8452
8453 #if DEBUG
8454     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8455            tape.counter);
8456 #endif
8457
8458     /* scroll remaining steps with finest movement resolution */
8459     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8460
8461     while (player->MovPos)
8462     {
8463       ScrollPlayer(player, SCROLL_GO_ON);
8464       ScrollScreen(NULL, SCROLL_GO_ON);
8465       FrameCounter++;
8466       DrawAllPlayers();
8467       BackToFront();
8468     }
8469
8470     player->move_delay_value = original_move_delay_value;
8471   }
8472
8473   if (player->last_move_dir & MV_HORIZONTAL)
8474   {
8475     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8476       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8477   }
8478   else
8479   {
8480     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8481       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8482   }
8483
8484   jx = player->jx;
8485   jy = player->jy;
8486
8487   if (moved & MF_MOVING && !ScreenMovPos &&
8488       (player == local_player || !options.network))
8489   {
8490     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8491     int offset = (setup.scroll_delay ? 3 : 0);
8492
8493     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8494     {
8495       /* actual player has left the screen -- scroll in that direction */
8496       if (jx != old_jx)         /* player has moved horizontally */
8497         scroll_x += (jx - old_jx);
8498       else                      /* player has moved vertically */
8499         scroll_y += (jy - old_jy);
8500     }
8501     else
8502     {
8503       if (jx != old_jx)         /* player has moved horizontally */
8504       {
8505         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8506             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8507           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8508
8509         /* don't scroll over playfield boundaries */
8510         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8511           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8512
8513         /* don't scroll more than one field at a time */
8514         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8515
8516         /* don't scroll against the player's moving direction */
8517         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8518             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8519           scroll_x = old_scroll_x;
8520       }
8521       else                      /* player has moved vertically */
8522       {
8523         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8524             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8525           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8526
8527         /* don't scroll over playfield boundaries */
8528         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8529           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8530
8531         /* don't scroll more than one field at a time */
8532         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8533
8534         /* don't scroll against the player's moving direction */
8535         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8536             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8537           scroll_y = old_scroll_y;
8538       }
8539     }
8540
8541     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8542     {
8543       if (!options.network && !AllPlayersInVisibleScreen())
8544       {
8545         scroll_x = old_scroll_x;
8546         scroll_y = old_scroll_y;
8547       }
8548       else
8549       {
8550         ScrollScreen(player, SCROLL_INIT);
8551         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8552       }
8553     }
8554   }
8555
8556 #if 0
8557 #if 1
8558   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8559 #else
8560   if (!(moved & MF_MOVING) && !player->is_pushing)
8561     player->Frame = 0;
8562 #endif
8563 #endif
8564
8565   player->StepFrame = 0;
8566
8567   if (moved & MF_MOVING)
8568   {
8569     if (old_jx != jx && old_jy == jy)
8570       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8571     else if (old_jx == jx && old_jy != jy)
8572       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8573
8574     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8575
8576     player->last_move_dir = player->MovDir;
8577     player->is_moving = TRUE;
8578 #if 1
8579     player->is_snapping = FALSE;
8580 #endif
8581
8582 #if 1
8583     player->is_switching = FALSE;
8584 #endif
8585
8586     player->is_dropping = FALSE;
8587
8588
8589 #if 1
8590     {
8591       static int trigger_sides[4][2] =
8592       {
8593         /* enter side           leave side */
8594         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8595         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8596         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8597         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8598       };
8599       int move_direction = player->MovDir;
8600       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8601       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8602
8603 #if 1
8604       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8605       {
8606         CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8607                                           CE_OTHER_GETS_LEFT,
8608                                           player->index_bit, leave_side);
8609         CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8610                                  CE_LEFT_BY_PLAYER,
8611                                  player->index_bit, leave_side);
8612       }
8613
8614       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8615       {
8616         CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8617                                           CE_OTHER_GETS_ENTERED,
8618                                           player->index_bit, enter_side);
8619         CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8620                                  player->index_bit, enter_side);
8621       }
8622 #endif
8623
8624     }
8625 #endif
8626
8627
8628   }
8629   else
8630   {
8631     CheckGravityMovementWhenNotMoving(player);
8632
8633     /*
8634     player->last_move_dir = MV_NO_MOVING;
8635     */
8636     player->is_moving = FALSE;
8637   }
8638
8639   if (game.engine_version < VERSION_IDENT(3,0,7,0))
8640   {
8641     TestIfHeroTouchesBadThing(jx, jy);
8642     TestIfPlayerTouchesCustomElement(jx, jy);
8643   }
8644
8645   if (!player->active)
8646     RemoveHero(player);
8647
8648   return moved;
8649 }
8650
8651 void ScrollPlayer(struct PlayerInfo *player, int mode)
8652 {
8653   int jx = player->jx, jy = player->jy;
8654   int last_jx = player->last_jx, last_jy = player->last_jy;
8655   int move_stepsize = TILEX / player->move_delay_value;
8656
8657   if (!player->active || !player->MovPos)
8658     return;
8659
8660   if (mode == SCROLL_INIT)
8661   {
8662     player->actual_frame_counter = FrameCounter;
8663     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8664
8665     if (Feld[last_jx][last_jy] == EL_EMPTY)
8666       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8667
8668 #if 0
8669     DrawPlayer(player);
8670 #endif
8671
8672     return;
8673   }
8674   else if (!FrameReached(&player->actual_frame_counter, 1))
8675     return;
8676
8677   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8678   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8679
8680   if (!player->block_last_field &&
8681       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8682     Feld[last_jx][last_jy] = EL_EMPTY;
8683
8684   /* before DrawPlayer() to draw correct player graphic for this case */
8685   if (player->MovPos == 0)
8686     CheckGravityMovement(player);
8687
8688 #if 0
8689   DrawPlayer(player);   /* needed here only to cleanup last field */
8690 #endif
8691
8692   if (player->MovPos == 0)      /* player reached destination field */
8693   {
8694 #if 1
8695     if (player->move_delay_reset_counter > 0)
8696     {
8697       player->move_delay_reset_counter--;
8698
8699       if (player->move_delay_reset_counter == 0)
8700       {
8701         /* continue with normal speed after quickly moving through gate */
8702         HALVE_PLAYER_SPEED(player);
8703
8704         /* be able to make the next move without delay */
8705         player->move_delay = 0;
8706       }
8707     }
8708 #else
8709     if (IS_PASSABLE(Feld[last_jx][last_jy]))
8710     {
8711       /* continue with normal speed after quickly moving through gate */
8712       HALVE_PLAYER_SPEED(player);
8713
8714       /* be able to make the next move without delay */
8715       player->move_delay = 0;
8716     }
8717 #endif
8718
8719     if (player->block_last_field &&
8720         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8721       Feld[last_jx][last_jy] = EL_EMPTY;
8722
8723     player->last_jx = jx;
8724     player->last_jy = jy;
8725
8726     if (Feld[jx][jy] == EL_EXIT_OPEN ||
8727         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8728         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
8729     {
8730       DrawPlayer(player);       /* needed here only to cleanup last field */
8731       RemoveHero(player);
8732
8733       if (local_player->friends_still_needed == 0 ||
8734           IS_SP_ELEMENT(Feld[jx][jy]))
8735         player->LevelSolved = player->GameOver = TRUE;
8736     }
8737
8738 #if 0
8739     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
8740     {
8741       static int trigger_sides[4][2] =
8742       {
8743         /* enter side           leave side */
8744         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8745         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8746         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8747         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8748       };
8749       int move_direction = player->MovDir;
8750       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8751       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8752       int old_jx = last_jx;
8753       int old_jy = last_jy;
8754
8755 #if 1
8756       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8757       {
8758         CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8759                                           CE_OTHER_GETS_LEFT,
8760                                           player->index_bit, leave_side);
8761         CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8762                                  CE_LEFT_BY_PLAYER,
8763                                  player->index_bit, leave_side);
8764       }
8765
8766       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8767       {
8768         CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8769                                           CE_OTHER_GETS_ENTERED,
8770                                           player->index_bit, enter_side);
8771         CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8772                                  player->index_bit, enter_side);
8773       }
8774 #endif
8775
8776     }
8777 #endif
8778
8779     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8780     {
8781       TestIfHeroTouchesBadThing(jx, jy);
8782       TestIfPlayerTouchesCustomElement(jx, jy);
8783 #if 1
8784       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
8785 #endif
8786
8787       if (!player->active)
8788         RemoveHero(player);
8789     }
8790
8791     if (level.use_step_counter)
8792     {
8793       int i;
8794
8795       TimePlayed++;
8796
8797       for (i = 0; i < MAX_PLAYERS; i++)
8798       {
8799         struct PlayerInfo *player = &stored_player[i];
8800
8801         if (SHIELD_ON(player))
8802         {
8803           player->shield_normal_time_left--;
8804
8805           if (player->shield_deadly_time_left > 0)
8806             player->shield_deadly_time_left--;
8807         }
8808       }
8809
8810       if (TimeLeft > 0)
8811       {
8812         TimeLeft--;
8813
8814         if (TimeLeft <= 10 && setup.time_limit)
8815           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8816
8817         DrawGameValue_Time(TimeLeft);
8818
8819         if (!TimeLeft && setup.time_limit)
8820           for (i = 0; i < MAX_PLAYERS; i++)
8821             KillHero(&stored_player[i]);
8822       }
8823       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8824         DrawGameValue_Time(TimePlayed);
8825     }
8826
8827     if (tape.single_step && tape.recording && !tape.pausing &&
8828         !player->programmed_action)
8829       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8830   }
8831 }
8832
8833 void ScrollScreen(struct PlayerInfo *player, int mode)
8834 {
8835   static unsigned long screen_frame_counter = 0;
8836
8837   if (mode == SCROLL_INIT)
8838   {
8839     /* set scrolling step size according to actual player's moving speed */
8840     ScrollStepSize = TILEX / player->move_delay_value;
8841
8842     screen_frame_counter = FrameCounter;
8843     ScreenMovDir = player->MovDir;
8844     ScreenMovPos = player->MovPos;
8845     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8846     return;
8847   }
8848   else if (!FrameReached(&screen_frame_counter, 1))
8849     return;
8850
8851   if (ScreenMovPos)
8852   {
8853     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8854     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8855     redraw_mask |= REDRAW_FIELD;
8856   }
8857   else
8858     ScreenMovDir = MV_NO_MOVING;
8859 }
8860
8861 void TestIfPlayerTouchesCustomElement(int x, int y)
8862 {
8863   static int xy[4][2] =
8864   {
8865     { 0, -1 },
8866     { -1, 0 },
8867     { +1, 0 },
8868     { 0, +1 }
8869   };
8870   static int trigger_sides[4][2] =
8871   {
8872     /* center side       border side */
8873     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8874     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8875     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8876     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8877   };
8878   static int touch_dir[4] =
8879   {
8880     MV_LEFT | MV_RIGHT,
8881     MV_UP   | MV_DOWN,
8882     MV_UP   | MV_DOWN,
8883     MV_LEFT | MV_RIGHT
8884   };
8885   int center_element = Feld[x][y];      /* should always be non-moving! */
8886   int i;
8887
8888   for (i = 0; i < NUM_DIRECTIONS; i++)
8889   {
8890     int xx = x + xy[i][0];
8891     int yy = y + xy[i][1];
8892     int center_side = trigger_sides[i][0];
8893     int border_side = trigger_sides[i][1];
8894     int border_element;
8895
8896     if (!IN_LEV_FIELD(xx, yy))
8897       continue;
8898
8899     if (IS_PLAYER(x, y))
8900     {
8901       struct PlayerInfo *player = PLAYERINFO(x, y);
8902
8903       if (game.engine_version < VERSION_IDENT(3,0,7,0))
8904         border_element = Feld[xx][yy];          /* may be moving! */
8905       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8906         border_element = Feld[xx][yy];
8907       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
8908         border_element = MovingOrBlocked2Element(xx, yy);
8909       else
8910         continue;               /* center and border element do not touch */
8911
8912       CheckTriggeredElementChangePlayer(xx, yy, border_element,
8913                                         CE_OTHER_GETS_TOUCHED,
8914                                         player->index_bit, border_side);
8915       CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8916                                player->index_bit, border_side);
8917     }
8918     else if (IS_PLAYER(xx, yy))
8919     {
8920       struct PlayerInfo *player = PLAYERINFO(xx, yy);
8921
8922       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8923       {
8924         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8925           continue;             /* center and border element do not touch */
8926       }
8927
8928       CheckTriggeredElementChangePlayer(x, y, center_element,
8929                                         CE_OTHER_GETS_TOUCHED,
8930                                         player->index_bit, center_side);
8931       CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8932                                player->index_bit, center_side);
8933
8934       break;
8935     }
8936   }
8937 }
8938
8939 void TestIfElementTouchesCustomElement(int x, int y)
8940 {
8941   static int xy[4][2] =
8942   {
8943     { 0, -1 },
8944     { -1, 0 },
8945     { +1, 0 },
8946     { 0, +1 }
8947   };
8948   static int trigger_sides[4][2] =
8949   {
8950     /* center side      border side */
8951     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8952     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8953     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8954     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8955   };
8956   static int touch_dir[4] =
8957   {
8958     MV_LEFT | MV_RIGHT,
8959     MV_UP   | MV_DOWN,
8960     MV_UP   | MV_DOWN,
8961     MV_LEFT | MV_RIGHT
8962   };
8963   boolean change_center_element = FALSE;
8964   int center_element_change_page = 0;
8965   int center_element = Feld[x][y];      /* should always be non-moving! */
8966   int i, j;
8967
8968   for (i = 0; i < NUM_DIRECTIONS; i++)
8969   {
8970     int xx = x + xy[i][0];
8971     int yy = y + xy[i][1];
8972     int center_side = trigger_sides[i][0];
8973     int border_side = trigger_sides[i][1];
8974     int border_element;
8975
8976     if (!IN_LEV_FIELD(xx, yy))
8977       continue;
8978
8979     if (game.engine_version < VERSION_IDENT(3,0,7,0))
8980       border_element = Feld[xx][yy];    /* may be moving! */
8981     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8982       border_element = Feld[xx][yy];
8983     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
8984       border_element = MovingOrBlocked2Element(xx, yy);
8985     else
8986       continue;                 /* center and border element do not touch */
8987
8988     /* check for change of center element (but change it only once) */
8989     if (IS_CUSTOM_ELEMENT(center_element) &&
8990         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8991         !change_center_element)
8992     {
8993       for (j = 0; j < element_info[center_element].num_change_pages; j++)
8994       {
8995         struct ElementChangeInfo *change =
8996           &element_info[center_element].change_page[j];
8997
8998         if (change->can_change &&
8999             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9000             change->trigger_side & border_side &&
9001 #if 1
9002             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9003 #else
9004             change->trigger_element == border_element
9005 #endif
9006             )
9007         {
9008           change_center_element = TRUE;
9009           center_element_change_page = j;
9010
9011           break;
9012         }
9013       }
9014     }
9015
9016     /* check for change of border element */
9017     if (IS_CUSTOM_ELEMENT(border_element) &&
9018         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9019     {
9020       for (j = 0; j < element_info[border_element].num_change_pages; j++)
9021       {
9022         struct ElementChangeInfo *change =
9023           &element_info[border_element].change_page[j];
9024
9025         if (change->can_change &&
9026             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9027             change->trigger_side & center_side &&
9028 #if 1
9029             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9030 #else
9031             change->trigger_element == center_element
9032 #endif
9033             )
9034         {
9035           CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
9036                                  j);
9037           break;
9038         }
9039       }
9040     }
9041   }
9042
9043   if (change_center_element)
9044     CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
9045                            center_element_change_page);
9046 }
9047
9048 void TestIfElementHitsCustomElement(int x, int y, int direction)
9049 {
9050   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9051   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9052   int hitx = x + dx, hity = y + dy;
9053   int hitting_element = Feld[x][y];
9054 #if 0
9055   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9056                         !IS_FREE(hitx, hity) &&
9057                         (!IS_MOVING(hitx, hity) ||
9058                          MovDir[hitx][hity] != direction ||
9059                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9060 #endif
9061
9062   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9063     return;
9064
9065 #if 0
9066   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9067     return;
9068 #endif
9069
9070   CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
9071                          direction);
9072
9073   if (IN_LEV_FIELD(hitx, hity))
9074   {
9075     int opposite_direction = MV_DIR_OPPOSITE(direction);
9076     int hitting_side = direction;
9077     int touched_side = opposite_direction;
9078     int touched_element = MovingOrBlocked2Element(hitx, hity);
9079 #if 1
9080     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9081                           MovDir[hitx][hity] != direction ||
9082                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9083
9084     object_hit = TRUE;
9085 #endif
9086
9087     if (object_hit)
9088     {
9089       int i;
9090
9091       CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
9092                              opposite_direction);
9093
9094       if (IS_CUSTOM_ELEMENT(hitting_element) &&
9095           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9096       {
9097         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9098         {
9099           struct ElementChangeInfo *change =
9100             &element_info[hitting_element].change_page[i];
9101
9102           if (change->can_change &&
9103               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9104               change->trigger_side & touched_side &&
9105           
9106 #if 1
9107               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9108 #else
9109               change->trigger_element == touched_element
9110 #endif
9111               )
9112           {
9113             CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
9114                                    i);
9115             break;
9116           }
9117         }
9118       }
9119
9120       if (IS_CUSTOM_ELEMENT(touched_element) &&
9121           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9122       {
9123         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9124         {
9125           struct ElementChangeInfo *change =
9126             &element_info[touched_element].change_page[i];
9127
9128           if (change->can_change &&
9129               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9130               change->trigger_side & hitting_side &&
9131 #if 1
9132               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9133 #else
9134               change->trigger_element == hitting_element
9135 #endif
9136               )
9137           {
9138             CheckElementChangePage(hitx, hity, touched_element,
9139                                    CE_OTHER_GETS_HIT, i);
9140             break;
9141           }
9142         }
9143       }
9144     }
9145   }
9146 }
9147
9148 #if 0
9149 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9150 {
9151   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9152   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9153   int hitx = x + dx, hity = y + dy;
9154   int hitting_element = Feld[x][y];
9155 #if 0
9156   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9157                         !IS_FREE(hitx, hity) &&
9158                         (!IS_MOVING(hitx, hity) ||
9159                          MovDir[hitx][hity] != direction ||
9160                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9161 #endif
9162
9163   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9164     return;
9165
9166 #if 0
9167   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9168     return;
9169 #endif
9170
9171   CheckElementChangeSide(x, y, hitting_element, EP_CAN_SMASH_EVERYTHING,
9172                          direction);
9173
9174   if (IN_LEV_FIELD(hitx, hity))
9175   {
9176     int opposite_direction = MV_DIR_OPPOSITE(direction);
9177     int hitting_side = direction;
9178     int touched_side = opposite_direction;
9179     int touched_element = MovingOrBlocked2Element(hitx, hity);
9180 #if 1
9181     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9182                           MovDir[hitx][hity] != direction ||
9183                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9184
9185     object_hit = TRUE;
9186 #endif
9187
9188     if (object_hit)
9189     {
9190       int i;
9191
9192       CheckElementChangeSide(hitx, hity, touched_element,
9193                              CE_SMASHED_BY_SOMETHING, opposite_direction);
9194
9195       if (IS_CUSTOM_ELEMENT(hitting_element) &&
9196           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9197       {
9198         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9199         {
9200           struct ElementChangeInfo *change =
9201             &element_info[hitting_element].change_page[i];
9202
9203           if (change->can_change &&
9204               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9205               change->trigger_side & touched_side &&
9206           
9207 #if 1
9208               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9209 #else
9210               change->trigger_element == touched_element
9211 #endif
9212               )
9213           {
9214             CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_SMASHING,
9215                                    i);
9216             break;
9217           }
9218         }
9219       }
9220
9221       if (IS_CUSTOM_ELEMENT(touched_element) &&
9222           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9223       {
9224         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9225         {
9226           struct ElementChangeInfo *change =
9227             &element_info[touched_element].change_page[i];
9228
9229           if (change->can_change &&
9230               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9231               change->trigger_side & hitting_side &&
9232 #if 1
9233               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9234 #else
9235               change->trigger_element == hitting_element
9236 #endif
9237               )
9238           {
9239             CheckElementChangePage(hitx, hity, touched_element,
9240                                    CE_OTHER_GETS_SMASHED, i);
9241             break;
9242           }
9243         }
9244       }
9245     }
9246   }
9247 }
9248 #endif
9249
9250 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9251 {
9252   int i, kill_x = -1, kill_y = -1;
9253   static int test_xy[4][2] =
9254   {
9255     { 0, -1 },
9256     { -1, 0 },
9257     { +1, 0 },
9258     { 0, +1 }
9259   };
9260   static int test_dir[4] =
9261   {
9262     MV_UP,
9263     MV_LEFT,
9264     MV_RIGHT,
9265     MV_DOWN
9266   };
9267
9268   for (i = 0; i < NUM_DIRECTIONS; i++)
9269   {
9270     int test_x, test_y, test_move_dir, test_element;
9271
9272     test_x = good_x + test_xy[i][0];
9273     test_y = good_y + test_xy[i][1];
9274     if (!IN_LEV_FIELD(test_x, test_y))
9275       continue;
9276
9277     test_move_dir =
9278       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9279
9280 #if 0
9281     test_element = Feld[test_x][test_y];
9282 #else
9283     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9284 #endif
9285
9286     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9287        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9288     */
9289     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9290         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
9291     {
9292       kill_x = test_x;
9293       kill_y = test_y;
9294       break;
9295     }
9296   }
9297
9298   if (kill_x != -1 || kill_y != -1)
9299   {
9300     if (IS_PLAYER(good_x, good_y))
9301     {
9302       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9303
9304       if (player->shield_deadly_time_left > 0)
9305         Bang(kill_x, kill_y);
9306       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9307         KillHero(player);
9308     }
9309     else
9310       Bang(good_x, good_y);
9311   }
9312 }
9313
9314 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9315 {
9316   int i, kill_x = -1, kill_y = -1;
9317   int bad_element = Feld[bad_x][bad_y];
9318   static int test_xy[4][2] =
9319   {
9320     { 0, -1 },
9321     { -1, 0 },
9322     { +1, 0 },
9323     { 0, +1 }
9324   };
9325   static int touch_dir[4] =
9326   {
9327     MV_LEFT | MV_RIGHT,
9328     MV_UP   | MV_DOWN,
9329     MV_UP   | MV_DOWN,
9330     MV_LEFT | MV_RIGHT
9331   };
9332   static int test_dir[4] =
9333   {
9334     MV_UP,
9335     MV_LEFT,
9336     MV_RIGHT,
9337     MV_DOWN
9338   };
9339
9340   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
9341     return;
9342
9343   for (i = 0; i < NUM_DIRECTIONS; i++)
9344   {
9345     int test_x, test_y, test_move_dir, test_element;
9346
9347     test_x = bad_x + test_xy[i][0];
9348     test_y = bad_y + test_xy[i][1];
9349     if (!IN_LEV_FIELD(test_x, test_y))
9350       continue;
9351
9352     test_move_dir =
9353       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9354
9355     test_element = Feld[test_x][test_y];
9356
9357     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9358        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9359     */
9360     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
9361         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
9362     {
9363       /* good thing is player or penguin that does not move away */
9364       if (IS_PLAYER(test_x, test_y))
9365       {
9366         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9367
9368         if (bad_element == EL_ROBOT && player->is_moving)
9369           continue;     /* robot does not kill player if he is moving */
9370
9371         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9372         {
9373           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9374             continue;           /* center and border element do not touch */
9375         }
9376
9377         kill_x = test_x;
9378         kill_y = test_y;
9379         break;
9380       }
9381       else if (test_element == EL_PENGUIN)
9382       {
9383         kill_x = test_x;
9384         kill_y = test_y;
9385         break;
9386       }
9387     }
9388   }
9389
9390   if (kill_x != -1 || kill_y != -1)
9391   {
9392     if (IS_PLAYER(kill_x, kill_y))
9393     {
9394       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9395
9396       if (player->shield_deadly_time_left > 0)
9397         Bang(bad_x, bad_y);
9398       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9399         KillHero(player);
9400     }
9401     else
9402       Bang(kill_x, kill_y);
9403   }
9404 }
9405
9406 void TestIfHeroTouchesBadThing(int x, int y)
9407 {
9408   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9409 }
9410
9411 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9412 {
9413   TestIfGoodThingHitsBadThing(x, y, move_dir);
9414 }
9415
9416 void TestIfBadThingTouchesHero(int x, int y)
9417 {
9418   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9419 }
9420
9421 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9422 {
9423   TestIfBadThingHitsGoodThing(x, y, move_dir);
9424 }
9425
9426 void TestIfFriendTouchesBadThing(int x, int y)
9427 {
9428   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9429 }
9430
9431 void TestIfBadThingTouchesFriend(int x, int y)
9432 {
9433   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9434 }
9435
9436 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9437 {
9438   int i, kill_x = bad_x, kill_y = bad_y;
9439   static int xy[4][2] =
9440   {
9441     { 0, -1 },
9442     { -1, 0 },
9443     { +1, 0 },
9444     { 0, +1 }
9445   };
9446
9447   for (i = 0; i < NUM_DIRECTIONS; i++)
9448   {
9449     int x, y, element;
9450
9451     x = bad_x + xy[i][0];
9452     y = bad_y + xy[i][1];
9453     if (!IN_LEV_FIELD(x, y))
9454       continue;
9455
9456     element = Feld[x][y];
9457     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9458         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9459     {
9460       kill_x = x;
9461       kill_y = y;
9462       break;
9463     }
9464   }
9465
9466   if (kill_x != bad_x || kill_y != bad_y)
9467     Bang(bad_x, bad_y);
9468 }
9469
9470 void KillHero(struct PlayerInfo *player)
9471 {
9472   int jx = player->jx, jy = player->jy;
9473
9474   if (!player->active)
9475     return;
9476
9477   /* remove accessible field at the player's position */
9478   Feld[jx][jy] = EL_EMPTY;
9479
9480   /* deactivate shield (else Bang()/Explode() would not work right) */
9481   player->shield_normal_time_left = 0;
9482   player->shield_deadly_time_left = 0;
9483
9484   Bang(jx, jy);
9485   BuryHero(player);
9486 }
9487
9488 static void KillHeroUnlessEnemyProtected(int x, int y)
9489 {
9490   if (!PLAYER_ENEMY_PROTECTED(x, y))
9491     KillHero(PLAYERINFO(x, y));
9492 }
9493
9494 static void KillHeroUnlessExplosionProtected(int x, int y)
9495 {
9496   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9497     KillHero(PLAYERINFO(x, y));
9498 }
9499
9500 void BuryHero(struct PlayerInfo *player)
9501 {
9502   int jx = player->jx, jy = player->jy;
9503
9504   if (!player->active)
9505     return;
9506
9507 #if 1
9508   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9509 #else
9510   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9511 #endif
9512   PlayLevelSound(jx, jy, SND_GAME_LOSING);
9513
9514   player->GameOver = TRUE;
9515   RemoveHero(player);
9516 }
9517
9518 void RemoveHero(struct PlayerInfo *player)
9519 {
9520   int jx = player->jx, jy = player->jy;
9521   int i, found = FALSE;
9522
9523   player->present = FALSE;
9524   player->active = FALSE;
9525
9526   if (!ExplodeField[jx][jy])
9527     StorePlayer[jx][jy] = 0;
9528
9529   for (i = 0; i < MAX_PLAYERS; i++)
9530     if (stored_player[i].active)
9531       found = TRUE;
9532
9533   if (!found)
9534     AllPlayersGone = TRUE;
9535
9536   ExitX = ZX = jx;
9537   ExitY = ZY = jy;
9538 }
9539
9540 /*
9541   =============================================================================
9542   checkDiagonalPushing()
9543   -----------------------------------------------------------------------------
9544   check if diagonal input device direction results in pushing of object
9545   (by checking if the alternative direction is walkable, diggable, ...)
9546   =============================================================================
9547 */
9548
9549 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9550                                     int x, int y, int real_dx, int real_dy)
9551 {
9552   int jx, jy, dx, dy, xx, yy;
9553
9554   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
9555     return TRUE;
9556
9557   /* diagonal direction: check alternative direction */
9558   jx = player->jx;
9559   jy = player->jy;
9560   dx = x - jx;
9561   dy = y - jy;
9562   xx = jx + (dx == 0 ? real_dx : 0);
9563   yy = jy + (dy == 0 ? real_dy : 0);
9564
9565   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9566 }
9567
9568 /*
9569   =============================================================================
9570   DigField()
9571   -----------------------------------------------------------------------------
9572   x, y:                 field next to player (non-diagonal) to try to dig to
9573   real_dx, real_dy:     direction as read from input device (can be diagonal)
9574   =============================================================================
9575 */
9576
9577 int DigField(struct PlayerInfo *player,
9578              int oldx, int oldy, int x, int y,
9579              int real_dx, int real_dy, int mode)
9580 {
9581   static int trigger_sides[4] =
9582   {
9583     CH_SIDE_RIGHT,      /* moving left  */
9584     CH_SIDE_LEFT,       /* moving right */
9585     CH_SIDE_BOTTOM,     /* moving up    */
9586     CH_SIDE_TOP,        /* moving down  */
9587   };
9588 #if 0
9589   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9590 #endif
9591   int jx = oldx, jy = oldy;
9592   int dx = x - jx, dy = y - jy;
9593   int nextx = x + dx, nexty = y + dy;
9594   int move_direction = (dx == -1 ? MV_LEFT :
9595                         dx == +1 ? MV_RIGHT :
9596                         dy == -1 ? MV_UP :
9597                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9598   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9599   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9600   int old_element = Feld[jx][jy];
9601   int element;
9602
9603   if (player->MovPos == 0)
9604   {
9605     player->is_digging = FALSE;
9606     player->is_collecting = FALSE;
9607   }
9608
9609   if (player->MovPos == 0)      /* last pushing move finished */
9610     player->is_pushing = FALSE;
9611
9612   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
9613   {
9614     player->is_switching = FALSE;
9615     player->push_delay = 0;
9616
9617     return MF_NO_ACTION;
9618   }
9619
9620   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9621     return MF_NO_ACTION;
9622
9623 #if 0
9624
9625 #if 0
9626   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9627 #else
9628   if (IS_TUBE(Feld[jx][jy]) ||
9629       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9630 #endif
9631   {
9632     int i = 0;
9633     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9634     int tube_leave_directions[][2] =
9635     {
9636       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9637       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
9638       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
9639       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
9640       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
9641       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
9642       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
9643       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
9644       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
9645       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
9646       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
9647       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9648     };
9649
9650     while (tube_leave_directions[i][0] != tube_element)
9651     {
9652       i++;
9653       if (tube_leave_directions[i][0] == -1)    /* should not happen */
9654         break;
9655     }
9656
9657     if (!(tube_leave_directions[i][1] & move_direction))
9658       return MF_NO_ACTION;      /* tube has no opening in this direction */
9659   }
9660
9661 #else
9662
9663   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9664     old_element = Back[jx][jy];
9665
9666 #endif
9667
9668   if (IS_WALKABLE(old_element) &&
9669       !(element_info[old_element].access_direction & move_direction))
9670     return MF_NO_ACTION;        /* field has no opening in this direction */
9671
9672   element = Feld[x][y];
9673
9674   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9675       game.engine_version >= VERSION_IDENT(2,2,0,0))
9676     return MF_NO_ACTION;
9677
9678   switch (element)
9679   {
9680     case EL_SP_PORT_LEFT:
9681     case EL_SP_PORT_RIGHT:
9682     case EL_SP_PORT_UP:
9683     case EL_SP_PORT_DOWN:
9684     case EL_SP_PORT_HORIZONTAL:
9685     case EL_SP_PORT_VERTICAL:
9686     case EL_SP_PORT_ANY:
9687     case EL_SP_GRAVITY_PORT_LEFT:
9688     case EL_SP_GRAVITY_PORT_RIGHT:
9689     case EL_SP_GRAVITY_PORT_UP:
9690     case EL_SP_GRAVITY_PORT_DOWN:
9691 #if 1
9692       if (!canEnterSupaplexPort(x, y, dx, dy))
9693         return MF_NO_ACTION;
9694 #else
9695       if ((dx == -1 &&
9696            element != EL_SP_PORT_LEFT &&
9697            element != EL_SP_GRAVITY_PORT_LEFT &&
9698            element != EL_SP_PORT_HORIZONTAL &&
9699            element != EL_SP_PORT_ANY) ||
9700           (dx == +1 &&
9701            element != EL_SP_PORT_RIGHT &&
9702            element != EL_SP_GRAVITY_PORT_RIGHT &&
9703            element != EL_SP_PORT_HORIZONTAL &&
9704            element != EL_SP_PORT_ANY) ||
9705           (dy == -1 &&
9706            element != EL_SP_PORT_UP &&
9707            element != EL_SP_GRAVITY_PORT_UP &&
9708            element != EL_SP_PORT_VERTICAL &&
9709            element != EL_SP_PORT_ANY) ||
9710           (dy == +1 &&
9711            element != EL_SP_PORT_DOWN &&
9712            element != EL_SP_GRAVITY_PORT_DOWN &&
9713            element != EL_SP_PORT_VERTICAL &&
9714            element != EL_SP_PORT_ANY) ||
9715           !IN_LEV_FIELD(nextx, nexty) ||
9716           !IS_FREE(nextx, nexty))
9717         return MF_NO_ACTION;
9718 #endif
9719
9720       if (element == EL_SP_GRAVITY_PORT_LEFT ||
9721           element == EL_SP_GRAVITY_PORT_RIGHT ||
9722           element == EL_SP_GRAVITY_PORT_UP ||
9723           element == EL_SP_GRAVITY_PORT_DOWN)
9724         game.gravity = !game.gravity;
9725
9726       /* automatically move to the next field with double speed */
9727       player->programmed_action = move_direction;
9728 #if 1
9729       if (player->move_delay_reset_counter == 0)
9730       {
9731         player->move_delay_reset_counter = 2;   /* two double speed steps */
9732
9733         DOUBLE_PLAYER_SPEED(player);
9734       }
9735 #else
9736       player->move_delay_reset_counter = 2;
9737
9738       DOUBLE_PLAYER_SPEED(player);
9739 #endif
9740
9741 #if 0
9742       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
9743 #endif
9744
9745       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9746       break;
9747
9748 #if 0
9749     case EL_TUBE_ANY:
9750     case EL_TUBE_VERTICAL:
9751     case EL_TUBE_HORIZONTAL:
9752     case EL_TUBE_VERTICAL_LEFT:
9753     case EL_TUBE_VERTICAL_RIGHT:
9754     case EL_TUBE_HORIZONTAL_UP:
9755     case EL_TUBE_HORIZONTAL_DOWN:
9756     case EL_TUBE_LEFT_UP:
9757     case EL_TUBE_LEFT_DOWN:
9758     case EL_TUBE_RIGHT_UP:
9759     case EL_TUBE_RIGHT_DOWN:
9760       {
9761         int i = 0;
9762         int tube_enter_directions[][2] =
9763         {
9764           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9765           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
9766           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
9767           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
9768           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
9769           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
9770           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
9771           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
9772           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
9773           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
9774           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
9775           { -1,                         MV_NO_MOVING                         }
9776         };
9777
9778         while (tube_enter_directions[i][0] != element)
9779         {
9780           i++;
9781           if (tube_enter_directions[i][0] == -1)        /* should not happen */
9782             break;
9783         }
9784
9785         if (!(tube_enter_directions[i][1] & move_direction))
9786           return MF_NO_ACTION;  /* tube has no opening in this direction */
9787
9788         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9789       }
9790       break;
9791 #endif
9792
9793     default:
9794
9795       if (IS_WALKABLE(element))
9796       {
9797         int sound_action = ACTION_WALKING;
9798
9799         if (!(element_info[element].access_direction & opposite_direction))
9800           return MF_NO_ACTION;  /* field not accessible from this direction */
9801
9802         if (element >= EL_GATE_1 && element <= EL_GATE_4)
9803         {
9804           if (!player->key[element - EL_GATE_1])
9805             return MF_NO_ACTION;
9806         }
9807         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9808         {
9809           if (!player->key[element - EL_GATE_1_GRAY])
9810             return MF_NO_ACTION;
9811         }
9812         else if (element == EL_EXIT_OPEN ||
9813                  element == EL_SP_EXIT_OPEN ||
9814                  element == EL_SP_EXIT_OPENING)
9815         {
9816           sound_action = ACTION_PASSING;        /* player is passing exit */
9817         }
9818         else if (element == EL_EMPTY)
9819         {
9820           sound_action = ACTION_MOVING;         /* nothing to walk on */
9821         }
9822
9823         /* play sound from background or player, whatever is available */
9824         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9825           PlayLevelSoundElementAction(x, y, element, sound_action);
9826         else
9827           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9828
9829         break;
9830       }
9831       else if (IS_PASSABLE(element))
9832       {
9833         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9834           return MF_NO_ACTION;
9835
9836         if (IS_CUSTOM_ELEMENT(element) &&
9837             !(element_info[element].access_direction & opposite_direction))
9838           return MF_NO_ACTION;  /* field not accessible from this direction */
9839
9840 #if 1
9841         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
9842           return MF_NO_ACTION;
9843 #endif
9844
9845         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9846         {
9847           if (!player->key[element - EL_EM_GATE_1])
9848             return MF_NO_ACTION;
9849         }
9850         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9851         {
9852           if (!player->key[element - EL_EM_GATE_1_GRAY])
9853             return MF_NO_ACTION;
9854         }
9855
9856         /* automatically move to the next field with double speed */
9857         player->programmed_action = move_direction;
9858 #if 1
9859         if (player->move_delay_reset_counter == 0)
9860         {
9861           player->move_delay_reset_counter = 2; /* two double speed steps */
9862
9863           DOUBLE_PLAYER_SPEED(player);
9864         }
9865 #else
9866         player->move_delay_reset_counter = 2;
9867
9868         DOUBLE_PLAYER_SPEED(player);
9869 #endif
9870
9871         PlayLevelSoundAction(x, y, ACTION_PASSING);
9872
9873         break;
9874       }
9875       else if (IS_DIGGABLE(element))
9876       {
9877         RemoveField(x, y);
9878
9879         if (mode != DF_SNAP)
9880         {
9881 #if 1
9882           GfxElement[x][y] = GFX_ELEMENT(element);
9883 #else
9884           GfxElement[x][y] =
9885             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9886 #endif
9887           player->is_digging = TRUE;
9888         }
9889
9890         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9891
9892         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9893                                           player->index_bit, CH_SIDE_ANY);
9894
9895 #if 1
9896         if (mode == DF_SNAP)
9897           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9898 #endif
9899
9900         break;
9901       }
9902       else if (IS_COLLECTIBLE(element))
9903       {
9904         RemoveField(x, y);
9905
9906         if (mode != DF_SNAP)
9907         {
9908           GfxElement[x][y] = element;
9909           player->is_collecting = TRUE;
9910         }
9911
9912         if (element == EL_SPEED_PILL)
9913           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9914         else if (element == EL_EXTRA_TIME && level.time > 0)
9915         {
9916           TimeLeft += 10;
9917           DrawGameValue_Time(TimeLeft);
9918         }
9919         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9920         {
9921           player->shield_normal_time_left += 10;
9922           if (element == EL_SHIELD_DEADLY)
9923             player->shield_deadly_time_left += 10;
9924         }
9925         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9926         {
9927           if (player->inventory_size < MAX_INVENTORY_SIZE)
9928             player->inventory_element[player->inventory_size++] = element;
9929
9930           DrawGameValue_Dynamite(local_player->inventory_size);
9931         }
9932         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9933         {
9934           player->dynabomb_count++;
9935           player->dynabombs_left++;
9936         }
9937         else if (element == EL_DYNABOMB_INCREASE_SIZE)
9938         {
9939           player->dynabomb_size++;
9940         }
9941         else if (element == EL_DYNABOMB_INCREASE_POWER)
9942         {
9943           player->dynabomb_xl = TRUE;
9944         }
9945         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9946                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9947         {
9948           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9949                         element - EL_KEY_1 : element - EL_EM_KEY_1);
9950
9951           player->key[key_nr] = TRUE;
9952
9953           DrawGameValue_Keys(player);
9954
9955           redraw_mask |= REDRAW_DOOR_1;
9956         }
9957         else if (IS_ENVELOPE(element))
9958         {
9959 #if 1
9960           player->show_envelope = element;
9961 #else
9962           ShowEnvelope(element - EL_ENVELOPE_1);
9963 #endif
9964         }
9965         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9966         {
9967           int i;
9968
9969           if (element_info[element].collect_count == 0)
9970             player->inventory_infinite_element = element;
9971           else
9972             for (i = 0; i < element_info[element].collect_count; i++)
9973               if (player->inventory_size < MAX_INVENTORY_SIZE)
9974                 player->inventory_element[player->inventory_size++] = element;
9975
9976           DrawGameValue_Dynamite(local_player->inventory_size);
9977         }
9978         else if (element_info[element].collect_count > 0)
9979         {
9980           local_player->gems_still_needed -=
9981             element_info[element].collect_count;
9982           if (local_player->gems_still_needed < 0)
9983             local_player->gems_still_needed = 0;
9984
9985           DrawGameValue_Emeralds(local_player->gems_still_needed);
9986         }
9987
9988         RaiseScoreElement(element);
9989         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9990
9991         CheckTriggeredElementChangePlayer(x, y, element,
9992                                           CE_OTHER_GETS_COLLECTED,
9993                                           player->index_bit, CH_SIDE_ANY);
9994
9995 #if 1
9996         if (mode == DF_SNAP)
9997           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9998 #endif
9999
10000         break;
10001       }
10002       else if (IS_PUSHABLE(element))
10003       {
10004         if (mode == DF_SNAP && element != EL_BD_ROCK)
10005           return MF_NO_ACTION;
10006
10007         if (CAN_FALL(element) && dy)
10008           return MF_NO_ACTION;
10009
10010         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10011             !(element == EL_SPRING && level.use_spring_bug))
10012           return MF_NO_ACTION;
10013
10014 #if 1
10015         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10016             ((move_direction & MV_VERTICAL &&
10017               ((element_info[element].move_pattern & MV_LEFT &&
10018                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10019                (element_info[element].move_pattern & MV_RIGHT &&
10020                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10021              (move_direction & MV_HORIZONTAL &&
10022               ((element_info[element].move_pattern & MV_UP &&
10023                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10024                (element_info[element].move_pattern & MV_DOWN &&
10025                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10026           return MF_NO_ACTION;
10027 #endif
10028
10029 #if 1
10030         /* do not push elements already moving away faster than player */
10031         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10032             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10033           return MF_NO_ACTION;
10034 #else
10035         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10036           return MF_NO_ACTION;
10037 #endif
10038
10039 #if 1
10040         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10041         {
10042           if (player->push_delay_value == -1)
10043             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10044         }
10045         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10046         {
10047           if (!player->is_pushing)
10048             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10049         }
10050
10051         /*
10052         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10053             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10054              !player_is_pushing))
10055           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10056         */
10057 #else
10058         if (!player->is_pushing &&
10059             game.engine_version >= VERSION_IDENT(2,2,0,7))
10060           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10061 #endif
10062
10063 #if 0
10064         printf("::: push delay: %ld [%d, %d] [%d]\n",
10065                player->push_delay_value, FrameCounter, game.engine_version,
10066                player->is_pushing);
10067 #endif
10068
10069         player->is_pushing = TRUE;
10070
10071         if (!(IN_LEV_FIELD(nextx, nexty) &&
10072               (IS_FREE(nextx, nexty) ||
10073                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10074                 IS_SB_ELEMENT(element)))))
10075           return MF_NO_ACTION;
10076
10077         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10078           return MF_NO_ACTION;
10079
10080         if (player->push_delay == 0)    /* new pushing; restart delay */
10081           player->push_delay = FrameCounter;
10082
10083         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10084             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10085             element != EL_SPRING && element != EL_BALLOON)
10086         {
10087           /* make sure that there is no move delay before next try to push */
10088           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10089             player->move_delay = INITIAL_MOVE_DELAY_OFF;
10090
10091           return MF_NO_ACTION;
10092         }
10093
10094 #if 0
10095         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10096 #endif
10097
10098         if (IS_SB_ELEMENT(element))
10099         {
10100           if (element == EL_SOKOBAN_FIELD_FULL)
10101           {
10102             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10103             local_player->sokobanfields_still_needed++;
10104           }
10105
10106           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10107           {
10108             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10109             local_player->sokobanfields_still_needed--;
10110           }
10111
10112           Feld[x][y] = EL_SOKOBAN_OBJECT;
10113
10114           if (Back[x][y] == Back[nextx][nexty])
10115             PlayLevelSoundAction(x, y, ACTION_PUSHING);
10116           else if (Back[x][y] != 0)
10117             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10118                                         ACTION_EMPTYING);
10119           else
10120             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10121                                         ACTION_FILLING);
10122
10123           if (local_player->sokobanfields_still_needed == 0 &&
10124               game.emulation == EMU_SOKOBAN)
10125           {
10126             player->LevelSolved = player->GameOver = TRUE;
10127             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10128           }
10129         }
10130         else
10131           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10132
10133         InitMovingField(x, y, move_direction);
10134         GfxAction[x][y] = ACTION_PUSHING;
10135
10136         if (mode == DF_SNAP)
10137           ContinueMoving(x, y);
10138         else
10139           MovPos[x][y] = (dx != 0 ? dx : dy);
10140
10141         Pushed[x][y] = TRUE;
10142         Pushed[nextx][nexty] = TRUE;
10143
10144         if (game.engine_version < VERSION_IDENT(2,2,0,7))
10145           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10146         else
10147           player->push_delay_value = -1;        /* get new value later */
10148
10149         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10150                                           player->index_bit, dig_side);
10151         CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10152                                  player->index_bit, dig_side);
10153
10154         break;
10155       }
10156       else if (IS_SWITCHABLE(element))
10157       {
10158         if (PLAYER_SWITCHING(player, x, y))
10159           return MF_ACTION;
10160
10161         player->is_switching = TRUE;
10162         player->switch_x = x;
10163         player->switch_y = y;
10164
10165         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10166
10167         if (element == EL_ROBOT_WHEEL)
10168         {
10169           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10170           ZX = x;
10171           ZY = y;
10172
10173           DrawLevelField(x, y);
10174         }
10175         else if (element == EL_SP_TERMINAL)
10176         {
10177           int xx, yy;
10178
10179           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10180           {
10181             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10182               Bang(xx, yy);
10183             else if (Feld[xx][yy] == EL_SP_TERMINAL)
10184               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10185           }
10186         }
10187         else if (IS_BELT_SWITCH(element))
10188         {
10189           ToggleBeltSwitch(x, y);
10190         }
10191         else if (element == EL_SWITCHGATE_SWITCH_UP ||
10192                  element == EL_SWITCHGATE_SWITCH_DOWN)
10193         {
10194           ToggleSwitchgateSwitch(x, y);
10195         }
10196         else if (element == EL_LIGHT_SWITCH ||
10197                  element == EL_LIGHT_SWITCH_ACTIVE)
10198         {
10199           ToggleLightSwitch(x, y);
10200
10201 #if 0
10202           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10203                          SND_LIGHT_SWITCH_ACTIVATING :
10204                          SND_LIGHT_SWITCH_DEACTIVATING);
10205 #endif
10206         }
10207         else if (element == EL_TIMEGATE_SWITCH)
10208         {
10209           ActivateTimegateSwitch(x, y);
10210         }
10211         else if (element == EL_BALLOON_SWITCH_LEFT ||
10212                  element == EL_BALLOON_SWITCH_RIGHT ||
10213                  element == EL_BALLOON_SWITCH_UP ||
10214                  element == EL_BALLOON_SWITCH_DOWN ||
10215                  element == EL_BALLOON_SWITCH_ANY)
10216         {
10217           if (element == EL_BALLOON_SWITCH_ANY)
10218             game.balloon_dir = move_direction;
10219           else
10220             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
10221                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10222                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
10223                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
10224                                 MV_NO_MOVING);
10225         }
10226         else if (element == EL_LAMP)
10227         {
10228           Feld[x][y] = EL_LAMP_ACTIVE;
10229           local_player->lights_still_needed--;
10230
10231           DrawLevelField(x, y);
10232         }
10233         else if (element == EL_TIME_ORB_FULL)
10234         {
10235           Feld[x][y] = EL_TIME_ORB_EMPTY;
10236           TimeLeft += 10;
10237           DrawGameValue_Time(TimeLeft);
10238
10239           DrawLevelField(x, y);
10240
10241 #if 0
10242           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10243 #endif
10244         }
10245
10246         return MF_ACTION;
10247       }
10248       else
10249       {
10250         if (!PLAYER_SWITCHING(player, x, y))
10251         {
10252           player->is_switching = TRUE;
10253           player->switch_x = x;
10254           player->switch_y = y;
10255
10256           CheckTriggeredElementChangePlayer(x, y, element,
10257                                             CE_OTHER_IS_SWITCHING,
10258                                             player->index_bit, dig_side);
10259           CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10260                                    player->index_bit, dig_side);
10261         }
10262
10263         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10264                                           player->index_bit, dig_side);
10265         CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10266                                  player->index_bit, dig_side);
10267       }
10268
10269       return MF_NO_ACTION;
10270   }
10271
10272   player->push_delay = 0;
10273
10274   if (Feld[x][y] != element)            /* really digged/collected something */
10275     player->is_collecting = !player->is_digging;
10276
10277   return MF_MOVING;
10278 }
10279
10280 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10281 {
10282   int jx = player->jx, jy = player->jy;
10283   int x = jx + dx, y = jy + dy;
10284   int snap_direction = (dx == -1 ? MV_LEFT :
10285                         dx == +1 ? MV_RIGHT :
10286                         dy == -1 ? MV_UP :
10287                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10288
10289 #if 0
10290   if (player->MovPos)
10291     return FALSE;
10292 #else
10293   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10294     return FALSE;
10295 #endif
10296
10297   if (!player->active || !IN_LEV_FIELD(x, y))
10298     return FALSE;
10299
10300   if (dx && dy)
10301     return FALSE;
10302
10303   if (!dx && !dy)
10304   {
10305     if (player->MovPos == 0)
10306       player->is_pushing = FALSE;
10307
10308     player->is_snapping = FALSE;
10309
10310     if (player->MovPos == 0)
10311     {
10312       player->is_moving = FALSE;
10313       player->is_digging = FALSE;
10314       player->is_collecting = FALSE;
10315     }
10316
10317     return FALSE;
10318   }
10319
10320   if (player->is_snapping)
10321     return FALSE;
10322
10323   player->MovDir = snap_direction;
10324
10325 #if 1
10326   if (player->MovPos == 0)
10327 #endif
10328   {
10329     player->is_moving = FALSE;
10330     player->is_digging = FALSE;
10331     player->is_collecting = FALSE;
10332   }
10333
10334   player->is_dropping = FALSE;
10335
10336   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10337     return FALSE;
10338
10339   player->is_snapping = TRUE;
10340
10341 #if 1
10342   if (player->MovPos == 0)
10343 #endif
10344   {
10345     player->is_moving = FALSE;
10346     player->is_digging = FALSE;
10347     player->is_collecting = FALSE;
10348   }
10349
10350   DrawLevelField(x, y);
10351   BackToFront();
10352
10353   return TRUE;
10354 }
10355
10356 boolean DropElement(struct PlayerInfo *player)
10357 {
10358   int jx = player->jx, jy = player->jy;
10359   int old_element = Feld[jx][jy];
10360   int new_element = (player->inventory_size > 0 ?
10361                      player->inventory_element[player->inventory_size - 1] :
10362                      player->inventory_infinite_element != EL_UNDEFINED ?
10363                      player->inventory_infinite_element :
10364                      player->dynabombs_left > 0 ?
10365                      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10366                      EL_UNDEFINED);
10367
10368   /* check if player is active, not moving and ready to drop */
10369   if (!player->active || player->MovPos || player->drop_delay > 0)
10370     return FALSE;
10371
10372   /* check if player has anything that can be dropped */
10373 #if 1
10374   if (new_element == EL_UNDEFINED)
10375     return FALSE;
10376 #else
10377   if (player->inventory_size == 0 &&
10378       player->inventory_infinite_element == EL_UNDEFINED &&
10379       player->dynabombs_left == 0)
10380     return FALSE;
10381 #endif
10382
10383   /* check if anything can be dropped at the current position */
10384   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10385     return FALSE;
10386
10387   /* collected custom elements can only be dropped on empty fields */
10388 #if 1
10389   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10390     return FALSE;
10391 #else
10392   if (player->inventory_size > 0 &&
10393       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10394       && old_element != EL_EMPTY)
10395     return FALSE;
10396 #endif
10397
10398   if (old_element != EL_EMPTY)
10399     Back[jx][jy] = old_element;         /* store old element on this field */
10400
10401   ResetGfxAnimation(jx, jy);
10402   ResetRandomAnimationValue(jx, jy);
10403
10404   if (player->inventory_size > 0 ||
10405       player->inventory_infinite_element != EL_UNDEFINED)
10406   {
10407     if (player->inventory_size > 0)
10408     {
10409       player->inventory_size--;
10410
10411 #if 0
10412       new_element = player->inventory_element[player->inventory_size];
10413 #endif
10414
10415       DrawGameValue_Dynamite(local_player->inventory_size);
10416
10417       if (new_element == EL_DYNAMITE)
10418         new_element = EL_DYNAMITE_ACTIVE;
10419       else if (new_element == EL_SP_DISK_RED)
10420         new_element = EL_SP_DISK_RED_ACTIVE;
10421     }
10422
10423     Feld[jx][jy] = new_element;
10424
10425     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10426       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10427
10428     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10429
10430 #if 1
10431     /* needed if previous element just changed to "empty" in the last frame */
10432     Changed[jx][jy] = 0;                /* allow another change */
10433 #endif
10434
10435     CheckTriggeredElementChangePlayer(jx, jy, new_element,
10436                                       CE_OTHER_GETS_DROPPED,
10437                                       player->index_bit, CH_SIDE_ANY);
10438     CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10439                              player->index_bit, CH_SIDE_ANY);
10440
10441     TestIfElementTouchesCustomElement(jx, jy);
10442   }
10443   else          /* player is dropping a dyna bomb */
10444   {
10445     player->dynabombs_left--;
10446
10447 #if 0
10448     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10449 #endif
10450
10451     Feld[jx][jy] = new_element;
10452
10453     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10454       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10455
10456     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10457   }
10458
10459
10460
10461 #if 1
10462
10463   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
10464   {
10465 #if 1
10466     InitField_WithBug1(jx, jy, FALSE);
10467 #else
10468     InitField(jx, jy, FALSE);
10469     if (CAN_MOVE(Feld[jx][jy]))
10470       InitMovDir(jx, jy);
10471 #endif
10472   }
10473
10474   new_element = Feld[jx][jy];
10475
10476   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10477       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10478   {
10479     int move_stepsize = element_info[new_element].move_stepsize;
10480     int direction, dx, dy, nextx, nexty;
10481
10482     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10483       MovDir[jx][jy] = player->MovDir;
10484
10485     direction = MovDir[jx][jy];
10486     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10487     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10488     nextx = jx + dx;
10489     nexty = jy + dy;
10490
10491     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10492     {
10493 #if 0
10494       WasJustMoving[jx][jy] = 3;
10495 #else
10496       InitMovingField(jx, jy, direction);
10497       ContinueMoving(jx, jy);
10498 #endif
10499     }
10500     else
10501     {
10502       Changed[jx][jy] = 0;              /* allow another change */
10503
10504 #if 1
10505       TestIfElementHitsCustomElement(jx, jy, direction);
10506 #else
10507       CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10508                              direction);
10509 #endif
10510     }
10511
10512     player->drop_delay = 2 * TILEX / move_stepsize + 1;
10513   }
10514
10515 #if 0
10516   player->drop_delay = 8 + 8 + 8;
10517 #endif
10518
10519 #endif
10520
10521   player->is_dropping = TRUE;
10522
10523
10524   return TRUE;
10525 }
10526
10527 /* ------------------------------------------------------------------------- */
10528 /* game sound playing functions                                              */
10529 /* ------------------------------------------------------------------------- */
10530
10531 static int *loop_sound_frame = NULL;
10532 static int *loop_sound_volume = NULL;
10533
10534 void InitPlayLevelSound()
10535 {
10536   int num_sounds = getSoundListSize();
10537
10538   checked_free(loop_sound_frame);
10539   checked_free(loop_sound_volume);
10540
10541   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
10542   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10543 }
10544
10545 static void PlayLevelSound(int x, int y, int nr)
10546 {
10547   int sx = SCREENX(x), sy = SCREENY(y);
10548   int volume, stereo_position;
10549   int max_distance = 8;
10550   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10551
10552   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10553       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10554     return;
10555
10556   if (!IN_LEV_FIELD(x, y) ||
10557       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10558       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10559     return;
10560
10561   volume = SOUND_MAX_VOLUME;
10562
10563   if (!IN_SCR_FIELD(sx, sy))
10564   {
10565     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10566     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10567
10568     volume -= volume * (dx > dy ? dx : dy) / max_distance;
10569   }
10570
10571   stereo_position = (SOUND_MAX_LEFT +
10572                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10573                      (SCR_FIELDX + 2 * max_distance));
10574
10575   if (IS_LOOP_SOUND(nr))
10576   {
10577     /* This assures that quieter loop sounds do not overwrite louder ones,
10578        while restarting sound volume comparison with each new game frame. */
10579
10580     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10581       return;
10582
10583     loop_sound_volume[nr] = volume;
10584     loop_sound_frame[nr] = FrameCounter;
10585   }
10586
10587   PlaySoundExt(nr, volume, stereo_position, type);
10588 }
10589
10590 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10591 {
10592   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10593                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
10594                  y < LEVELY(BY1) ? LEVELY(BY1) :
10595                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
10596                  sound_action);
10597 }
10598
10599 static void PlayLevelSoundAction(int x, int y, int action)
10600 {
10601   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10602 }
10603
10604 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10605 {
10606   int sound_effect = element_info[element].sound[action];
10607
10608   if (sound_effect != SND_UNDEFINED)
10609     PlayLevelSound(x, y, sound_effect);
10610 }
10611
10612 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10613                                               int action)
10614 {
10615   int sound_effect = element_info[element].sound[action];
10616
10617   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10618     PlayLevelSound(x, y, sound_effect);
10619 }
10620
10621 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10622 {
10623   int sound_effect = element_info[Feld[x][y]].sound[action];
10624
10625   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10626     PlayLevelSound(x, y, sound_effect);
10627 }
10628
10629 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10630 {
10631   int sound_effect = element_info[Feld[x][y]].sound[action];
10632
10633   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10634     StopSound(sound_effect);
10635 }
10636
10637 static void PlayLevelMusic()
10638 {
10639   if (levelset.music[level_nr] != MUS_UNDEFINED)
10640     PlayMusic(levelset.music[level_nr]);        /* from config file */
10641   else
10642     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
10643 }
10644
10645 void RaiseScore(int value)
10646 {
10647   local_player->score += value;
10648
10649   DrawGameValue_Score(local_player->score);
10650 }
10651
10652 void RaiseScoreElement(int element)
10653 {
10654   switch(element)
10655   {
10656     case EL_EMERALD:
10657     case EL_BD_DIAMOND:
10658     case EL_EMERALD_YELLOW:
10659     case EL_EMERALD_RED:
10660     case EL_EMERALD_PURPLE:
10661     case EL_SP_INFOTRON:
10662       RaiseScore(level.score[SC_EMERALD]);
10663       break;
10664     case EL_DIAMOND:
10665       RaiseScore(level.score[SC_DIAMOND]);
10666       break;
10667     case EL_CRYSTAL:
10668       RaiseScore(level.score[SC_CRYSTAL]);
10669       break;
10670     case EL_PEARL:
10671       RaiseScore(level.score[SC_PEARL]);
10672       break;
10673     case EL_BUG:
10674     case EL_BD_BUTTERFLY:
10675     case EL_SP_ELECTRON:
10676       RaiseScore(level.score[SC_BUG]);
10677       break;
10678     case EL_SPACESHIP:
10679     case EL_BD_FIREFLY:
10680     case EL_SP_SNIKSNAK:
10681       RaiseScore(level.score[SC_SPACESHIP]);
10682       break;
10683     case EL_YAMYAM:
10684     case EL_DARK_YAMYAM:
10685       RaiseScore(level.score[SC_YAMYAM]);
10686       break;
10687     case EL_ROBOT:
10688       RaiseScore(level.score[SC_ROBOT]);
10689       break;
10690     case EL_PACMAN:
10691       RaiseScore(level.score[SC_PACMAN]);
10692       break;
10693     case EL_NUT:
10694       RaiseScore(level.score[SC_NUT]);
10695       break;
10696     case EL_DYNAMITE:
10697     case EL_SP_DISK_RED:
10698     case EL_DYNABOMB_INCREASE_NUMBER:
10699     case EL_DYNABOMB_INCREASE_SIZE:
10700     case EL_DYNABOMB_INCREASE_POWER:
10701       RaiseScore(level.score[SC_DYNAMITE]);
10702       break;
10703     case EL_SHIELD_NORMAL:
10704     case EL_SHIELD_DEADLY:
10705       RaiseScore(level.score[SC_SHIELD]);
10706       break;
10707     case EL_EXTRA_TIME:
10708       RaiseScore(level.score[SC_TIME_BONUS]);
10709       break;
10710     case EL_KEY_1:
10711     case EL_KEY_2:
10712     case EL_KEY_3:
10713     case EL_KEY_4:
10714       RaiseScore(level.score[SC_KEY]);
10715       break;
10716     default:
10717       RaiseScore(element_info[element].collect_score);
10718       break;
10719   }
10720 }
10721
10722 void RequestQuitGame(boolean ask_if_really_quit)
10723 {
10724   if (AllPlayersGone ||
10725       !ask_if_really_quit ||
10726       level_editor_test_game ||
10727       Request("Do you really want to quit the game ?",
10728               REQ_ASK | REQ_STAY_CLOSED))
10729   {
10730 #if defined(PLATFORM_UNIX)
10731     if (options.network)
10732       SendToServer_StopPlaying();
10733     else
10734 #endif
10735     {
10736       game_status = GAME_MODE_MAIN;
10737       DrawMainMenu();
10738     }
10739   }
10740   else
10741   {
10742
10743 #if 1
10744     if (tape.playing && tape.index_search)
10745     {
10746       SetDrawDeactivationMask(REDRAW_NONE);
10747       audio.sound_deactivated = FALSE;
10748     }
10749 #endif
10750
10751     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10752
10753 #if 1
10754     if (tape.playing && tape.index_search)
10755     {
10756       SetDrawDeactivationMask(REDRAW_FIELD);
10757       audio.sound_deactivated = TRUE;
10758     }
10759 #endif
10760
10761   }
10762 }
10763
10764
10765 /* ---------- new game button stuff ---------------------------------------- */
10766
10767 /* graphic position values for game buttons */
10768 #define GAME_BUTTON_XSIZE       30
10769 #define GAME_BUTTON_YSIZE       30
10770 #define GAME_BUTTON_XPOS        5
10771 #define GAME_BUTTON_YPOS        215
10772 #define SOUND_BUTTON_XPOS       5
10773 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10774
10775 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10776 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10777 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10778 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10779 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10780 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10781
10782 static struct
10783 {
10784   int x, y;
10785   int gadget_id;
10786   char *infotext;
10787 } gamebutton_info[NUM_GAME_BUTTONS] =
10788 {
10789   {
10790     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
10791     GAME_CTRL_ID_STOP,
10792     "stop game"
10793   },
10794   {
10795     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
10796     GAME_CTRL_ID_PAUSE,
10797     "pause game"
10798   },
10799   {
10800     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
10801     GAME_CTRL_ID_PLAY,
10802     "play game"
10803   },
10804   {
10805     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
10806     SOUND_CTRL_ID_MUSIC,
10807     "background music on/off"
10808   },
10809   {
10810     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
10811     SOUND_CTRL_ID_LOOPS,
10812     "sound loops on/off"
10813   },
10814   {
10815     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
10816     SOUND_CTRL_ID_SIMPLE,
10817     "normal sounds on/off"
10818   }
10819 };
10820
10821 void CreateGameButtons()
10822 {
10823   int i;
10824
10825   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10826   {
10827     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10828     struct GadgetInfo *gi;
10829     int button_type;
10830     boolean checked;
10831     unsigned long event_mask;
10832     int gd_xoffset, gd_yoffset;
10833     int gd_x1, gd_x2, gd_y1, gd_y2;
10834     int id = i;
10835
10836     gd_xoffset = gamebutton_info[i].x;
10837     gd_yoffset = gamebutton_info[i].y;
10838     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10839     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10840
10841     if (id == GAME_CTRL_ID_STOP ||
10842         id == GAME_CTRL_ID_PAUSE ||
10843         id == GAME_CTRL_ID_PLAY)
10844     {
10845       button_type = GD_TYPE_NORMAL_BUTTON;
10846       checked = FALSE;
10847       event_mask = GD_EVENT_RELEASED;
10848       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10849       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10850     }
10851     else
10852     {
10853       button_type = GD_TYPE_CHECK_BUTTON;
10854       checked =
10855         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10856          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10857          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10858       event_mask = GD_EVENT_PRESSED;
10859       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
10860       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10861     }
10862
10863     gi = CreateGadget(GDI_CUSTOM_ID, id,
10864                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
10865                       GDI_X, DX + gd_xoffset,
10866                       GDI_Y, DY + gd_yoffset,
10867                       GDI_WIDTH, GAME_BUTTON_XSIZE,
10868                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
10869                       GDI_TYPE, button_type,
10870                       GDI_STATE, GD_BUTTON_UNPRESSED,
10871                       GDI_CHECKED, checked,
10872                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10873                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10874                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10875                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10876                       GDI_EVENT_MASK, event_mask,
10877                       GDI_CALLBACK_ACTION, HandleGameButtons,
10878                       GDI_END);
10879
10880     if (gi == NULL)
10881       Error(ERR_EXIT, "cannot create gadget");
10882
10883     game_gadget[id] = gi;
10884   }
10885 }
10886
10887 void FreeGameButtons()
10888 {
10889   int i;
10890
10891   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10892     FreeGadget(game_gadget[i]);
10893 }
10894
10895 static void MapGameButtons()
10896 {
10897   int i;
10898
10899   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10900     MapGadget(game_gadget[i]);
10901 }
10902
10903 void UnmapGameButtons()
10904 {
10905   int i;
10906
10907   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10908     UnmapGadget(game_gadget[i]);
10909 }
10910
10911 static void HandleGameButtons(struct GadgetInfo *gi)
10912 {
10913   int id = gi->custom_id;
10914
10915   if (game_status != GAME_MODE_PLAYING)
10916     return;
10917
10918   switch (id)
10919   {
10920     case GAME_CTRL_ID_STOP:
10921       RequestQuitGame(TRUE);
10922       break;
10923
10924     case GAME_CTRL_ID_PAUSE:
10925       if (options.network)
10926       {
10927 #if defined(PLATFORM_UNIX)
10928         if (tape.pausing)
10929           SendToServer_ContinuePlaying();
10930         else
10931           SendToServer_PausePlaying();
10932 #endif
10933       }
10934       else
10935         TapeTogglePause(TAPE_TOGGLE_MANUAL);
10936       break;
10937
10938     case GAME_CTRL_ID_PLAY:
10939       if (tape.pausing)
10940       {
10941 #if defined(PLATFORM_UNIX)
10942         if (options.network)
10943           SendToServer_ContinuePlaying();
10944         else
10945 #endif
10946         {
10947           tape.pausing = FALSE;
10948           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10949         }
10950       }
10951       break;
10952
10953     case SOUND_CTRL_ID_MUSIC:
10954       if (setup.sound_music)
10955       { 
10956         setup.sound_music = FALSE;
10957         FadeMusic();
10958       }
10959       else if (audio.music_available)
10960       { 
10961         setup.sound = setup.sound_music = TRUE;
10962
10963         SetAudioMode(setup.sound);
10964
10965         PlayLevelMusic();
10966       }
10967       break;
10968
10969     case SOUND_CTRL_ID_LOOPS:
10970       if (setup.sound_loops)
10971         setup.sound_loops = FALSE;
10972       else if (audio.loops_available)
10973       {
10974         setup.sound = setup.sound_loops = TRUE;
10975         SetAudioMode(setup.sound);
10976       }
10977       break;
10978
10979     case SOUND_CTRL_ID_SIMPLE:
10980       if (setup.sound_simple)
10981         setup.sound_simple = FALSE;
10982       else if (audio.sound_available)
10983       {
10984         setup.sound = setup.sound_simple = TRUE;
10985         SetAudioMode(setup.sound);
10986       }
10987       break;
10988
10989     default:
10990       break;
10991   }
10992 }