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