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