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