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