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