rnd-20040303-B-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MovePlayer() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollPlayer() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_NO_EXPLOSION         0
47 #define EX_NORMAL               1
48 #define EX_CENTER               2
49 #define EX_BORDER               3
50
51 /* special positions in the game control window (relative to control window) */
52 #define XX_LEVEL                37
53 #define YY_LEVEL                20
54 #define XX_EMERALDS             29
55 #define YY_EMERALDS             54
56 #define XX_DYNAMITE             29
57 #define YY_DYNAMITE             89
58 #define XX_KEYS                 18
59 #define YY_KEYS                 123
60 #define XX_SCORE                15
61 #define YY_SCORE                159
62 #define XX_TIME1                29
63 #define XX_TIME2                30
64 #define YY_TIME                 194
65
66 /* special positions in the game control window (relative to main window) */
67 #define DX_LEVEL                (DX + XX_LEVEL)
68 #define DY_LEVEL                (DY + YY_LEVEL)
69 #define DX_EMERALDS             (DX + XX_EMERALDS)
70 #define DY_EMERALDS             (DY + YY_EMERALDS)
71 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
72 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
73 #define DX_KEYS                 (DX + XX_KEYS)
74 #define DY_KEYS                 (DY + YY_KEYS)
75 #define DX_SCORE                (DX + XX_SCORE)
76 #define DY_SCORE                (DY + YY_SCORE)
77 #define DX_TIME1                (DX + XX_TIME1)
78 #define DX_TIME2                (DX + XX_TIME2)
79 #define DY_TIME                 (DY + YY_TIME)
80
81 /* values for initial player move delay (initial delay counter value) */
82 #define INITIAL_MOVE_DELAY_OFF  -1
83 #define INITIAL_MOVE_DELAY_ON   0
84
85 /* values for player movement speed (which is in fact a delay value) */
86 #define MOVE_DELAY_NORMAL_SPEED 8
87 #define MOVE_DELAY_HIGH_SPEED   4
88
89 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
90 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
91 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
92 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
93
94 /* values for other actions */
95 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
96
97 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
98
99 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
100                                  RND(element_info[e].push_delay_random))
101 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
102                                  RND(element_info[e].move_delay_random))
103 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
104                                     (element_info[e].move_delay_random))
105
106 #define ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, condition)                \
107                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
108                                         (CAN_MOVE_INTO_ACID(e) &&       \
109                                          Feld[x][y] == EL_ACID) ||      \
110                                         (condition)))
111
112 #if 0
113 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
114                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
115                                         (condition) ||                  \
116                                         (DONT_COLLIDE_WITH(e) &&        \
117                                          IS_PLAYER(x, y) &&             \
118                                          !PLAYER_ENEMY_PROTECTED(x, y))))
119 #else
120 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
121                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
122                                         (condition) ||                  \
123                                         (CAN_MOVE_INTO_ACID(e) &&       \
124                                          Feld[x][y] == EL_ACID) ||      \
125                                         (DONT_COLLIDE_WITH(e) &&        \
126                                          IS_PLAYER(x, y) &&             \
127                                          !PLAYER_ENEMY_PROTECTED(x, y))))
128 #endif
129
130 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)              \
131                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
132                                         (condition)))
133
134 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
135         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
136
137 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                        \
138         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
139
140 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                         \
141         ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
142
143 #if 0
144 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
145 #else
146 #define ENEMY_CAN_ENTER_FIELD(e, x, y) ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, 0)
147 #endif
148
149 #define YAMYAM_CAN_ENTER_FIELD(x, y)                                    \
150                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
151                                         (CAN_MOVE_INTO_ACID(EL_YAMYAM) && \
152                                          Feld[x][y] == EL_ACID) ||      \
153                                         Feld[x][y] == EL_DIAMOND))
154
155 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                               \
156                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
157                                         (CAN_MOVE_INTO_ACID(EL_DARK_YAMYAM) &&\
158                                          Feld[x][y] == EL_ACID) ||      \
159                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
160
161 #define PACMAN_CAN_ENTER_FIELD(x, y)                                    \
162                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
163                                         (CAN_MOVE_INTO_ACID(EL_PACMAN) && \
164                                          Feld[x][y] == EL_ACID) ||      \
165                                         IS_AMOEBOID(Feld[x][y])))
166
167 #define PIG_CAN_ENTER_FIELD(x, y)                                       \
168                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
169                                         (CAN_MOVE_INTO_ACID(EL_PIG) &&  \
170                                          Feld[x][y] == EL_ACID) ||      \
171                                         IS_FOOD_PIG(Feld[x][y])))
172
173 #define PENGUIN_CAN_ENTER_FIELD(x, y)                                   \
174                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
175                                         (CAN_MOVE_INTO_ACID(EL_PENGUIN) && \
176                                          Feld[x][y] == EL_ACID) ||      \
177                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
178                                         Feld[x][y] == EL_EXIT_OPEN))
179
180 #define DRAGON_CAN_ENTER_FIELD(x, y)                                    \
181                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
182                                         (CAN_MOVE_INTO_ACID(EL_DRAGON) && \
183                                          Feld[x][y] == EL_ACID)))
184
185 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                           \
186                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
187                                         (CAN_MOVE_INTO_ACID(EL_MOLE) && \
188                                          Feld[x][y] == EL_ACID) ||      \
189                                         (condition)))
190
191 #define SPRING_CAN_ENTER_FIELD(x, y)                                    \
192                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
193                                         (CAN_MOVE_INTO_ACID(EL_SPRING) && \
194                                          Feld[x][y] == EL_ACID)))
195
196 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
197 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
198 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
199 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
200
201 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
202         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
203
204 #if 0
205 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
206                 (!IS_PLAYER(x, y) &&                                    \
207                  (Feld[x][y] == EL_ACID ||                              \
208                   IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
209 #else
210 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
211                 (!IS_PLAYER(x, y) &&                                    \
212                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
213 #endif
214
215 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
216         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
217
218 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
219 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
220
221 /* game button identifiers */
222 #define GAME_CTRL_ID_STOP               0
223 #define GAME_CTRL_ID_PAUSE              1
224 #define GAME_CTRL_ID_PLAY               2
225 #define SOUND_CTRL_ID_MUSIC             3
226 #define SOUND_CTRL_ID_LOOPS             4
227 #define SOUND_CTRL_ID_SIMPLE            5
228
229 #define NUM_GAME_BUTTONS                6
230
231
232 /* forward declaration for internal use */
233
234 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
235 static boolean MovePlayer(struct PlayerInfo *, int, int);
236 static void ScrollPlayer(struct PlayerInfo *, int);
237 static void ScrollScreen(struct PlayerInfo *, int);
238
239 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
240
241 static void InitBeltMovement(void);
242 static void CloseAllOpenTimegates(void);
243 static void CheckGravityMovement(struct PlayerInfo *);
244 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
245 static void KillHeroUnlessEnemyProtected(int, int);
246 static void KillHeroUnlessExplosionProtected(int, int);
247
248 static void TestIfPlayerTouchesCustomElement(int, int);
249 static void TestIfElementTouchesCustomElement(int, int);
250 static void TestIfElementHitsCustomElement(int, int, int);
251
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_horizontal = player->action & MV_HORIZONTAL;
8077     int move_dir_vertical   = player->action & MV_VERTICAL;
8078     int move_dir =
8079       (player->last_move_dir & MV_HORIZONTAL ?
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 player_is_snapping = player->action & JOY_BUTTON_1;
8087     boolean field_under_player_is_free =
8088       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8089     boolean player_is_moving_to_valid_field =
8090       (
8091 #if 1
8092        !player_is_snapping &&
8093 #endif
8094        IN_LEV_FIELD(new_jx, new_jy) &&
8095        (Feld[new_jx][new_jy] == EL_SP_BASE ||
8096         Feld[new_jx][new_jy] == EL_SAND ||
8097         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8098          canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8099     /* !!! extend EL_SAND to anything diggable !!! */
8100
8101     boolean player_is_standing_on_valid_field =
8102       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8103        (IS_WALKABLE(Feld[jx][jy]) &&
8104         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8105
8106 #if 0
8107     printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
8108            field_under_player_is_free,
8109            player_is_standing_on_valid_field,
8110            player_is_moving_to_valid_field,
8111            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
8112 #endif
8113
8114     if (field_under_player_is_free &&
8115         !player_is_standing_on_valid_field &&
8116         !player_is_moving_to_valid_field)
8117     {
8118 #if 0
8119       printf("::: setting programmed_action to MV_DOWN ...\n");
8120 #endif
8121
8122       player->programmed_action = MV_DOWN;
8123     }
8124   }
8125 }
8126
8127 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8128 {
8129 #if 1
8130   return CheckGravityMovement(player);
8131 #endif
8132
8133   if (game.gravity && !player->programmed_action)
8134   {
8135     int jx = player->jx, jy = player->jy;
8136     boolean field_under_player_is_free =
8137       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8138     boolean player_is_standing_on_valid_field =
8139       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8140        (IS_WALKABLE(Feld[jx][jy]) &&
8141         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8142
8143     if (field_under_player_is_free && !player_is_standing_on_valid_field)
8144       player->programmed_action = MV_DOWN;
8145   }
8146 }
8147
8148 /*
8149   MovePlayerOneStep()
8150   -----------------------------------------------------------------------------
8151   dx, dy:               direction (non-diagonal) to try to move the player to
8152   real_dx, real_dy:     direction as read from input device (can be diagonal)
8153 */
8154
8155 boolean MovePlayerOneStep(struct PlayerInfo *player,
8156                           int dx, int dy, int real_dx, int real_dy)
8157 {
8158 #if 0
8159   static int trigger_sides[4][2] =
8160   {
8161     /* enter side        leave side */
8162     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
8163     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
8164     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
8165     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
8166   };
8167   int move_direction = (dx == -1 ? MV_LEFT :
8168                         dx == +1 ? MV_RIGHT :
8169                         dy == -1 ? MV_UP :
8170                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8171   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8172   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8173 #endif
8174   int jx = player->jx, jy = player->jy;
8175   int new_jx = jx + dx, new_jy = jy + dy;
8176   int element;
8177   int can_move;
8178
8179   if (!player->active || (!dx && !dy))
8180     return MF_NO_ACTION;
8181
8182   player->MovDir = (dx < 0 ? MV_LEFT :
8183                     dx > 0 ? MV_RIGHT :
8184                     dy < 0 ? MV_UP :
8185                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
8186
8187   if (!IN_LEV_FIELD(new_jx, new_jy))
8188     return MF_NO_ACTION;
8189
8190   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8191     return MF_NO_ACTION;
8192
8193 #if 0
8194   element = MovingOrBlocked2Element(new_jx, new_jy);
8195 #else
8196   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8197 #endif
8198
8199   if (DONT_RUN_INTO(element))
8200   {
8201     if (element == EL_ACID && dx == 0 && dy == 1)
8202     {
8203       SplashAcid(new_jx, new_jy);
8204       Feld[jx][jy] = EL_PLAYER_1;
8205       InitMovingField(jx, jy, MV_DOWN);
8206       Store[jx][jy] = EL_ACID;
8207       ContinueMoving(jx, jy);
8208       BuryHero(player);
8209     }
8210     else
8211       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8212
8213     return MF_MOVING;
8214   }
8215
8216   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8217   if (can_move != MF_MOVING)
8218     return can_move;
8219
8220   /* check if DigField() has caused relocation of the player */
8221   if (player->jx != jx || player->jy != jy)
8222     return MF_NO_ACTION;
8223
8224   StorePlayer[jx][jy] = 0;
8225   player->last_jx = jx;
8226   player->last_jy = jy;
8227   player->jx = new_jx;
8228   player->jy = new_jy;
8229   StorePlayer[new_jx][new_jy] = player->element_nr;
8230
8231   player->MovPos =
8232     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8233
8234   player->step_counter++;
8235
8236   player->drop_delay = 0;
8237
8238   PlayerVisit[jx][jy] = FrameCounter;
8239
8240   ScrollPlayer(player, SCROLL_INIT);
8241
8242 #if 0
8243   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8244   {
8245     CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8246                                     leave_side);
8247     CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8248   }
8249
8250   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8251   {
8252     CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8253                                     CE_OTHER_GETS_ENTERED, enter_side);
8254     CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8255                            CE_ENTERED_BY_PLAYER, enter_side);
8256   }
8257 #endif
8258
8259   return MF_MOVING;
8260 }
8261
8262 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8263 {
8264   int jx = player->jx, jy = player->jy;
8265   int old_jx = jx, old_jy = jy;
8266   int moved = MF_NO_ACTION;
8267
8268 #if 1
8269   if (!player->active)
8270     return FALSE;
8271
8272   if (!dx && !dy)
8273   {
8274     if (player->MovPos == 0)
8275     {
8276       player->is_moving = FALSE;
8277       player->is_digging = FALSE;
8278       player->is_collecting = FALSE;
8279       player->is_snapping = FALSE;
8280       player->is_pushing = FALSE;
8281     }
8282
8283     return FALSE;
8284   }
8285 #else
8286   if (!player->active || (!dx && !dy))
8287     return FALSE;
8288 #endif
8289
8290 #if 0
8291   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8292       !tape.playing)
8293     return FALSE;
8294 #else
8295
8296 #if 1
8297   if (!FrameReached(&player->move_delay, player->move_delay_value))
8298     return FALSE;
8299 #else
8300   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8301       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8302     return FALSE;
8303 #endif
8304
8305 #endif
8306
8307   /* remove the last programmed player action */
8308   player->programmed_action = 0;
8309
8310   if (player->MovPos)
8311   {
8312     /* should only happen if pre-1.2 tape recordings are played */
8313     /* this is only for backward compatibility */
8314
8315     int original_move_delay_value = player->move_delay_value;
8316
8317 #if DEBUG
8318     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8319            tape.counter);
8320 #endif
8321
8322     /* scroll remaining steps with finest movement resolution */
8323     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8324
8325     while (player->MovPos)
8326     {
8327       ScrollPlayer(player, SCROLL_GO_ON);
8328       ScrollScreen(NULL, SCROLL_GO_ON);
8329       FrameCounter++;
8330       DrawAllPlayers();
8331       BackToFront();
8332     }
8333
8334     player->move_delay_value = original_move_delay_value;
8335   }
8336
8337   if (player->last_move_dir & MV_HORIZONTAL)
8338   {
8339     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8340       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8341   }
8342   else
8343   {
8344     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8345       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8346   }
8347
8348   jx = player->jx;
8349   jy = player->jy;
8350
8351   if (moved & MF_MOVING && !ScreenMovPos &&
8352       (player == local_player || !options.network))
8353   {
8354     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8355     int offset = (setup.scroll_delay ? 3 : 0);
8356
8357     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8358     {
8359       /* actual player has left the screen -- scroll in that direction */
8360       if (jx != old_jx)         /* player has moved horizontally */
8361         scroll_x += (jx - old_jx);
8362       else                      /* player has moved vertically */
8363         scroll_y += (jy - old_jy);
8364     }
8365     else
8366     {
8367       if (jx != old_jx)         /* player has moved horizontally */
8368       {
8369         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8370             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8371           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8372
8373         /* don't scroll over playfield boundaries */
8374         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8375           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8376
8377         /* don't scroll more than one field at a time */
8378         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8379
8380         /* don't scroll against the player's moving direction */
8381         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8382             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8383           scroll_x = old_scroll_x;
8384       }
8385       else                      /* player has moved vertically */
8386       {
8387         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8388             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8389           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8390
8391         /* don't scroll over playfield boundaries */
8392         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8393           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8394
8395         /* don't scroll more than one field at a time */
8396         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8397
8398         /* don't scroll against the player's moving direction */
8399         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8400             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8401           scroll_y = old_scroll_y;
8402       }
8403     }
8404
8405     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8406     {
8407       if (!options.network && !AllPlayersInVisibleScreen())
8408       {
8409         scroll_x = old_scroll_x;
8410         scroll_y = old_scroll_y;
8411       }
8412       else
8413       {
8414         ScrollScreen(player, SCROLL_INIT);
8415         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8416       }
8417     }
8418   }
8419
8420 #if 0
8421 #if 1
8422   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8423 #else
8424   if (!(moved & MF_MOVING) && !player->is_pushing)
8425     player->Frame = 0;
8426 #endif
8427 #endif
8428
8429   player->StepFrame = 0;
8430
8431   if (moved & MF_MOVING)
8432   {
8433     if (old_jx != jx && old_jy == jy)
8434       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8435     else if (old_jx == jx && old_jy != jy)
8436       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8437
8438     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8439
8440     player->last_move_dir = player->MovDir;
8441     player->is_moving = TRUE;
8442 #if 1
8443     player->is_snapping = FALSE;
8444 #endif
8445
8446 #if 1
8447     player->is_switching = FALSE;
8448 #endif
8449
8450     player->is_dropping = FALSE;
8451
8452
8453 #if 1
8454     {
8455       static int trigger_sides[4][2] =
8456       {
8457         /* enter side           leave side */
8458         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8459         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8460         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8461         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8462       };
8463       int move_direction = player->MovDir;
8464       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8465       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8466
8467 #if 1
8468       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8469       {
8470         CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8471                                           CE_OTHER_GETS_LEFT,
8472                                           player->index_bit, leave_side);
8473         CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
8474                                  CE_LEFT_BY_PLAYER,
8475                                  player->index_bit, leave_side);
8476       }
8477
8478       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8479       {
8480         CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
8481                                           CE_OTHER_GETS_ENTERED,
8482                                           player->index_bit, enter_side);
8483         CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
8484                                  player->index_bit, enter_side);
8485       }
8486 #endif
8487
8488     }
8489 #endif
8490
8491
8492   }
8493   else
8494   {
8495     CheckGravityMovementWhenNotMoving(player);
8496
8497     /*
8498     player->last_move_dir = MV_NO_MOVING;
8499     */
8500     player->is_moving = FALSE;
8501   }
8502
8503   if (game.engine_version < VERSION_IDENT(3,0,7,0))
8504   {
8505     TestIfHeroTouchesBadThing(jx, jy);
8506     TestIfPlayerTouchesCustomElement(jx, jy);
8507   }
8508
8509   if (!player->active)
8510     RemoveHero(player);
8511
8512   return moved;
8513 }
8514
8515 void ScrollPlayer(struct PlayerInfo *player, int mode)
8516 {
8517   int jx = player->jx, jy = player->jy;
8518   int last_jx = player->last_jx, last_jy = player->last_jy;
8519   int move_stepsize = TILEX / player->move_delay_value;
8520
8521   if (!player->active || !player->MovPos)
8522     return;
8523
8524   if (mode == SCROLL_INIT)
8525   {
8526     player->actual_frame_counter = FrameCounter;
8527     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8528
8529     if (Feld[last_jx][last_jy] == EL_EMPTY)
8530       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8531
8532 #if 0
8533     DrawPlayer(player);
8534 #endif
8535
8536     return;
8537   }
8538   else if (!FrameReached(&player->actual_frame_counter, 1))
8539     return;
8540
8541   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8542   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8543
8544   if (!player->block_last_field &&
8545       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8546     Feld[last_jx][last_jy] = EL_EMPTY;
8547
8548   /* before DrawPlayer() to draw correct player graphic for this case */
8549   if (player->MovPos == 0)
8550     CheckGravityMovement(player);
8551
8552 #if 0
8553   DrawPlayer(player);   /* needed here only to cleanup last field */
8554 #endif
8555
8556   if (player->MovPos == 0)      /* player reached destination field */
8557   {
8558 #if 1
8559     if (player->move_delay_reset_counter > 0)
8560     {
8561       player->move_delay_reset_counter--;
8562
8563       if (player->move_delay_reset_counter == 0)
8564       {
8565         /* continue with normal speed after quickly moving through gate */
8566         HALVE_PLAYER_SPEED(player);
8567
8568         /* be able to make the next move without delay */
8569         player->move_delay = 0;
8570       }
8571     }
8572 #else
8573     if (IS_PASSABLE(Feld[last_jx][last_jy]))
8574     {
8575       /* continue with normal speed after quickly moving through gate */
8576       HALVE_PLAYER_SPEED(player);
8577
8578       /* be able to make the next move without delay */
8579       player->move_delay = 0;
8580     }
8581 #endif
8582
8583     if (player->block_last_field &&
8584         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8585       Feld[last_jx][last_jy] = EL_EMPTY;
8586
8587     player->last_jx = jx;
8588     player->last_jy = jy;
8589
8590     if (Feld[jx][jy] == EL_EXIT_OPEN ||
8591         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8592         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
8593     {
8594       DrawPlayer(player);       /* needed here only to cleanup last field */
8595       RemoveHero(player);
8596
8597       if (local_player->friends_still_needed == 0 ||
8598           IS_SP_ELEMENT(Feld[jx][jy]))
8599         player->LevelSolved = player->GameOver = TRUE;
8600     }
8601
8602     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8603     {
8604       TestIfHeroTouchesBadThing(jx, jy);
8605       TestIfPlayerTouchesCustomElement(jx, jy);
8606 #if 1
8607       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
8608 #endif
8609
8610       if (!player->active)
8611         RemoveHero(player);
8612     }
8613
8614     if (level.use_step_counter)
8615     {
8616       int i;
8617
8618       TimePlayed++;
8619
8620       for (i = 0; i < MAX_PLAYERS; i++)
8621       {
8622         struct PlayerInfo *player = &stored_player[i];
8623
8624         if (SHIELD_ON(player))
8625         {
8626           player->shield_normal_time_left--;
8627
8628           if (player->shield_deadly_time_left > 0)
8629             player->shield_deadly_time_left--;
8630         }
8631       }
8632
8633       if (TimeLeft > 0)
8634       {
8635         TimeLeft--;
8636
8637         if (TimeLeft <= 10 && setup.time_limit)
8638           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8639
8640         DrawGameValue_Time(TimeLeft);
8641
8642         if (!TimeLeft && setup.time_limit)
8643           for (i = 0; i < MAX_PLAYERS; i++)
8644             KillHero(&stored_player[i]);
8645       }
8646       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8647         DrawGameValue_Time(TimePlayed);
8648     }
8649
8650     if (tape.single_step && tape.recording && !tape.pausing &&
8651         !player->programmed_action)
8652       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8653   }
8654 }
8655
8656 void ScrollScreen(struct PlayerInfo *player, int mode)
8657 {
8658   static unsigned long screen_frame_counter = 0;
8659
8660   if (mode == SCROLL_INIT)
8661   {
8662     /* set scrolling step size according to actual player's moving speed */
8663     ScrollStepSize = TILEX / player->move_delay_value;
8664
8665     screen_frame_counter = FrameCounter;
8666     ScreenMovDir = player->MovDir;
8667     ScreenMovPos = player->MovPos;
8668     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8669     return;
8670   }
8671   else if (!FrameReached(&screen_frame_counter, 1))
8672     return;
8673
8674   if (ScreenMovPos)
8675   {
8676     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8677     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8678     redraw_mask |= REDRAW_FIELD;
8679   }
8680   else
8681     ScreenMovDir = MV_NO_MOVING;
8682 }
8683
8684 void TestIfPlayerTouchesCustomElement(int x, int y)
8685 {
8686   static int xy[4][2] =
8687   {
8688     { 0, -1 },
8689     { -1, 0 },
8690     { +1, 0 },
8691     { 0, +1 }
8692   };
8693   static int trigger_sides[4][2] =
8694   {
8695     /* center side       border side */
8696     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8697     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8698     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8699     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8700   };
8701   static int touch_dir[4] =
8702   {
8703     MV_LEFT | MV_RIGHT,
8704     MV_UP   | MV_DOWN,
8705     MV_UP   | MV_DOWN,
8706     MV_LEFT | MV_RIGHT
8707   };
8708   int center_element = Feld[x][y];      /* should always be non-moving! */
8709   int i;
8710
8711   for (i = 0; i < NUM_DIRECTIONS; i++)
8712   {
8713     int xx = x + xy[i][0];
8714     int yy = y + xy[i][1];
8715     int center_side = trigger_sides[i][0];
8716     int border_side = trigger_sides[i][1];
8717     int border_element;
8718
8719     if (!IN_LEV_FIELD(xx, yy))
8720       continue;
8721
8722     if (IS_PLAYER(x, y))
8723     {
8724       struct PlayerInfo *player = PLAYERINFO(x, y);
8725
8726       if (game.engine_version < VERSION_IDENT(3,0,7,0))
8727         border_element = Feld[xx][yy];          /* may be moving! */
8728       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8729         border_element = Feld[xx][yy];
8730       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
8731         border_element = MovingOrBlocked2Element(xx, yy);
8732       else
8733         continue;               /* center and border element do not touch */
8734
8735       CheckTriggeredElementChangePlayer(xx, yy, border_element,
8736                                         CE_OTHER_GETS_TOUCHED,
8737                                         player->index_bit, border_side);
8738       CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8739                                player->index_bit, border_side);
8740     }
8741     else if (IS_PLAYER(xx, yy))
8742     {
8743       struct PlayerInfo *player = PLAYERINFO(xx, yy);
8744
8745       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8746       {
8747         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8748           continue;             /* center and border element do not touch */
8749       }
8750
8751       CheckTriggeredElementChangePlayer(x, y, center_element,
8752                                         CE_OTHER_GETS_TOUCHED,
8753                                         player->index_bit, center_side);
8754       CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8755                                player->index_bit, center_side);
8756
8757       break;
8758     }
8759   }
8760 }
8761
8762 void TestIfElementTouchesCustomElement(int x, int y)
8763 {
8764   static int xy[4][2] =
8765   {
8766     { 0, -1 },
8767     { -1, 0 },
8768     { +1, 0 },
8769     { 0, +1 }
8770   };
8771   static int trigger_sides[4][2] =
8772   {
8773     /* center side      border side */
8774     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8775     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8776     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8777     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8778   };
8779   static int touch_dir[4] =
8780   {
8781     MV_LEFT | MV_RIGHT,
8782     MV_UP   | MV_DOWN,
8783     MV_UP   | MV_DOWN,
8784     MV_LEFT | MV_RIGHT
8785   };
8786   boolean change_center_element = FALSE;
8787   int center_element_change_page = 0;
8788   int center_element = Feld[x][y];      /* should always be non-moving! */
8789   int i, j;
8790
8791   for (i = 0; i < NUM_DIRECTIONS; i++)
8792   {
8793     int xx = x + xy[i][0];
8794     int yy = y + xy[i][1];
8795     int center_side = trigger_sides[i][0];
8796     int border_side = trigger_sides[i][1];
8797     int border_element;
8798
8799     if (!IN_LEV_FIELD(xx, yy))
8800       continue;
8801
8802     if (game.engine_version < VERSION_IDENT(3,0,7,0))
8803       border_element = Feld[xx][yy];    /* may be moving! */
8804     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8805       border_element = Feld[xx][yy];
8806     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
8807       border_element = MovingOrBlocked2Element(xx, yy);
8808     else
8809       continue;                 /* center and border element do not touch */
8810
8811     /* check for change of center element (but change it only once) */
8812     if (IS_CUSTOM_ELEMENT(center_element) &&
8813         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8814         !change_center_element)
8815     {
8816       for (j = 0; j < element_info[center_element].num_change_pages; j++)
8817       {
8818         struct ElementChangeInfo *change =
8819           &element_info[center_element].change_page[j];
8820
8821         if (change->can_change &&
8822             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8823             change->trigger_side & border_side &&
8824 #if 1
8825             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8826 #else
8827             change->trigger_element == border_element
8828 #endif
8829             )
8830         {
8831           change_center_element = TRUE;
8832           center_element_change_page = j;
8833
8834           break;
8835         }
8836       }
8837     }
8838
8839     /* check for change of border element */
8840     if (IS_CUSTOM_ELEMENT(border_element) &&
8841         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8842     {
8843       for (j = 0; j < element_info[border_element].num_change_pages; j++)
8844       {
8845         struct ElementChangeInfo *change =
8846           &element_info[border_element].change_page[j];
8847
8848         if (change->can_change &&
8849             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8850             change->trigger_side & center_side &&
8851 #if 1
8852             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8853 #else
8854             change->trigger_element == center_element
8855 #endif
8856             )
8857         {
8858           CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
8859                                  j);
8860           break;
8861         }
8862       }
8863     }
8864   }
8865
8866   if (change_center_element)
8867     CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
8868                            center_element_change_page);
8869 }
8870
8871 void TestIfElementHitsCustomElement(int x, int y, int direction)
8872 {
8873   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8874   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8875   int hitx = x + dx, hity = y + dy;
8876   int hitting_element = Feld[x][y];
8877 #if 0
8878   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8879                         !IS_FREE(hitx, hity) &&
8880                         (!IS_MOVING(hitx, hity) ||
8881                          MovDir[hitx][hity] != direction ||
8882                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
8883 #endif
8884
8885   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8886     return;
8887
8888 #if 0
8889   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8890     return;
8891 #endif
8892
8893   CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
8894                          direction);
8895
8896   if (IN_LEV_FIELD(hitx, hity))
8897   {
8898     int opposite_direction = MV_DIR_OPPOSITE(direction);
8899     int hitting_side = direction;
8900     int touched_side = opposite_direction;
8901     int touched_element = MovingOrBlocked2Element(hitx, hity);
8902 #if 1
8903     boolean object_hit = (!IS_MOVING(hitx, hity) ||
8904                           MovDir[hitx][hity] != direction ||
8905                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
8906
8907     object_hit = TRUE;
8908 #endif
8909
8910     if (object_hit)
8911     {
8912       int i;
8913
8914       CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
8915                              opposite_direction);
8916
8917       if (IS_CUSTOM_ELEMENT(hitting_element) &&
8918           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8919       {
8920         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8921         {
8922           struct ElementChangeInfo *change =
8923             &element_info[hitting_element].change_page[i];
8924
8925           if (change->can_change &&
8926               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8927               change->trigger_side & touched_side &&
8928           
8929 #if 1
8930               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8931 #else
8932               change->trigger_element == touched_element
8933 #endif
8934               )
8935           {
8936             CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
8937                                    i);
8938             break;
8939           }
8940         }
8941       }
8942
8943       if (IS_CUSTOM_ELEMENT(touched_element) &&
8944           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8945       {
8946         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8947         {
8948           struct ElementChangeInfo *change =
8949             &element_info[touched_element].change_page[i];
8950
8951           if (change->can_change &&
8952               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8953               change->trigger_side & hitting_side &&
8954 #if 1
8955               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8956 #else
8957               change->trigger_element == hitting_element
8958 #endif
8959               )
8960           {
8961             CheckElementChangePage(hitx, hity, touched_element,
8962                                    CE_OTHER_GETS_HIT, i);
8963             break;
8964           }
8965         }
8966       }
8967     }
8968   }
8969 }
8970
8971 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8972 {
8973   int i, kill_x = -1, kill_y = -1;
8974   static int test_xy[4][2] =
8975   {
8976     { 0, -1 },
8977     { -1, 0 },
8978     { +1, 0 },
8979     { 0, +1 }
8980   };
8981   static int test_dir[4] =
8982   {
8983     MV_UP,
8984     MV_LEFT,
8985     MV_RIGHT,
8986     MV_DOWN
8987   };
8988
8989   for (i = 0; i < NUM_DIRECTIONS; i++)
8990   {
8991     int test_x, test_y, test_move_dir, test_element;
8992
8993     test_x = good_x + test_xy[i][0];
8994     test_y = good_y + test_xy[i][1];
8995     if (!IN_LEV_FIELD(test_x, test_y))
8996       continue;
8997
8998     test_move_dir =
8999       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9000
9001 #if 0
9002     test_element = Feld[test_x][test_y];
9003 #else
9004     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9005 #endif
9006
9007     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9008        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9009     */
9010     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9011         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
9012     {
9013       kill_x = test_x;
9014       kill_y = test_y;
9015       break;
9016     }
9017   }
9018
9019   if (kill_x != -1 || kill_y != -1)
9020   {
9021     if (IS_PLAYER(good_x, good_y))
9022     {
9023       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9024
9025       if (player->shield_deadly_time_left > 0)
9026         Bang(kill_x, kill_y);
9027       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9028         KillHero(player);
9029     }
9030     else
9031       Bang(good_x, good_y);
9032   }
9033 }
9034
9035 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9036 {
9037   int i, kill_x = -1, kill_y = -1;
9038   int bad_element = Feld[bad_x][bad_y];
9039   static int test_xy[4][2] =
9040   {
9041     { 0, -1 },
9042     { -1, 0 },
9043     { +1, 0 },
9044     { 0, +1 }
9045   };
9046   static int touch_dir[4] =
9047   {
9048     MV_LEFT | MV_RIGHT,
9049     MV_UP   | MV_DOWN,
9050     MV_UP   | MV_DOWN,
9051     MV_LEFT | MV_RIGHT
9052   };
9053   static int test_dir[4] =
9054   {
9055     MV_UP,
9056     MV_LEFT,
9057     MV_RIGHT,
9058     MV_DOWN
9059   };
9060
9061   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
9062     return;
9063
9064   for (i = 0; i < NUM_DIRECTIONS; i++)
9065   {
9066     int test_x, test_y, test_move_dir, test_element;
9067
9068     test_x = bad_x + test_xy[i][0];
9069     test_y = bad_y + test_xy[i][1];
9070     if (!IN_LEV_FIELD(test_x, test_y))
9071       continue;
9072
9073     test_move_dir =
9074       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9075
9076     test_element = Feld[test_x][test_y];
9077
9078     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9079        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9080     */
9081     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
9082         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
9083     {
9084       /* good thing is player or penguin that does not move away */
9085       if (IS_PLAYER(test_x, test_y))
9086       {
9087         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9088
9089         if (bad_element == EL_ROBOT && player->is_moving)
9090           continue;     /* robot does not kill player if he is moving */
9091
9092         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9093         {
9094           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9095             continue;           /* center and border element do not touch */
9096         }
9097
9098         kill_x = test_x;
9099         kill_y = test_y;
9100         break;
9101       }
9102       else if (test_element == EL_PENGUIN)
9103       {
9104         kill_x = test_x;
9105         kill_y = test_y;
9106         break;
9107       }
9108     }
9109   }
9110
9111   if (kill_x != -1 || kill_y != -1)
9112   {
9113     if (IS_PLAYER(kill_x, kill_y))
9114     {
9115       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9116
9117       if (player->shield_deadly_time_left > 0)
9118         Bang(bad_x, bad_y);
9119       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9120         KillHero(player);
9121     }
9122     else
9123       Bang(kill_x, kill_y);
9124   }
9125 }
9126
9127 void TestIfHeroTouchesBadThing(int x, int y)
9128 {
9129   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9130 }
9131
9132 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9133 {
9134   TestIfGoodThingHitsBadThing(x, y, move_dir);
9135 }
9136
9137 void TestIfBadThingTouchesHero(int x, int y)
9138 {
9139   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9140 }
9141
9142 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9143 {
9144   TestIfBadThingHitsGoodThing(x, y, move_dir);
9145 }
9146
9147 void TestIfFriendTouchesBadThing(int x, int y)
9148 {
9149   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9150 }
9151
9152 void TestIfBadThingTouchesFriend(int x, int y)
9153 {
9154   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9155 }
9156
9157 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9158 {
9159   int i, kill_x = bad_x, kill_y = bad_y;
9160   static int xy[4][2] =
9161   {
9162     { 0, -1 },
9163     { -1, 0 },
9164     { +1, 0 },
9165     { 0, +1 }
9166   };
9167
9168   for (i = 0; i < NUM_DIRECTIONS; i++)
9169   {
9170     int x, y, element;
9171
9172     x = bad_x + xy[i][0];
9173     y = bad_y + xy[i][1];
9174     if (!IN_LEV_FIELD(x, y))
9175       continue;
9176
9177     element = Feld[x][y];
9178     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9179         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9180     {
9181       kill_x = x;
9182       kill_y = y;
9183       break;
9184     }
9185   }
9186
9187   if (kill_x != bad_x || kill_y != bad_y)
9188     Bang(bad_x, bad_y);
9189 }
9190
9191 void KillHero(struct PlayerInfo *player)
9192 {
9193   int jx = player->jx, jy = player->jy;
9194
9195   if (!player->active)
9196     return;
9197
9198   /* remove accessible field at the player's position */
9199   Feld[jx][jy] = EL_EMPTY;
9200
9201   /* deactivate shield (else Bang()/Explode() would not work right) */
9202   player->shield_normal_time_left = 0;
9203   player->shield_deadly_time_left = 0;
9204
9205   Bang(jx, jy);
9206   BuryHero(player);
9207 }
9208
9209 static void KillHeroUnlessEnemyProtected(int x, int y)
9210 {
9211   if (!PLAYER_ENEMY_PROTECTED(x, y))
9212     KillHero(PLAYERINFO(x, y));
9213 }
9214
9215 static void KillHeroUnlessExplosionProtected(int x, int y)
9216 {
9217   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9218     KillHero(PLAYERINFO(x, y));
9219 }
9220
9221 void BuryHero(struct PlayerInfo *player)
9222 {
9223   int jx = player->jx, jy = player->jy;
9224
9225   if (!player->active)
9226     return;
9227
9228 #if 1
9229   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9230 #else
9231   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9232 #endif
9233   PlayLevelSound(jx, jy, SND_GAME_LOSING);
9234
9235   player->GameOver = TRUE;
9236   RemoveHero(player);
9237 }
9238
9239 void RemoveHero(struct PlayerInfo *player)
9240 {
9241   int jx = player->jx, jy = player->jy;
9242   int i, found = FALSE;
9243
9244   player->present = FALSE;
9245   player->active = FALSE;
9246
9247   if (!ExplodeField[jx][jy])
9248     StorePlayer[jx][jy] = 0;
9249
9250   for (i = 0; i < MAX_PLAYERS; i++)
9251     if (stored_player[i].active)
9252       found = TRUE;
9253
9254   if (!found)
9255     AllPlayersGone = TRUE;
9256
9257   ExitX = ZX = jx;
9258   ExitY = ZY = jy;
9259 }
9260
9261 /*
9262   =============================================================================
9263   checkDiagonalPushing()
9264   -----------------------------------------------------------------------------
9265   check if diagonal input device direction results in pushing of object
9266   (by checking if the alternative direction is walkable, diggable, ...)
9267   =============================================================================
9268 */
9269
9270 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9271                                     int x, int y, int real_dx, int real_dy)
9272 {
9273   int jx, jy, dx, dy, xx, yy;
9274
9275   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
9276     return TRUE;
9277
9278   /* diagonal direction: check alternative direction */
9279   jx = player->jx;
9280   jy = player->jy;
9281   dx = x - jx;
9282   dy = y - jy;
9283   xx = jx + (dx == 0 ? real_dx : 0);
9284   yy = jy + (dy == 0 ? real_dy : 0);
9285
9286   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9287 }
9288
9289 /*
9290   =============================================================================
9291   DigField()
9292   -----------------------------------------------------------------------------
9293   x, y:                 field next to player (non-diagonal) to try to dig to
9294   real_dx, real_dy:     direction as read from input device (can be diagonal)
9295   =============================================================================
9296 */
9297
9298 int DigField(struct PlayerInfo *player,
9299              int oldx, int oldy, int x, int y,
9300              int real_dx, int real_dy, int mode)
9301 {
9302   static int trigger_sides[4] =
9303   {
9304     CH_SIDE_RIGHT,      /* moving left  */
9305     CH_SIDE_LEFT,       /* moving right */
9306     CH_SIDE_BOTTOM,     /* moving up    */
9307     CH_SIDE_TOP,        /* moving down  */
9308   };
9309 #if 0
9310   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9311 #endif
9312   int jx = oldx, jy = oldy;
9313   int dx = x - jx, dy = y - jy;
9314   int nextx = x + dx, nexty = y + dy;
9315   int move_direction = (dx == -1 ? MV_LEFT :
9316                         dx == +1 ? MV_RIGHT :
9317                         dy == -1 ? MV_UP :
9318                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9319   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9320   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
9321   int old_element = Feld[jx][jy];
9322   int element;
9323
9324   if (player->MovPos == 0)
9325   {
9326     player->is_digging = FALSE;
9327     player->is_collecting = FALSE;
9328   }
9329
9330   if (player->MovPos == 0)      /* last pushing move finished */
9331     player->is_pushing = FALSE;
9332
9333   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
9334   {
9335     player->is_switching = FALSE;
9336     player->push_delay = 0;
9337
9338     return MF_NO_ACTION;
9339   }
9340
9341   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9342     return MF_NO_ACTION;
9343
9344 #if 0
9345
9346 #if 0
9347   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9348 #else
9349   if (IS_TUBE(Feld[jx][jy]) ||
9350       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9351 #endif
9352   {
9353     int i = 0;
9354     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9355     int tube_leave_directions[][2] =
9356     {
9357       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9358       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
9359       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
9360       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
9361       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
9362       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
9363       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
9364       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
9365       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
9366       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
9367       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
9368       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9369     };
9370
9371     while (tube_leave_directions[i][0] != tube_element)
9372     {
9373       i++;
9374       if (tube_leave_directions[i][0] == -1)    /* should not happen */
9375         break;
9376     }
9377
9378     if (!(tube_leave_directions[i][1] & move_direction))
9379       return MF_NO_ACTION;      /* tube has no opening in this direction */
9380   }
9381
9382 #else
9383
9384   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9385     old_element = Back[jx][jy];
9386
9387 #endif
9388
9389   if (IS_WALKABLE(old_element) &&
9390       !(element_info[old_element].access_direction & move_direction))
9391     return MF_NO_ACTION;        /* field has no opening in this direction */
9392
9393   element = Feld[x][y];
9394
9395   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9396       game.engine_version >= VERSION_IDENT(2,2,0,0))
9397     return MF_NO_ACTION;
9398
9399   switch (element)
9400   {
9401     case EL_SP_PORT_LEFT:
9402     case EL_SP_PORT_RIGHT:
9403     case EL_SP_PORT_UP:
9404     case EL_SP_PORT_DOWN:
9405     case EL_SP_PORT_HORIZONTAL:
9406     case EL_SP_PORT_VERTICAL:
9407     case EL_SP_PORT_ANY:
9408     case EL_SP_GRAVITY_PORT_LEFT:
9409     case EL_SP_GRAVITY_PORT_RIGHT:
9410     case EL_SP_GRAVITY_PORT_UP:
9411     case EL_SP_GRAVITY_PORT_DOWN:
9412 #if 1
9413       if (!canEnterSupaplexPort(x, y, dx, dy))
9414         return MF_NO_ACTION;
9415 #else
9416       if ((dx == -1 &&
9417            element != EL_SP_PORT_LEFT &&
9418            element != EL_SP_GRAVITY_PORT_LEFT &&
9419            element != EL_SP_PORT_HORIZONTAL &&
9420            element != EL_SP_PORT_ANY) ||
9421           (dx == +1 &&
9422            element != EL_SP_PORT_RIGHT &&
9423            element != EL_SP_GRAVITY_PORT_RIGHT &&
9424            element != EL_SP_PORT_HORIZONTAL &&
9425            element != EL_SP_PORT_ANY) ||
9426           (dy == -1 &&
9427            element != EL_SP_PORT_UP &&
9428            element != EL_SP_GRAVITY_PORT_UP &&
9429            element != EL_SP_PORT_VERTICAL &&
9430            element != EL_SP_PORT_ANY) ||
9431           (dy == +1 &&
9432            element != EL_SP_PORT_DOWN &&
9433            element != EL_SP_GRAVITY_PORT_DOWN &&
9434            element != EL_SP_PORT_VERTICAL &&
9435            element != EL_SP_PORT_ANY) ||
9436           !IN_LEV_FIELD(nextx, nexty) ||
9437           !IS_FREE(nextx, nexty))
9438         return MF_NO_ACTION;
9439 #endif
9440
9441       if (element == EL_SP_GRAVITY_PORT_LEFT ||
9442           element == EL_SP_GRAVITY_PORT_RIGHT ||
9443           element == EL_SP_GRAVITY_PORT_UP ||
9444           element == EL_SP_GRAVITY_PORT_DOWN)
9445         game.gravity = !game.gravity;
9446
9447       /* automatically move to the next field with double speed */
9448       player->programmed_action = move_direction;
9449 #if 1
9450       if (player->move_delay_reset_counter == 0)
9451       {
9452         player->move_delay_reset_counter = 2;   /* two double speed steps */
9453
9454         DOUBLE_PLAYER_SPEED(player);
9455       }
9456 #else
9457       player->move_delay_reset_counter = 2;
9458
9459       DOUBLE_PLAYER_SPEED(player);
9460 #endif
9461
9462       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9463       break;
9464
9465 #if 0
9466     case EL_TUBE_ANY:
9467     case EL_TUBE_VERTICAL:
9468     case EL_TUBE_HORIZONTAL:
9469     case EL_TUBE_VERTICAL_LEFT:
9470     case EL_TUBE_VERTICAL_RIGHT:
9471     case EL_TUBE_HORIZONTAL_UP:
9472     case EL_TUBE_HORIZONTAL_DOWN:
9473     case EL_TUBE_LEFT_UP:
9474     case EL_TUBE_LEFT_DOWN:
9475     case EL_TUBE_RIGHT_UP:
9476     case EL_TUBE_RIGHT_DOWN:
9477       {
9478         int i = 0;
9479         int tube_enter_directions[][2] =
9480         {
9481           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9482           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
9483           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
9484           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
9485           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
9486           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
9487           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
9488           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
9489           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
9490           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
9491           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
9492           { -1,                         MV_NO_MOVING                         }
9493         };
9494
9495         while (tube_enter_directions[i][0] != element)
9496         {
9497           i++;
9498           if (tube_enter_directions[i][0] == -1)        /* should not happen */
9499             break;
9500         }
9501
9502         if (!(tube_enter_directions[i][1] & move_direction))
9503           return MF_NO_ACTION;  /* tube has no opening in this direction */
9504
9505         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9506       }
9507       break;
9508 #endif
9509
9510     default:
9511
9512       if (IS_WALKABLE(element))
9513       {
9514         int sound_action = ACTION_WALKING;
9515
9516         if (!(element_info[element].access_direction & opposite_direction))
9517           return MF_NO_ACTION;  /* field not accessible from this direction */
9518
9519         if (element >= EL_GATE_1 && element <= EL_GATE_4)
9520         {
9521           if (!player->key[element - EL_GATE_1])
9522             return MF_NO_ACTION;
9523         }
9524         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9525         {
9526           if (!player->key[element - EL_GATE_1_GRAY])
9527             return MF_NO_ACTION;
9528         }
9529         else if (element == EL_EXIT_OPEN ||
9530                  element == EL_SP_EXIT_OPEN ||
9531                  element == EL_SP_EXIT_OPENING)
9532         {
9533           sound_action = ACTION_PASSING;        /* player is passing exit */
9534         }
9535         else if (element == EL_EMPTY)
9536         {
9537           sound_action = ACTION_MOVING;         /* nothing to walk on */
9538         }
9539
9540         /* play sound from background or player, whatever is available */
9541         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9542           PlayLevelSoundElementAction(x, y, element, sound_action);
9543         else
9544           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9545
9546         break;
9547       }
9548       else if (IS_PASSABLE(element))
9549       {
9550         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9551           return MF_NO_ACTION;
9552
9553         if (IS_CUSTOM_ELEMENT(element) &&
9554             !(element_info[element].access_direction & opposite_direction))
9555           return MF_NO_ACTION;  /* field not accessible from this direction */
9556
9557 #if 1
9558         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
9559           return MF_NO_ACTION;
9560 #endif
9561
9562         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9563         {
9564           if (!player->key[element - EL_EM_GATE_1])
9565             return MF_NO_ACTION;
9566         }
9567         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9568         {
9569           if (!player->key[element - EL_EM_GATE_1_GRAY])
9570             return MF_NO_ACTION;
9571         }
9572
9573         /* automatically move to the next field with double speed */
9574         player->programmed_action = move_direction;
9575 #if 1
9576         if (player->move_delay_reset_counter == 0)
9577         {
9578           player->move_delay_reset_counter = 2; /* two double speed steps */
9579
9580           DOUBLE_PLAYER_SPEED(player);
9581         }
9582 #else
9583         player->move_delay_reset_counter = 2;
9584
9585         DOUBLE_PLAYER_SPEED(player);
9586 #endif
9587
9588         PlayLevelSoundAction(x, y, ACTION_PASSING);
9589
9590         break;
9591       }
9592       else if (IS_DIGGABLE(element))
9593       {
9594         RemoveField(x, y);
9595
9596         if (mode != DF_SNAP)
9597         {
9598 #if 1
9599           GfxElement[x][y] = GFX_ELEMENT(element);
9600 #else
9601           GfxElement[x][y] =
9602             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9603 #endif
9604           player->is_digging = TRUE;
9605         }
9606
9607         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9608
9609         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
9610                                           player->index_bit, CH_SIDE_ANY);
9611
9612 #if 1
9613         if (mode == DF_SNAP)
9614           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9615 #endif
9616
9617         break;
9618       }
9619       else if (IS_COLLECTIBLE(element))
9620       {
9621         RemoveField(x, y);
9622
9623         if (mode != DF_SNAP)
9624         {
9625           GfxElement[x][y] = element;
9626           player->is_collecting = TRUE;
9627         }
9628
9629         if (element == EL_SPEED_PILL)
9630           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9631         else if (element == EL_EXTRA_TIME && level.time > 0)
9632         {
9633           TimeLeft += 10;
9634           DrawGameValue_Time(TimeLeft);
9635         }
9636         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9637         {
9638           player->shield_normal_time_left += 10;
9639           if (element == EL_SHIELD_DEADLY)
9640             player->shield_deadly_time_left += 10;
9641         }
9642         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9643         {
9644           if (player->inventory_size < MAX_INVENTORY_SIZE)
9645             player->inventory_element[player->inventory_size++] = element;
9646
9647           DrawGameValue_Dynamite(local_player->inventory_size);
9648         }
9649         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9650         {
9651           player->dynabomb_count++;
9652           player->dynabombs_left++;
9653         }
9654         else if (element == EL_DYNABOMB_INCREASE_SIZE)
9655         {
9656           player->dynabomb_size++;
9657         }
9658         else if (element == EL_DYNABOMB_INCREASE_POWER)
9659         {
9660           player->dynabomb_xl = TRUE;
9661         }
9662         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9663                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9664         {
9665           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9666                         element - EL_KEY_1 : element - EL_EM_KEY_1);
9667
9668           player->key[key_nr] = TRUE;
9669
9670           DrawGameValue_Keys(player);
9671
9672           redraw_mask |= REDRAW_DOOR_1;
9673         }
9674         else if (IS_ENVELOPE(element))
9675         {
9676 #if 1
9677           player->show_envelope = element;
9678 #else
9679           ShowEnvelope(element - EL_ENVELOPE_1);
9680 #endif
9681         }
9682         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9683         {
9684           int i;
9685
9686           if (element_info[element].collect_count == 0)
9687             player->inventory_infinite_element = element;
9688           else
9689             for (i = 0; i < element_info[element].collect_count; i++)
9690               if (player->inventory_size < MAX_INVENTORY_SIZE)
9691                 player->inventory_element[player->inventory_size++] = element;
9692
9693           DrawGameValue_Dynamite(local_player->inventory_size);
9694         }
9695         else if (element_info[element].collect_count > 0)
9696         {
9697           local_player->gems_still_needed -=
9698             element_info[element].collect_count;
9699           if (local_player->gems_still_needed < 0)
9700             local_player->gems_still_needed = 0;
9701
9702           DrawGameValue_Emeralds(local_player->gems_still_needed);
9703         }
9704
9705         RaiseScoreElement(element);
9706         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9707
9708         CheckTriggeredElementChangePlayer(x, y, element,
9709                                           CE_OTHER_GETS_COLLECTED,
9710                                           player->index_bit, CH_SIDE_ANY);
9711
9712 #if 1
9713         if (mode == DF_SNAP)
9714           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9715 #endif
9716
9717         break;
9718       }
9719       else if (IS_PUSHABLE(element))
9720       {
9721         if (mode == DF_SNAP && element != EL_BD_ROCK)
9722           return MF_NO_ACTION;
9723
9724         if (CAN_FALL(element) && dy)
9725           return MF_NO_ACTION;
9726
9727         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9728             !(element == EL_SPRING && level.use_spring_bug))
9729           return MF_NO_ACTION;
9730
9731 #if 1
9732         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9733             ((move_direction & MV_VERTICAL &&
9734               ((element_info[element].move_pattern & MV_LEFT &&
9735                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9736                (element_info[element].move_pattern & MV_RIGHT &&
9737                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9738              (move_direction & MV_HORIZONTAL &&
9739               ((element_info[element].move_pattern & MV_UP &&
9740                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9741                (element_info[element].move_pattern & MV_DOWN &&
9742                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9743           return MF_NO_ACTION;
9744 #endif
9745
9746 #if 1
9747         /* do not push elements already moving away faster than player */
9748         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9749             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9750           return MF_NO_ACTION;
9751 #else
9752         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9753           return MF_NO_ACTION;
9754 #endif
9755
9756 #if 1
9757         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9758         {
9759           if (player->push_delay_value == -1)
9760             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9761         }
9762         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9763         {
9764           if (!player->is_pushing)
9765             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9766         }
9767
9768         /*
9769         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9770             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9771              !player_is_pushing))
9772           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9773         */
9774 #else
9775         if (!player->is_pushing &&
9776             game.engine_version >= VERSION_IDENT(2,2,0,7))
9777           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9778 #endif
9779
9780 #if 0
9781         printf("::: push delay: %ld [%d, %d] [%d]\n",
9782                player->push_delay_value, FrameCounter, game.engine_version,
9783                player->is_pushing);
9784 #endif
9785
9786         player->is_pushing = TRUE;
9787
9788         if (!(IN_LEV_FIELD(nextx, nexty) &&
9789               (IS_FREE(nextx, nexty) ||
9790                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9791                 IS_SB_ELEMENT(element)))))
9792           return MF_NO_ACTION;
9793
9794         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9795           return MF_NO_ACTION;
9796
9797         if (player->push_delay == 0)    /* new pushing; restart delay */
9798           player->push_delay = FrameCounter;
9799
9800         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9801             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9802             element != EL_SPRING && element != EL_BALLOON)
9803         {
9804           /* make sure that there is no move delay before next try to push */
9805           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9806             player->move_delay = INITIAL_MOVE_DELAY_OFF;
9807
9808           return MF_NO_ACTION;
9809         }
9810
9811 #if 0
9812         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9813 #endif
9814
9815         if (IS_SB_ELEMENT(element))
9816         {
9817           if (element == EL_SOKOBAN_FIELD_FULL)
9818           {
9819             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9820             local_player->sokobanfields_still_needed++;
9821           }
9822
9823           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9824           {
9825             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9826             local_player->sokobanfields_still_needed--;
9827           }
9828
9829           Feld[x][y] = EL_SOKOBAN_OBJECT;
9830
9831           if (Back[x][y] == Back[nextx][nexty])
9832             PlayLevelSoundAction(x, y, ACTION_PUSHING);
9833           else if (Back[x][y] != 0)
9834             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9835                                         ACTION_EMPTYING);
9836           else
9837             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9838                                         ACTION_FILLING);
9839
9840           if (local_player->sokobanfields_still_needed == 0 &&
9841               game.emulation == EMU_SOKOBAN)
9842           {
9843             player->LevelSolved = player->GameOver = TRUE;
9844             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9845           }
9846         }
9847         else
9848           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9849
9850         InitMovingField(x, y, move_direction);
9851         GfxAction[x][y] = ACTION_PUSHING;
9852
9853         if (mode == DF_SNAP)
9854           ContinueMoving(x, y);
9855         else
9856           MovPos[x][y] = (dx != 0 ? dx : dy);
9857
9858         Pushed[x][y] = TRUE;
9859         Pushed[nextx][nexty] = TRUE;
9860
9861         if (game.engine_version < VERSION_IDENT(2,2,0,7))
9862           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9863         else
9864           player->push_delay_value = -1;        /* get new value later */
9865
9866         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
9867                                           player->index_bit, dig_side);
9868         CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9869                                  player->index_bit, dig_side);
9870
9871         break;
9872       }
9873       else if (IS_SWITCHABLE(element))
9874       {
9875         if (PLAYER_SWITCHING(player, x, y))
9876           return MF_ACTION;
9877
9878         player->is_switching = TRUE;
9879         player->switch_x = x;
9880         player->switch_y = y;
9881
9882         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9883
9884         if (element == EL_ROBOT_WHEEL)
9885         {
9886           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9887           ZX = x;
9888           ZY = y;
9889
9890           DrawLevelField(x, y);
9891         }
9892         else if (element == EL_SP_TERMINAL)
9893         {
9894           int xx, yy;
9895
9896           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9897           {
9898             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9899               Bang(xx, yy);
9900             else if (Feld[xx][yy] == EL_SP_TERMINAL)
9901               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9902           }
9903         }
9904         else if (IS_BELT_SWITCH(element))
9905         {
9906           ToggleBeltSwitch(x, y);
9907         }
9908         else if (element == EL_SWITCHGATE_SWITCH_UP ||
9909                  element == EL_SWITCHGATE_SWITCH_DOWN)
9910         {
9911           ToggleSwitchgateSwitch(x, y);
9912         }
9913         else if (element == EL_LIGHT_SWITCH ||
9914                  element == EL_LIGHT_SWITCH_ACTIVE)
9915         {
9916           ToggleLightSwitch(x, y);
9917
9918 #if 0
9919           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9920                          SND_LIGHT_SWITCH_ACTIVATING :
9921                          SND_LIGHT_SWITCH_DEACTIVATING);
9922 #endif
9923         }
9924         else if (element == EL_TIMEGATE_SWITCH)
9925         {
9926           ActivateTimegateSwitch(x, y);
9927         }
9928         else if (element == EL_BALLOON_SWITCH_LEFT ||
9929                  element == EL_BALLOON_SWITCH_RIGHT ||
9930                  element == EL_BALLOON_SWITCH_UP ||
9931                  element == EL_BALLOON_SWITCH_DOWN ||
9932                  element == EL_BALLOON_SWITCH_ANY)
9933         {
9934           if (element == EL_BALLOON_SWITCH_ANY)
9935             game.balloon_dir = move_direction;
9936           else
9937             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
9938                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9939                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
9940                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
9941                                 MV_NO_MOVING);
9942         }
9943         else if (element == EL_LAMP)
9944         {
9945           Feld[x][y] = EL_LAMP_ACTIVE;
9946           local_player->lights_still_needed--;
9947
9948           DrawLevelField(x, y);
9949         }
9950         else if (element == EL_TIME_ORB_FULL)
9951         {
9952           Feld[x][y] = EL_TIME_ORB_EMPTY;
9953           TimeLeft += 10;
9954           DrawGameValue_Time(TimeLeft);
9955
9956           DrawLevelField(x, y);
9957
9958 #if 0
9959           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9960 #endif
9961         }
9962
9963         return MF_ACTION;
9964       }
9965       else
9966       {
9967         if (!PLAYER_SWITCHING(player, x, y))
9968         {
9969           player->is_switching = TRUE;
9970           player->switch_x = x;
9971           player->switch_y = y;
9972
9973           CheckTriggeredElementChangePlayer(x, y, element,
9974                                             CE_OTHER_IS_SWITCHING,
9975                                             player->index_bit, dig_side);
9976           CheckElementChangePlayer(x, y, element, CE_SWITCHED,
9977                                    player->index_bit, dig_side);
9978         }
9979
9980         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
9981                                           player->index_bit, dig_side);
9982         CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
9983                                  player->index_bit, dig_side);
9984       }
9985
9986       return MF_NO_ACTION;
9987   }
9988
9989   player->push_delay = 0;
9990
9991   if (Feld[x][y] != element)            /* really digged/collected something */
9992     player->is_collecting = !player->is_digging;
9993
9994   return MF_MOVING;
9995 }
9996
9997 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9998 {
9999   int jx = player->jx, jy = player->jy;
10000   int x = jx + dx, y = jy + dy;
10001   int snap_direction = (dx == -1 ? MV_LEFT :
10002                         dx == +1 ? MV_RIGHT :
10003                         dy == -1 ? MV_UP :
10004                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10005
10006   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10007     return FALSE;
10008
10009   if (!player->active || !IN_LEV_FIELD(x, y))
10010     return FALSE;
10011
10012   if (dx && dy)
10013     return FALSE;
10014
10015   if (!dx && !dy)
10016   {
10017     if (player->MovPos == 0)
10018       player->is_pushing = FALSE;
10019
10020     player->is_snapping = FALSE;
10021
10022     if (player->MovPos == 0)
10023     {
10024       player->is_moving = FALSE;
10025       player->is_digging = FALSE;
10026       player->is_collecting = FALSE;
10027     }
10028
10029     return FALSE;
10030   }
10031
10032   if (player->is_snapping)
10033     return FALSE;
10034
10035   player->MovDir = snap_direction;
10036
10037 #if 1
10038   if (player->MovPos == 0)
10039 #endif
10040   {
10041     player->is_moving = FALSE;
10042     player->is_digging = FALSE;
10043     player->is_collecting = FALSE;
10044   }
10045
10046   player->is_dropping = FALSE;
10047
10048   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10049     return FALSE;
10050
10051   player->is_snapping = TRUE;
10052
10053 #if 1
10054   if (player->MovPos == 0)
10055 #endif
10056   {
10057     player->is_moving = FALSE;
10058     player->is_digging = FALSE;
10059     player->is_collecting = FALSE;
10060   }
10061
10062   DrawLevelField(x, y);
10063   BackToFront();
10064
10065   return TRUE;
10066 }
10067
10068 boolean DropElement(struct PlayerInfo *player)
10069 {
10070   int jx = player->jx, jy = player->jy;
10071   int old_element = Feld[jx][jy];
10072   int new_element = (player->inventory_size > 0 ?
10073                      player->inventory_element[player->inventory_size - 1] :
10074                      player->inventory_infinite_element != EL_UNDEFINED ?
10075                      player->inventory_infinite_element :
10076                      player->dynabombs_left > 0 ?
10077                      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10078                      EL_UNDEFINED);
10079
10080   /* check if player is active, not moving and ready to drop */
10081   if (!player->active || player->MovPos || player->drop_delay > 0)
10082     return FALSE;
10083
10084   /* check if player has anything that can be dropped */
10085 #if 1
10086   if (new_element == EL_UNDEFINED)
10087     return FALSE;
10088 #else
10089   if (player->inventory_size == 0 &&
10090       player->inventory_infinite_element == EL_UNDEFINED &&
10091       player->dynabombs_left == 0)
10092     return FALSE;
10093 #endif
10094
10095   /* check if anything can be dropped at the current position */
10096   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10097     return FALSE;
10098
10099   /* collected custom elements can only be dropped on empty fields */
10100 #if 1
10101   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10102     return FALSE;
10103 #else
10104   if (player->inventory_size > 0 &&
10105       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10106       && old_element != EL_EMPTY)
10107     return FALSE;
10108 #endif
10109
10110   if (old_element != EL_EMPTY)
10111     Back[jx][jy] = old_element;         /* store old element on this field */
10112
10113   ResetGfxAnimation(jx, jy);
10114   ResetRandomAnimationValue(jx, jy);
10115
10116   if (player->inventory_size > 0 ||
10117       player->inventory_infinite_element != EL_UNDEFINED)
10118   {
10119     if (player->inventory_size > 0)
10120     {
10121       player->inventory_size--;
10122
10123 #if 0
10124       new_element = player->inventory_element[player->inventory_size];
10125 #endif
10126
10127       DrawGameValue_Dynamite(local_player->inventory_size);
10128
10129       if (new_element == EL_DYNAMITE)
10130         new_element = EL_DYNAMITE_ACTIVE;
10131       else if (new_element == EL_SP_DISK_RED)
10132         new_element = EL_SP_DISK_RED_ACTIVE;
10133     }
10134
10135     Feld[jx][jy] = new_element;
10136
10137     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10138       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10139
10140     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10141
10142 #if 1
10143     /* needed if previous element just changed to "empty" in the last frame */
10144     Changed[jx][jy] = 0;                /* allow another change */
10145 #endif
10146
10147     CheckTriggeredElementChangePlayer(jx, jy, new_element,
10148                                       CE_OTHER_GETS_DROPPED,
10149                                       player->index_bit, CH_SIDE_ANY);
10150     CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10151                              player->index_bit, CH_SIDE_ANY);
10152
10153     TestIfElementTouchesCustomElement(jx, jy);
10154   }
10155   else          /* player is dropping a dyna bomb */
10156   {
10157     player->dynabombs_left--;
10158
10159 #if 0
10160     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10161 #endif
10162
10163     Feld[jx][jy] = new_element;
10164
10165     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10166       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10167
10168     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10169   }
10170
10171
10172
10173 #if 1
10174
10175   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
10176   {
10177 #if 1
10178     InitField_WithBug1(jx, jy, FALSE);
10179 #else
10180     InitField(jx, jy, FALSE);
10181     if (CAN_MOVE(Feld[jx][jy]))
10182       InitMovDir(jx, jy);
10183 #endif
10184   }
10185
10186   new_element = Feld[jx][jy];
10187
10188   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10189       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10190   {
10191     int move_stepsize = element_info[new_element].move_stepsize;
10192     int direction, dx, dy, nextx, nexty;
10193
10194     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10195       MovDir[jx][jy] = player->MovDir;
10196
10197     direction = MovDir[jx][jy];
10198     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10199     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10200     nextx = jx + dx;
10201     nexty = jy + dy;
10202
10203     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10204     {
10205 #if 0
10206       WasJustMoving[jx][jy] = 3;
10207 #else
10208       InitMovingField(jx, jy, direction);
10209       ContinueMoving(jx, jy);
10210 #endif
10211     }
10212     else
10213     {
10214       Changed[jx][jy] = 0;              /* allow another change */
10215
10216 #if 1
10217       TestIfElementHitsCustomElement(jx, jy, direction);
10218 #else
10219       CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
10220                              direction);
10221 #endif
10222     }
10223
10224     player->drop_delay = 2 * TILEX / move_stepsize + 1;
10225   }
10226
10227 #if 0
10228   player->drop_delay = 8 + 8 + 8;
10229 #endif
10230
10231 #endif
10232
10233   player->is_dropping = TRUE;
10234
10235
10236   return TRUE;
10237 }
10238
10239 /* ------------------------------------------------------------------------- */
10240 /* game sound playing functions                                              */
10241 /* ------------------------------------------------------------------------- */
10242
10243 static int *loop_sound_frame = NULL;
10244 static int *loop_sound_volume = NULL;
10245
10246 void InitPlayLevelSound()
10247 {
10248   int num_sounds = getSoundListSize();
10249
10250   checked_free(loop_sound_frame);
10251   checked_free(loop_sound_volume);
10252
10253   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
10254   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10255 }
10256
10257 static void PlayLevelSound(int x, int y, int nr)
10258 {
10259   int sx = SCREENX(x), sy = SCREENY(y);
10260   int volume, stereo_position;
10261   int max_distance = 8;
10262   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10263
10264   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10265       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10266     return;
10267
10268   if (!IN_LEV_FIELD(x, y) ||
10269       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10270       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10271     return;
10272
10273   volume = SOUND_MAX_VOLUME;
10274
10275   if (!IN_SCR_FIELD(sx, sy))
10276   {
10277     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10278     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10279
10280     volume -= volume * (dx > dy ? dx : dy) / max_distance;
10281   }
10282
10283   stereo_position = (SOUND_MAX_LEFT +
10284                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10285                      (SCR_FIELDX + 2 * max_distance));
10286
10287   if (IS_LOOP_SOUND(nr))
10288   {
10289     /* This assures that quieter loop sounds do not overwrite louder ones,
10290        while restarting sound volume comparison with each new game frame. */
10291
10292     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10293       return;
10294
10295     loop_sound_volume[nr] = volume;
10296     loop_sound_frame[nr] = FrameCounter;
10297   }
10298
10299   PlaySoundExt(nr, volume, stereo_position, type);
10300 }
10301
10302 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10303 {
10304   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10305                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
10306                  y < LEVELY(BY1) ? LEVELY(BY1) :
10307                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
10308                  sound_action);
10309 }
10310
10311 static void PlayLevelSoundAction(int x, int y, int action)
10312 {
10313   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10314 }
10315
10316 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10317 {
10318   int sound_effect = element_info[element].sound[action];
10319
10320   if (sound_effect != SND_UNDEFINED)
10321     PlayLevelSound(x, y, sound_effect);
10322 }
10323
10324 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10325                                               int action)
10326 {
10327   int sound_effect = element_info[element].sound[action];
10328
10329   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10330     PlayLevelSound(x, y, sound_effect);
10331 }
10332
10333 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10334 {
10335   int sound_effect = element_info[Feld[x][y]].sound[action];
10336
10337   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10338     PlayLevelSound(x, y, sound_effect);
10339 }
10340
10341 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10342 {
10343   int sound_effect = element_info[Feld[x][y]].sound[action];
10344
10345   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10346     StopSound(sound_effect);
10347 }
10348
10349 static void PlayLevelMusic()
10350 {
10351   if (levelset.music[level_nr] != MUS_UNDEFINED)
10352     PlayMusic(levelset.music[level_nr]);        /* from config file */
10353   else
10354     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
10355 }
10356
10357 void RaiseScore(int value)
10358 {
10359   local_player->score += value;
10360
10361   DrawGameValue_Score(local_player->score);
10362 }
10363
10364 void RaiseScoreElement(int element)
10365 {
10366   switch(element)
10367   {
10368     case EL_EMERALD:
10369     case EL_BD_DIAMOND:
10370     case EL_EMERALD_YELLOW:
10371     case EL_EMERALD_RED:
10372     case EL_EMERALD_PURPLE:
10373     case EL_SP_INFOTRON:
10374       RaiseScore(level.score[SC_EMERALD]);
10375       break;
10376     case EL_DIAMOND:
10377       RaiseScore(level.score[SC_DIAMOND]);
10378       break;
10379     case EL_CRYSTAL:
10380       RaiseScore(level.score[SC_CRYSTAL]);
10381       break;
10382     case EL_PEARL:
10383       RaiseScore(level.score[SC_PEARL]);
10384       break;
10385     case EL_BUG:
10386     case EL_BD_BUTTERFLY:
10387     case EL_SP_ELECTRON:
10388       RaiseScore(level.score[SC_BUG]);
10389       break;
10390     case EL_SPACESHIP:
10391     case EL_BD_FIREFLY:
10392     case EL_SP_SNIKSNAK:
10393       RaiseScore(level.score[SC_SPACESHIP]);
10394       break;
10395     case EL_YAMYAM:
10396     case EL_DARK_YAMYAM:
10397       RaiseScore(level.score[SC_YAMYAM]);
10398       break;
10399     case EL_ROBOT:
10400       RaiseScore(level.score[SC_ROBOT]);
10401       break;
10402     case EL_PACMAN:
10403       RaiseScore(level.score[SC_PACMAN]);
10404       break;
10405     case EL_NUT:
10406       RaiseScore(level.score[SC_NUT]);
10407       break;
10408     case EL_DYNAMITE:
10409     case EL_SP_DISK_RED:
10410     case EL_DYNABOMB_INCREASE_NUMBER:
10411     case EL_DYNABOMB_INCREASE_SIZE:
10412     case EL_DYNABOMB_INCREASE_POWER:
10413       RaiseScore(level.score[SC_DYNAMITE]);
10414       break;
10415     case EL_SHIELD_NORMAL:
10416     case EL_SHIELD_DEADLY:
10417       RaiseScore(level.score[SC_SHIELD]);
10418       break;
10419     case EL_EXTRA_TIME:
10420       RaiseScore(level.score[SC_TIME_BONUS]);
10421       break;
10422     case EL_KEY_1:
10423     case EL_KEY_2:
10424     case EL_KEY_3:
10425     case EL_KEY_4:
10426       RaiseScore(level.score[SC_KEY]);
10427       break;
10428     default:
10429       RaiseScore(element_info[element].collect_score);
10430       break;
10431   }
10432 }
10433
10434 void RequestQuitGame(boolean ask_if_really_quit)
10435 {
10436   if (AllPlayersGone ||
10437       !ask_if_really_quit ||
10438       level_editor_test_game ||
10439       Request("Do you really want to quit the game ?",
10440               REQ_ASK | REQ_STAY_CLOSED))
10441   {
10442 #if defined(PLATFORM_UNIX)
10443     if (options.network)
10444       SendToServer_StopPlaying();
10445     else
10446 #endif
10447     {
10448       game_status = GAME_MODE_MAIN;
10449       DrawMainMenu();
10450     }
10451   }
10452   else
10453   {
10454     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10455   }
10456 }
10457
10458
10459 /* ---------- new game button stuff ---------------------------------------- */
10460
10461 /* graphic position values for game buttons */
10462 #define GAME_BUTTON_XSIZE       30
10463 #define GAME_BUTTON_YSIZE       30
10464 #define GAME_BUTTON_XPOS        5
10465 #define GAME_BUTTON_YPOS        215
10466 #define SOUND_BUTTON_XPOS       5
10467 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10468
10469 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10470 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10471 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10472 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10473 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10474 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10475
10476 static struct
10477 {
10478   int x, y;
10479   int gadget_id;
10480   char *infotext;
10481 } gamebutton_info[NUM_GAME_BUTTONS] =
10482 {
10483   {
10484     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
10485     GAME_CTRL_ID_STOP,
10486     "stop game"
10487   },
10488   {
10489     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
10490     GAME_CTRL_ID_PAUSE,
10491     "pause game"
10492   },
10493   {
10494     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
10495     GAME_CTRL_ID_PLAY,
10496     "play game"
10497   },
10498   {
10499     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
10500     SOUND_CTRL_ID_MUSIC,
10501     "background music on/off"
10502   },
10503   {
10504     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
10505     SOUND_CTRL_ID_LOOPS,
10506     "sound loops on/off"
10507   },
10508   {
10509     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
10510     SOUND_CTRL_ID_SIMPLE,
10511     "normal sounds on/off"
10512   }
10513 };
10514
10515 void CreateGameButtons()
10516 {
10517   int i;
10518
10519   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10520   {
10521     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10522     struct GadgetInfo *gi;
10523     int button_type;
10524     boolean checked;
10525     unsigned long event_mask;
10526     int gd_xoffset, gd_yoffset;
10527     int gd_x1, gd_x2, gd_y1, gd_y2;
10528     int id = i;
10529
10530     gd_xoffset = gamebutton_info[i].x;
10531     gd_yoffset = gamebutton_info[i].y;
10532     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10533     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10534
10535     if (id == GAME_CTRL_ID_STOP ||
10536         id == GAME_CTRL_ID_PAUSE ||
10537         id == GAME_CTRL_ID_PLAY)
10538     {
10539       button_type = GD_TYPE_NORMAL_BUTTON;
10540       checked = FALSE;
10541       event_mask = GD_EVENT_RELEASED;
10542       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10543       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10544     }
10545     else
10546     {
10547       button_type = GD_TYPE_CHECK_BUTTON;
10548       checked =
10549         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10550          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10551          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10552       event_mask = GD_EVENT_PRESSED;
10553       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
10554       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10555     }
10556
10557     gi = CreateGadget(GDI_CUSTOM_ID, id,
10558                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
10559                       GDI_X, DX + gd_xoffset,
10560                       GDI_Y, DY + gd_yoffset,
10561                       GDI_WIDTH, GAME_BUTTON_XSIZE,
10562                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
10563                       GDI_TYPE, button_type,
10564                       GDI_STATE, GD_BUTTON_UNPRESSED,
10565                       GDI_CHECKED, checked,
10566                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10567                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10568                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10569                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10570                       GDI_EVENT_MASK, event_mask,
10571                       GDI_CALLBACK_ACTION, HandleGameButtons,
10572                       GDI_END);
10573
10574     if (gi == NULL)
10575       Error(ERR_EXIT, "cannot create gadget");
10576
10577     game_gadget[id] = gi;
10578   }
10579 }
10580
10581 void FreeGameButtons()
10582 {
10583   int i;
10584
10585   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10586     FreeGadget(game_gadget[i]);
10587 }
10588
10589 static void MapGameButtons()
10590 {
10591   int i;
10592
10593   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10594     MapGadget(game_gadget[i]);
10595 }
10596
10597 void UnmapGameButtons()
10598 {
10599   int i;
10600
10601   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10602     UnmapGadget(game_gadget[i]);
10603 }
10604
10605 static void HandleGameButtons(struct GadgetInfo *gi)
10606 {
10607   int id = gi->custom_id;
10608
10609   if (game_status != GAME_MODE_PLAYING)
10610     return;
10611
10612   switch (id)
10613   {
10614     case GAME_CTRL_ID_STOP:
10615       RequestQuitGame(TRUE);
10616       break;
10617
10618     case GAME_CTRL_ID_PAUSE:
10619       if (options.network)
10620       {
10621 #if defined(PLATFORM_UNIX)
10622         if (tape.pausing)
10623           SendToServer_ContinuePlaying();
10624         else
10625           SendToServer_PausePlaying();
10626 #endif
10627       }
10628       else
10629         TapeTogglePause(TAPE_TOGGLE_MANUAL);
10630       break;
10631
10632     case GAME_CTRL_ID_PLAY:
10633       if (tape.pausing)
10634       {
10635 #if defined(PLATFORM_UNIX)
10636         if (options.network)
10637           SendToServer_ContinuePlaying();
10638         else
10639 #endif
10640         {
10641           tape.pausing = FALSE;
10642           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10643         }
10644       }
10645       break;
10646
10647     case SOUND_CTRL_ID_MUSIC:
10648       if (setup.sound_music)
10649       { 
10650         setup.sound_music = FALSE;
10651         FadeMusic();
10652       }
10653       else if (audio.music_available)
10654       { 
10655         setup.sound = setup.sound_music = TRUE;
10656
10657         SetAudioMode(setup.sound);
10658
10659         PlayLevelMusic();
10660       }
10661       break;
10662
10663     case SOUND_CTRL_ID_LOOPS:
10664       if (setup.sound_loops)
10665         setup.sound_loops = FALSE;
10666       else if (audio.loops_available)
10667       {
10668         setup.sound = setup.sound_loops = TRUE;
10669         SetAudioMode(setup.sound);
10670       }
10671       break;
10672
10673     case SOUND_CTRL_ID_SIMPLE:
10674       if (setup.sound_simple)
10675         setup.sound_simple = FALSE;
10676       else if (audio.sound_available)
10677       {
10678         setup.sound = setup.sound_simple = TRUE;
10679         SetAudioMode(setup.sound);
10680       }
10681       break;
10682
10683     default:
10684       break;
10685   }
10686 }