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