rnd-20040225-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MovePlayer() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollPlayer() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_NO_EXPLOSION         0
47 #define EX_NORMAL               1
48 #define EX_CENTER               2
49 #define EX_BORDER               3
50
51 /* special positions in the game control window (relative to control window) */
52 #define XX_LEVEL                37
53 #define YY_LEVEL                20
54 #define XX_EMERALDS             29
55 #define YY_EMERALDS             54
56 #define XX_DYNAMITE             29
57 #define YY_DYNAMITE             89
58 #define XX_KEYS                 18
59 #define YY_KEYS                 123
60 #define XX_SCORE                15
61 #define YY_SCORE                159
62 #define XX_TIME1                29
63 #define XX_TIME2                30
64 #define YY_TIME                 194
65
66 /* special positions in the game control window (relative to main window) */
67 #define DX_LEVEL                (DX + XX_LEVEL)
68 #define DY_LEVEL                (DY + YY_LEVEL)
69 #define DX_EMERALDS             (DX + XX_EMERALDS)
70 #define DY_EMERALDS             (DY + YY_EMERALDS)
71 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
72 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
73 #define DX_KEYS                 (DX + XX_KEYS)
74 #define DY_KEYS                 (DY + YY_KEYS)
75 #define DX_SCORE                (DX + XX_SCORE)
76 #define DY_SCORE                (DY + YY_SCORE)
77 #define DX_TIME1                (DX + XX_TIME1)
78 #define DX_TIME2                (DX + XX_TIME2)
79 #define DY_TIME                 (DY + YY_TIME)
80
81 /* values for initial player move delay (initial delay counter value) */
82 #define INITIAL_MOVE_DELAY_OFF  -1
83 #define INITIAL_MOVE_DELAY_ON   0
84
85 /* values for player movement speed (which is in fact a delay value) */
86 #define MOVE_DELAY_NORMAL_SPEED 8
87 #define MOVE_DELAY_HIGH_SPEED   4
88
89 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
90 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
91 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
92 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
93
94 /* values for other actions */
95 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
96
97 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
98
99 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
100                                  RND(element_info[e].push_delay_random))
101 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
102                                  RND(element_info[e].move_delay_random))
103 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
104                                     (element_info[e].move_delay_random))
105
106 #define ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, condition)                \
107                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
108                                         (CAN_MOVE_INTO_ACID(e) &&       \
109                                          Feld[x][y] == EL_ACID) ||      \
110                                         (condition)))
111
112 #if 0
113 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
114                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
115                                         (condition) ||                  \
116                                         (DONT_COLLIDE_WITH(e) &&        \
117                                          IS_PLAYER(x, y) &&             \
118                                          !PLAYER_ENEMY_PROTECTED(x, y))))
119 #else
120 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
121                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
122                                         (condition) ||                  \
123                                         (CAN_MOVE_INTO_ACID(e) &&       \
124                                          Feld[x][y] == EL_ACID) ||      \
125                                         (DONT_COLLIDE_WITH(e) &&        \
126                                          IS_PLAYER(x, y) &&             \
127                                          !PLAYER_ENEMY_PROTECTED(x, y))))
128 #endif
129
130 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)              \
131                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
132                                         (condition)))
133
134 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
135         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
136
137 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                        \
138         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
139
140 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                         \
141         ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
142
143 #if 0
144 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
145 #else
146 #define ENEMY_CAN_ENTER_FIELD(e, x, y) ELEMENT_CAN_ENTER_FIELD_BASE(e, x, y, 0)
147 #endif
148
149 #define YAMYAM_CAN_ENTER_FIELD(x, y)                                    \
150                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
151                                         (CAN_MOVE_INTO_ACID(EL_YAMYAM) && \
152                                          Feld[x][y] == EL_ACID) ||      \
153                                         Feld[x][y] == EL_DIAMOND))
154
155 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                               \
156                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
157                                         (CAN_MOVE_INTO_ACID(EL_DARK_YAMYAM) &&\
158                                          Feld[x][y] == EL_ACID) ||      \
159                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
160
161 #define PACMAN_CAN_ENTER_FIELD(x, y)                                    \
162                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
163                                         (CAN_MOVE_INTO_ACID(EL_PACMAN) && \
164                                          Feld[x][y] == EL_ACID) ||      \
165                                         IS_AMOEBOID(Feld[x][y])))
166
167 #define PIG_CAN_ENTER_FIELD(x, y)                                       \
168                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
169                                         (CAN_MOVE_INTO_ACID(EL_PIG) &&  \
170                                          Feld[x][y] == EL_ACID) ||      \
171                                         IS_FOOD_PIG(Feld[x][y])))
172
173 #define PENGUIN_CAN_ENTER_FIELD(x, y)                                   \
174                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
175                                         (CAN_MOVE_INTO_ACID(EL_PENGUIN) && \
176                                          Feld[x][y] == EL_ACID) ||      \
177                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
178                                         Feld[x][y] == EL_EXIT_OPEN))
179
180 #define DRAGON_CAN_ENTER_FIELD(x, y)                                    \
181                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
182                                         (CAN_MOVE_INTO_ACID(EL_DRAGON) && \
183                                          Feld[x][y] == EL_ACID)))
184
185 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                           \
186                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
187                                         (CAN_MOVE_INTO_ACID(EL_MOLE) && \
188                                          Feld[x][y] == EL_ACID) ||      \
189                                         (condition)))
190
191 #define SPRING_CAN_ENTER_FIELD(x, y)                                    \
192                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
193                                         (CAN_MOVE_INTO_ACID(EL_SPRING) && \
194                                          Feld[x][y] == EL_ACID)))
195
196 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
197 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
198 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
199 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
200
201 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
202         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
203
204 #if 0
205 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
206                 (!IS_PLAYER(x, y) &&                                    \
207                  (Feld[x][y] == EL_ACID ||                              \
208                   IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
209 #else
210 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
211                 (!IS_PLAYER(x, y) &&                                    \
212                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
213 #endif
214
215 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
216         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
217
218 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
219 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
220
221 /* game button identifiers */
222 #define GAME_CTRL_ID_STOP               0
223 #define GAME_CTRL_ID_PAUSE              1
224 #define GAME_CTRL_ID_PLAY               2
225 #define SOUND_CTRL_ID_MUSIC             3
226 #define SOUND_CTRL_ID_LOOPS             4
227 #define SOUND_CTRL_ID_SIMPLE            5
228
229 #define NUM_GAME_BUTTONS                6
230
231
232 /* forward declaration for internal use */
233
234 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
235 static boolean MovePlayer(struct PlayerInfo *, int, int);
236 static void ScrollPlayer(struct PlayerInfo *, int);
237 static void ScrollScreen(struct PlayerInfo *, int);
238
239 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
240
241 static void InitBeltMovement(void);
242 static void CloseAllOpenTimegates(void);
243 static void CheckGravityMovement(struct PlayerInfo *);
244 static void KillHeroUnlessEnemyProtected(int, int);
245 static void KillHeroUnlessExplosionProtected(int, int);
246
247 static void TestIfPlayerTouchesCustomElement(int, int);
248 static void TestIfElementTouchesCustomElement(int, int);
249 static void TestIfElementHitsCustomElement(int, int, int);
250
251 static void ChangeElement(int, int, int);
252 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 (!IN_LEV_FIELD(x, y) ||
2657           ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2658            (x != ex || y != ey)))
2659         continue;
2660
2661       element = Feld[x][y];
2662
2663       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2664       {
2665         element = MovingOrBlocked2Element(x, y);
2666
2667         if (!IS_EXPLOSION_PROOF(element))
2668           RemoveMovingField(x, y);
2669       }
2670
2671 #if 1
2672
2673 #if 0
2674       if (IS_EXPLOSION_PROOF(element))
2675         continue;
2676 #else
2677       /* indestructible elements can only explode in center (but not flames) */
2678       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2679           element == EL_FLAMES)
2680         continue;
2681 #endif
2682
2683 #else
2684       if ((IS_INDESTRUCTIBLE(element) &&
2685            (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2686             (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2687           element == EL_FLAMES)
2688         continue;
2689 #endif
2690
2691       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2692       {
2693         if (IS_ACTIVE_BOMB(element))
2694         {
2695           /* re-activate things under the bomb like gate or penguin */
2696           Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2697           Store[x][y] = 0;
2698         }
2699
2700         continue;
2701       }
2702
2703       /* save walkable background elements while explosion on same tile */
2704 #if 0
2705       if (IS_INDESTRUCTIBLE(element))
2706         Back[x][y] = element;
2707 #else
2708       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2709         Back[x][y] = element;
2710 #endif
2711
2712       /* ignite explodable elements reached by other explosion */
2713       if (element == EL_EXPLOSION)
2714         element = Store2[x][y];
2715
2716 #if 1
2717       if (AmoebaNr[x][y] &&
2718           (element == EL_AMOEBA_FULL ||
2719            element == EL_BD_AMOEBA ||
2720            element == EL_AMOEBA_GROWING))
2721       {
2722         AmoebaCnt[AmoebaNr[x][y]]--;
2723         AmoebaCnt2[AmoebaNr[x][y]]--;
2724       }
2725
2726       RemoveField(x, y);
2727 #endif
2728
2729       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2730       {
2731         switch(StorePlayer[ex][ey])
2732         {
2733           case EL_PLAYER_2:
2734             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2735             break;
2736           case EL_PLAYER_3:
2737             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2738             break;
2739           case EL_PLAYER_4:
2740             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2741             break;
2742           case EL_PLAYER_1:
2743           default:
2744             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2745             break;
2746         }
2747
2748         if (game.emulation == EMU_SUPAPLEX)
2749           Store[x][y] = EL_EMPTY;
2750       }
2751       else if (center_element == EL_MOLE)
2752         Store[x][y] = EL_EMERALD_RED;
2753       else if (center_element == EL_PENGUIN)
2754         Store[x][y] = EL_EMERALD_PURPLE;
2755       else if (center_element == EL_BUG)
2756         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2757       else if (center_element == EL_BD_BUTTERFLY)
2758         Store[x][y] = EL_BD_DIAMOND;
2759       else if (center_element == EL_SP_ELECTRON)
2760         Store[x][y] = EL_SP_INFOTRON;
2761       else if (center_element == EL_AMOEBA_TO_DIAMOND)
2762         Store[x][y] = level.amoeba_content;
2763       else if (center_element == EL_YAMYAM)
2764         Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2765       else if (IS_CUSTOM_ELEMENT(center_element) &&
2766                element_info[center_element].content[xx][yy] != EL_EMPTY)
2767         Store[x][y] = element_info[center_element].content[xx][yy];
2768       else if (element == EL_WALL_EMERALD)
2769         Store[x][y] = EL_EMERALD;
2770       else if (element == EL_WALL_DIAMOND)
2771         Store[x][y] = EL_DIAMOND;
2772       else if (element == EL_WALL_BD_DIAMOND)
2773         Store[x][y] = EL_BD_DIAMOND;
2774       else if (element == EL_WALL_EMERALD_YELLOW)
2775         Store[x][y] = EL_EMERALD_YELLOW;
2776       else if (element == EL_WALL_EMERALD_RED)
2777         Store[x][y] = EL_EMERALD_RED;
2778       else if (element == EL_WALL_EMERALD_PURPLE)
2779         Store[x][y] = EL_EMERALD_PURPLE;
2780       else if (element == EL_WALL_PEARL)
2781         Store[x][y] = EL_PEARL;
2782       else if (element == EL_WALL_CRYSTAL)
2783         Store[x][y] = EL_CRYSTAL;
2784       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2785         Store[x][y] = element_info[element].content[1][1];
2786       else
2787         Store[x][y] = EL_EMPTY;
2788
2789       if (x != ex || y != ey ||
2790           center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2791         Store2[x][y] = element;
2792
2793 #if 0
2794       if (AmoebaNr[x][y] &&
2795           (element == EL_AMOEBA_FULL ||
2796            element == EL_BD_AMOEBA ||
2797            element == EL_AMOEBA_GROWING))
2798       {
2799         AmoebaCnt[AmoebaNr[x][y]]--;
2800         AmoebaCnt2[AmoebaNr[x][y]]--;
2801       }
2802
2803 #if 1
2804       RemoveField(x, y);
2805 #else
2806       MovDir[x][y] = MovPos[x][y] = 0;
2807       GfxDir[x][y] = MovDir[x][y];
2808       AmoebaNr[x][y] = 0;
2809 #endif
2810 #endif
2811
2812       Feld[x][y] = EL_EXPLOSION;
2813 #if 1
2814       GfxElement[x][y] = center_element;
2815 #else
2816       GfxElement[x][y] = EL_UNDEFINED;
2817 #endif
2818
2819       ExplodePhase[x][y] = 1;
2820 #if 1
2821       ExplodeDelay[x][y] = last_phase;
2822 #endif
2823       Stop[x][y] = TRUE;
2824     }
2825
2826     if (center_element == EL_YAMYAM)
2827       game.yamyam_content_nr =
2828         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2829
2830     return;
2831   }
2832
2833   if (Stop[ex][ey])
2834     return;
2835
2836   x = ex;
2837   y = ey;
2838
2839 #if 1
2840   last_phase = ExplodeDelay[x][y];
2841 #endif
2842
2843   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2844
2845 #ifdef DEBUG
2846
2847   /* activate this even in non-DEBUG version until cause for crash in
2848      getGraphicAnimationFrame() (see below) is found and eliminated */
2849 #endif
2850 #if 1
2851
2852   if (GfxElement[x][y] == EL_UNDEFINED)
2853   {
2854     printf("\n\n");
2855     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2856     printf("Explode(): This should never happen!\n");
2857     printf("\n\n");
2858
2859     GfxElement[x][y] = EL_EMPTY;
2860   }
2861 #endif
2862
2863 #if 1
2864
2865   border_element = Store2[x][y];
2866   if (IS_PLAYER(x, y))
2867     border_element = StorePlayer[x][y];
2868
2869   if (phase == element_info[border_element].ignition_delay ||
2870       phase == last_phase)
2871   {
2872     boolean border_explosion = FALSE;
2873
2874 #if 1
2875     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2876 #else
2877     if (IS_PLAYER(x, y))
2878 #endif
2879     {
2880       KillHeroUnlessExplosionProtected(x, y);
2881       border_explosion = TRUE;
2882
2883 #if 0
2884       if (phase == last_phase)
2885         printf("::: IS_PLAYER\n");
2886 #endif
2887     }
2888     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2889     {
2890       Feld[x][y] = Store2[x][y];
2891       Store2[x][y] = 0;
2892       Bang(x, y);
2893       border_explosion = TRUE;
2894
2895 #if 0
2896       if (phase == last_phase)
2897         printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2898 #endif
2899     }
2900     else if (border_element == EL_AMOEBA_TO_DIAMOND)
2901     {
2902       AmoebeUmwandeln(x, y);
2903       Store2[x][y] = 0;
2904       border_explosion = TRUE;
2905
2906 #if 0
2907       if (phase == last_phase)
2908         printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2909                element_info[border_element].explosion_delay,
2910                element_info[border_element].ignition_delay,
2911                phase);
2912 #endif
2913     }
2914
2915 #if 1
2916     /* if an element just explodes due to another explosion (chain-reaction),
2917        do not immediately end the new explosion when it was the last frame of
2918        the explosion (as it would be done in the following "if"-statement!) */
2919     if (border_explosion && phase == last_phase)
2920       return;
2921 #endif
2922   }
2923
2924 #else
2925
2926   if (phase == first_phase_after_start)
2927   {
2928     int element = Store2[x][y];
2929
2930     if (element == EL_BLACK_ORB)
2931     {
2932       Feld[x][y] = Store2[x][y];
2933       Store2[x][y] = 0;
2934       Bang(x, y);
2935     }
2936   }
2937   else if (phase == half_phase)
2938   {
2939     int element = Store2[x][y];
2940
2941     if (IS_PLAYER(x, y))
2942       KillHeroUnlessExplosionProtected(x, y);
2943     else if (CAN_EXPLODE_BY_EXPLOSION(element))
2944     {
2945       Feld[x][y] = Store2[x][y];
2946       Store2[x][y] = 0;
2947       Bang(x, y);
2948     }
2949     else if (element == EL_AMOEBA_TO_DIAMOND)
2950       AmoebeUmwandeln(x, y);
2951   }
2952 #endif
2953
2954   if (phase == last_phase)
2955   {
2956     int element;
2957
2958     element = Feld[x][y] = Store[x][y];
2959     Store[x][y] = Store2[x][y] = 0;
2960     GfxElement[x][y] = EL_UNDEFINED;
2961
2962     /* player can escape from explosions and might therefore be still alive */
2963     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2964         element <= EL_PLAYER_IS_EXPLODING_4)
2965       Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2966                     EL_EMPTY :
2967                     element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2968                     element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2969                     element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2970                     EL_EMERALD_PURPLE);
2971
2972     /* restore probably existing indestructible background element */
2973     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2974       element = Feld[x][y] = Back[x][y];
2975     Back[x][y] = 0;
2976
2977     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2978     GfxDir[x][y] = MV_NO_MOVING;
2979     ChangeDelay[x][y] = 0;
2980     ChangePage[x][y] = -1;
2981
2982 #if 1
2983     InitField_WithBug2(x, y, FALSE);
2984 #else
2985     InitField(x, y, FALSE);
2986 #if 1
2987     /* !!! not needed !!! */
2988 #if 1
2989     if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
2990         CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
2991       InitMovDir(x, y);
2992 #else
2993     if (CAN_MOVE(element))
2994       InitMovDir(x, y);
2995 #endif
2996 #endif
2997 #endif
2998     DrawLevelField(x, y);
2999
3000     TestIfElementTouchesCustomElement(x, y);
3001
3002     if (GFX_CRUMBLED(element))
3003       DrawLevelFieldCrumbledSandNeighbours(x, y);
3004
3005     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3006       StorePlayer[x][y] = 0;
3007
3008     if (ELEM_IS_PLAYER(element))
3009       RelocatePlayer(x, y, element);
3010   }
3011 #if 1
3012   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3013 #else
3014   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3015 #endif
3016   {
3017 #if 1
3018     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3019 #else
3020     int stored = Store[x][y];
3021     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3022                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3023                    IMG_SP_EXPLOSION);
3024 #endif
3025     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3026
3027 #if 0
3028     printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3029            element_info[GfxElement[x][y]].token_name,
3030            graphic);
3031 #endif
3032
3033     if (phase == delay)
3034       DrawLevelFieldCrumbledSand(x, y);
3035
3036     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3037     {
3038       DrawLevelElement(x, y, Back[x][y]);
3039       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3040     }
3041     else if (IS_WALKABLE_UNDER(Back[x][y]))
3042     {
3043       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3044       DrawLevelElementThruMask(x, y, Back[x][y]);
3045     }
3046     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3047       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3048   }
3049 }
3050
3051 void DynaExplode(int ex, int ey)
3052 {
3053   int i, j;
3054   int dynabomb_element = Feld[ex][ey];
3055   int dynabomb_size = 1;
3056   boolean dynabomb_xl = FALSE;
3057   struct PlayerInfo *player;
3058   static int xy[4][2] =
3059   {
3060     { 0, -1 },
3061     { -1, 0 },
3062     { +1, 0 },
3063     { 0, +1 }
3064   };
3065
3066   if (IS_ACTIVE_BOMB(dynabomb_element))
3067   {
3068     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3069     dynabomb_size = player->dynabomb_size;
3070     dynabomb_xl = player->dynabomb_xl;
3071     player->dynabombs_left++;
3072   }
3073
3074   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
3075
3076   for (i = 0; i < NUM_DIRECTIONS; i++)
3077   {
3078     for (j = 1; j <= dynabomb_size; j++)
3079     {
3080       int x = ex + j * xy[i][0];
3081       int y = ey + j * xy[i][1];
3082       int element;
3083
3084       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3085         break;
3086
3087       element = Feld[x][y];
3088
3089       /* do not restart explosions of fields with active bombs */
3090       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3091         continue;
3092
3093       Explode(x, y, EX_PHASE_START, EX_BORDER);
3094
3095       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3096       if (element != EL_EMPTY &&
3097           element != EL_SAND &&
3098           element != EL_EXPLOSION &&
3099           !dynabomb_xl)
3100         break;
3101     }
3102   }
3103 }
3104
3105 void Bang(int x, int y)
3106 {
3107 #if 1
3108   int element = MovingOrBlocked2Element(x, y);
3109 #else
3110   int element = Feld[x][y];
3111 #endif
3112
3113 #if 1
3114   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3115 #else
3116   if (IS_PLAYER(x, y))
3117 #endif
3118   {
3119     struct PlayerInfo *player = PLAYERINFO(x, y);
3120
3121     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3122                             player->element_nr);
3123   }
3124
3125 #if 0
3126 #if 1
3127   PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3128 #else
3129   if (game.emulation == EMU_SUPAPLEX)
3130     PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3131   else
3132     PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3133 #endif
3134 #endif
3135
3136 #if 0
3137   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
3138     element = EL_EMPTY;
3139 #endif
3140
3141   switch(element)
3142   {
3143     case EL_BUG:
3144     case EL_SPACESHIP:
3145     case EL_BD_BUTTERFLY:
3146     case EL_BD_FIREFLY:
3147     case EL_YAMYAM:
3148     case EL_DARK_YAMYAM:
3149     case EL_ROBOT:
3150     case EL_PACMAN:
3151     case EL_MOLE:
3152       RaiseScoreElement(element);
3153       Explode(x, y, EX_PHASE_START, EX_NORMAL);
3154       break;
3155     case EL_DYNABOMB_PLAYER_1_ACTIVE:
3156     case EL_DYNABOMB_PLAYER_2_ACTIVE:
3157     case EL_DYNABOMB_PLAYER_3_ACTIVE:
3158     case EL_DYNABOMB_PLAYER_4_ACTIVE:
3159     case EL_DYNABOMB_INCREASE_NUMBER:
3160     case EL_DYNABOMB_INCREASE_SIZE:
3161     case EL_DYNABOMB_INCREASE_POWER:
3162       DynaExplode(x, y);
3163       break;
3164     case EL_PENGUIN:
3165     case EL_LAMP:
3166     case EL_LAMP_ACTIVE:
3167
3168 #if 1
3169     case EL_AMOEBA_TO_DIAMOND:
3170 #endif
3171
3172       if (IS_PLAYER(x, y))
3173         Explode(x, y, EX_PHASE_START, EX_NORMAL);
3174       else
3175         Explode(x, y, EX_PHASE_START, EX_CENTER);
3176       break;
3177     default:
3178       if (CAN_EXPLODE_DYNA(element))
3179         DynaExplode(x, y);
3180       else if (CAN_EXPLODE_1X1(element))
3181         Explode(x, y, EX_PHASE_START, EX_CENTER);
3182       else
3183         Explode(x, y, EX_PHASE_START, EX_NORMAL);
3184       break;
3185   }
3186
3187   CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3188 }
3189
3190 void SplashAcid(int x, int y)
3191 {
3192 #if 1
3193   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3194       (!IN_LEV_FIELD(x - 1, y - 2) ||
3195        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3196     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3197
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_RIGHT;
3202
3203   PlayLevelSound(x, y, SND_ACID_SPLASHING);
3204 #else
3205   /* input: position of element entering acid (obsolete) */
3206
3207   int element = Feld[x][y];
3208
3209   if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3210     return;
3211
3212   if (element != EL_ACID_SPLASH_LEFT &&
3213       element != EL_ACID_SPLASH_RIGHT)
3214   {
3215     PlayLevelSound(x, y, SND_ACID_SPLASHING);
3216
3217     if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3218         (!IN_LEV_FIELD(x - 1, y - 1) ||
3219          !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3220       Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
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_RIGHT;
3226   }
3227 #endif
3228 }
3229
3230 static void InitBeltMovement()
3231 {
3232   static int belt_base_element[4] =
3233   {
3234     EL_CONVEYOR_BELT_1_LEFT,
3235     EL_CONVEYOR_BELT_2_LEFT,
3236     EL_CONVEYOR_BELT_3_LEFT,
3237     EL_CONVEYOR_BELT_4_LEFT
3238   };
3239   static int belt_base_active_element[4] =
3240   {
3241     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3242     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3243     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3244     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3245   };
3246
3247   int x, y, i, j;
3248
3249   /* set frame order for belt animation graphic according to belt direction */
3250   for (i = 0; i < NUM_BELTS; i++)
3251   {
3252     int belt_nr = i;
3253
3254     for (j = 0; j < NUM_BELT_PARTS; j++)
3255     {
3256       int element = belt_base_active_element[belt_nr] + j;
3257       int graphic = el2img(element);
3258
3259       if (game.belt_dir[i] == MV_LEFT)
3260         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3261       else
3262         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3263     }
3264   }
3265
3266   for (y = 0; y < lev_fieldy; y++)
3267   {
3268     for (x = 0; x < lev_fieldx; x++)
3269     {
3270       int element = Feld[x][y];
3271
3272       for (i = 0; i < NUM_BELTS; i++)
3273       {
3274         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3275         {
3276           int e_belt_nr = getBeltNrFromBeltElement(element);
3277           int belt_nr = i;
3278
3279           if (e_belt_nr == belt_nr)
3280           {
3281             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3282
3283             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3284           }
3285         }
3286       }
3287     }
3288   }
3289 }
3290
3291 static void ToggleBeltSwitch(int x, int y)
3292 {
3293   static int belt_base_element[4] =
3294   {
3295     EL_CONVEYOR_BELT_1_LEFT,
3296     EL_CONVEYOR_BELT_2_LEFT,
3297     EL_CONVEYOR_BELT_3_LEFT,
3298     EL_CONVEYOR_BELT_4_LEFT
3299   };
3300   static int belt_base_active_element[4] =
3301   {
3302     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3303     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3304     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3305     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3306   };
3307   static int belt_base_switch_element[4] =
3308   {
3309     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3310     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3311     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3312     EL_CONVEYOR_BELT_4_SWITCH_LEFT
3313   };
3314   static int belt_move_dir[4] =
3315   {
3316     MV_LEFT,
3317     MV_NO_MOVING,
3318     MV_RIGHT,
3319     MV_NO_MOVING,
3320   };
3321
3322   int element = Feld[x][y];
3323   int belt_nr = getBeltNrFromBeltSwitchElement(element);
3324   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3325   int belt_dir = belt_move_dir[belt_dir_nr];
3326   int xx, yy, i;
3327
3328   if (!IS_BELT_SWITCH(element))
3329     return;
3330
3331   game.belt_dir_nr[belt_nr] = belt_dir_nr;
3332   game.belt_dir[belt_nr] = belt_dir;
3333
3334   if (belt_dir_nr == 3)
3335     belt_dir_nr = 1;
3336
3337   /* set frame order for belt animation graphic according to belt direction */
3338   for (i = 0; i < NUM_BELT_PARTS; i++)
3339   {
3340     int element = belt_base_active_element[belt_nr] + i;
3341     int graphic = el2img(element);
3342
3343     if (belt_dir == MV_LEFT)
3344       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3345     else
3346       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3347   }
3348
3349   for (yy = 0; yy < lev_fieldy; yy++)
3350   {
3351     for (xx = 0; xx < lev_fieldx; xx++)
3352     {
3353       int element = Feld[xx][yy];
3354
3355       if (IS_BELT_SWITCH(element))
3356       {
3357         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3358
3359         if (e_belt_nr == belt_nr)
3360         {
3361           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3362           DrawLevelField(xx, yy);
3363         }
3364       }
3365       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3366       {
3367         int e_belt_nr = getBeltNrFromBeltElement(element);
3368
3369         if (e_belt_nr == belt_nr)
3370         {
3371           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3372
3373           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3374           DrawLevelField(xx, yy);
3375         }
3376       }
3377       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3378       {
3379         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3380
3381         if (e_belt_nr == belt_nr)
3382         {
3383           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3384
3385           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3386           DrawLevelField(xx, yy);
3387         }
3388       }
3389     }
3390   }
3391 }
3392
3393 static void ToggleSwitchgateSwitch(int x, int y)
3394 {
3395   int xx, yy;
3396
3397   game.switchgate_pos = !game.switchgate_pos;
3398
3399   for (yy = 0; yy < lev_fieldy; yy++)
3400   {
3401     for (xx = 0; xx < lev_fieldx; xx++)
3402     {
3403       int element = Feld[xx][yy];
3404
3405       if (element == EL_SWITCHGATE_SWITCH_UP ||
3406           element == EL_SWITCHGATE_SWITCH_DOWN)
3407       {
3408         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3409         DrawLevelField(xx, yy);
3410       }
3411       else if (element == EL_SWITCHGATE_OPEN ||
3412                element == EL_SWITCHGATE_OPENING)
3413       {
3414         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3415 #if 1
3416         PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3417 #else
3418         PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3419 #endif
3420       }
3421       else if (element == EL_SWITCHGATE_CLOSED ||
3422                element == EL_SWITCHGATE_CLOSING)
3423       {
3424         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3425 #if 1
3426         PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3427 #else
3428         PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3429 #endif
3430       }
3431     }
3432   }
3433 }
3434
3435 static int getInvisibleActiveFromInvisibleElement(int element)
3436 {
3437   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3438           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
3439           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
3440           element);
3441 }
3442
3443 static int getInvisibleFromInvisibleActiveElement(int element)
3444 {
3445   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3446           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
3447           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
3448           element);
3449 }
3450
3451 static void RedrawAllLightSwitchesAndInvisibleElements()
3452 {
3453   int x, y;
3454
3455   for (y = 0; y < lev_fieldy; y++)
3456   {
3457     for (x = 0; x < lev_fieldx; x++)
3458     {
3459       int element = Feld[x][y];
3460
3461       if (element == EL_LIGHT_SWITCH &&
3462           game.light_time_left > 0)
3463       {
3464         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3465         DrawLevelField(x, y);
3466       }
3467       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3468                game.light_time_left == 0)
3469       {
3470         Feld[x][y] = EL_LIGHT_SWITCH;
3471         DrawLevelField(x, y);
3472       }
3473       else if (element == EL_INVISIBLE_STEELWALL ||
3474                element == EL_INVISIBLE_WALL ||
3475                element == EL_INVISIBLE_SAND)
3476       {
3477         if (game.light_time_left > 0)
3478           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3479
3480         DrawLevelField(x, y);
3481       }
3482       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3483                element == EL_INVISIBLE_WALL_ACTIVE ||
3484                element == EL_INVISIBLE_SAND_ACTIVE)
3485       {
3486         if (game.light_time_left == 0)
3487           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3488
3489         DrawLevelField(x, y);
3490       }
3491     }
3492   }
3493 }
3494
3495 static void ToggleLightSwitch(int x, int y)
3496 {
3497   int element = Feld[x][y];
3498
3499   game.light_time_left =
3500     (element == EL_LIGHT_SWITCH ?
3501      level.time_light * FRAMES_PER_SECOND : 0);
3502
3503   RedrawAllLightSwitchesAndInvisibleElements();
3504 }
3505
3506 static void ActivateTimegateSwitch(int x, int y)
3507 {
3508   int xx, yy;
3509
3510   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3511
3512   for (yy = 0; yy < lev_fieldy; yy++)
3513   {
3514     for (xx = 0; xx < lev_fieldx; xx++)
3515     {
3516       int element = Feld[xx][yy];
3517
3518       if (element == EL_TIMEGATE_CLOSED ||
3519           element == EL_TIMEGATE_CLOSING)
3520       {
3521         Feld[xx][yy] = EL_TIMEGATE_OPENING;
3522         PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3523       }
3524
3525       /*
3526       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3527       {
3528         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3529         DrawLevelField(xx, yy);
3530       }
3531       */
3532
3533     }
3534   }
3535
3536   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3537 }
3538
3539 inline static int getElementMoveStepsize(int x, int y)
3540 {
3541   int element = Feld[x][y];
3542   int direction = MovDir[x][y];
3543   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3544   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3545   int horiz_move = (dx != 0);
3546   int sign = (horiz_move ? dx : dy);
3547   int step = sign * element_info[element].move_stepsize;
3548
3549   /* special values for move stepsize for spring and things on conveyor belt */
3550   if (horiz_move)
3551   {
3552 #if 0
3553     if (element == EL_SPRING)
3554       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3555     else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3556              y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3557       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3558 #else
3559     if (CAN_FALL(element) &&
3560         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3561       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3562     else if (element == EL_SPRING)
3563       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3564 #endif
3565   }
3566
3567   return step;
3568 }
3569
3570 void Impact(int x, int y)
3571 {
3572   boolean lastline = (y == lev_fieldy-1);
3573   boolean object_hit = FALSE;
3574   boolean impact = (lastline || object_hit);
3575   int element = Feld[x][y];
3576   int smashed = EL_UNDEFINED;
3577
3578   if (!lastline)        /* check if element below was hit */
3579   {
3580     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3581       return;
3582
3583     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3584                                          MovDir[x][y + 1] != MV_DOWN ||
3585                                          MovPos[x][y + 1] <= TILEY / 2));
3586
3587 #if 0
3588     object_hit = !IS_FREE(x, y + 1);
3589 #endif
3590
3591     /* do not smash moving elements that left the smashed field in time */
3592     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3593         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3594       object_hit = FALSE;
3595
3596     if (object_hit)
3597       smashed = MovingOrBlocked2Element(x, y + 1);
3598
3599     impact = (lastline || object_hit);
3600   }
3601
3602   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
3603   {
3604     SplashAcid(x, y + 1);
3605     return;
3606   }
3607
3608   /* only reset graphic animation if graphic really changes after impact */
3609   if (impact &&
3610       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3611   {
3612     ResetGfxAnimation(x, y);
3613     DrawLevelField(x, y);
3614   }
3615
3616   if (impact && CAN_EXPLODE_IMPACT(element))
3617   {
3618     Bang(x, y);
3619     return;
3620   }
3621   else if (impact && element == EL_PEARL)
3622   {
3623     Feld[x][y] = EL_PEARL_BREAKING;
3624     PlayLevelSound(x, y, SND_PEARL_BREAKING);
3625     return;
3626   }
3627   else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3628   {
3629     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3630
3631     return;
3632   }
3633
3634   if (impact && element == EL_AMOEBA_DROP)
3635   {
3636     if (object_hit && IS_PLAYER(x, y + 1))
3637       KillHeroUnlessEnemyProtected(x, y + 1);
3638     else if (object_hit && smashed == EL_PENGUIN)
3639       Bang(x, y + 1);
3640     else
3641     {
3642       Feld[x][y] = EL_AMOEBA_GROWING;
3643       Store[x][y] = EL_AMOEBA_WET;
3644
3645       ResetRandomAnimationValue(x, y);
3646     }
3647     return;
3648   }
3649
3650   if (object_hit)               /* check which object was hit */
3651   {
3652     if (CAN_PASS_MAGIC_WALL(element) && 
3653         (smashed == EL_MAGIC_WALL ||
3654          smashed == EL_BD_MAGIC_WALL))
3655     {
3656       int xx, yy;
3657       int activated_magic_wall =
3658         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3659          EL_BD_MAGIC_WALL_ACTIVE);
3660
3661       /* activate magic wall / mill */
3662       for (yy = 0; yy < lev_fieldy; yy++)
3663         for (xx = 0; xx < lev_fieldx; xx++)
3664           if (Feld[xx][yy] == smashed)
3665             Feld[xx][yy] = activated_magic_wall;
3666
3667       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3668       game.magic_wall_active = TRUE;
3669
3670       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3671                             SND_MAGIC_WALL_ACTIVATING :
3672                             SND_BD_MAGIC_WALL_ACTIVATING));
3673     }
3674
3675     if (IS_PLAYER(x, y + 1))
3676     {
3677       if (CAN_SMASH_PLAYER(element))
3678       {
3679         KillHeroUnlessEnemyProtected(x, y + 1);
3680         return;
3681       }
3682     }
3683     else if (smashed == EL_PENGUIN)
3684     {
3685       if (CAN_SMASH_PLAYER(element))
3686       {
3687         Bang(x, y + 1);
3688         return;
3689       }
3690     }
3691     else if (element == EL_BD_DIAMOND)
3692     {
3693       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3694       {
3695         Bang(x, y + 1);
3696         return;
3697       }
3698     }
3699     else if (((element == EL_SP_INFOTRON ||
3700                element == EL_SP_ZONK) &&
3701               (smashed == EL_SP_SNIKSNAK ||
3702                smashed == EL_SP_ELECTRON ||
3703                smashed == EL_SP_DISK_ORANGE)) ||
3704              (element == EL_SP_INFOTRON &&
3705               smashed == EL_SP_DISK_YELLOW))
3706     {
3707       Bang(x, y + 1);
3708       return;
3709     }
3710 #if 0
3711     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3712     {
3713       Bang(x, y + 1);
3714       return;
3715     }
3716 #endif
3717     else if (CAN_SMASH_EVERYTHING(element))
3718     {
3719       if (IS_CLASSIC_ENEMY(smashed) ||
3720           CAN_EXPLODE_SMASHED(smashed))
3721       {
3722         Bang(x, y + 1);
3723         return;
3724       }
3725       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3726       {
3727         if (smashed == EL_LAMP ||
3728             smashed == EL_LAMP_ACTIVE)
3729         {
3730           Bang(x, y + 1);
3731           return;
3732         }
3733         else if (smashed == EL_NUT)
3734         {
3735           Feld[x][y + 1] = EL_NUT_BREAKING;
3736           PlayLevelSound(x, y, SND_NUT_BREAKING);
3737           RaiseScoreElement(EL_NUT);
3738           return;
3739         }
3740         else if (smashed == EL_PEARL)
3741         {
3742           Feld[x][y + 1] = EL_PEARL_BREAKING;
3743           PlayLevelSound(x, y, SND_PEARL_BREAKING);
3744           return;
3745         }
3746         else if (smashed == EL_DIAMOND)
3747         {
3748           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3749           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3750           return;
3751         }
3752         else if (IS_BELT_SWITCH(smashed))
3753         {
3754           ToggleBeltSwitch(x, y + 1);
3755         }
3756         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3757                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
3758         {
3759           ToggleSwitchgateSwitch(x, y + 1);
3760         }
3761         else if (smashed == EL_LIGHT_SWITCH ||
3762                  smashed == EL_LIGHT_SWITCH_ACTIVE)
3763         {
3764           ToggleLightSwitch(x, y + 1);
3765         }
3766         else
3767         {
3768           CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3769
3770           CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3771                                           CE_OTHER_IS_SWITCHING);
3772           CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3773                                  CE_SWITCHED, -1);
3774         }
3775       }
3776       else
3777       {
3778         CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3779       }
3780     }
3781   }
3782
3783   /* play sound of magic wall / mill */
3784   if (!lastline &&
3785       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3786        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3787   {
3788     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3789       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3790     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3791       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3792
3793     return;
3794   }
3795
3796   /* play sound of object that hits the ground */
3797   if (lastline || object_hit)
3798     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3799 }
3800
3801 inline static void TurnRoundExt(int x, int y)
3802 {
3803   static struct
3804   {
3805     int x, y;
3806   } move_xy[] =
3807   {
3808     {  0,  0 },
3809     { -1,  0 },
3810     { +1,  0 },
3811     {  0,  0 },
3812     {  0, -1 },
3813     {  0,  0 }, { 0, 0 }, { 0, 0 },
3814     {  0, +1 }
3815   };
3816   static struct
3817   {
3818     int left, right, back;
3819   } turn[] =
3820   {
3821     { 0,        0,              0        },
3822     { MV_DOWN,  MV_UP,          MV_RIGHT },
3823     { MV_UP,    MV_DOWN,        MV_LEFT  },
3824     { 0,        0,              0        },
3825     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
3826     { 0,        0,              0        },
3827     { 0,        0,              0        },
3828     { 0,        0,              0        },
3829     { MV_RIGHT, MV_LEFT,        MV_UP    }
3830   };
3831
3832   int element = Feld[x][y];
3833   int move_pattern = element_info[element].move_pattern;
3834
3835   int old_move_dir = MovDir[x][y];
3836   int left_dir  = turn[old_move_dir].left;
3837   int right_dir = turn[old_move_dir].right;
3838   int back_dir  = turn[old_move_dir].back;
3839
3840   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
3841   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
3842   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
3843   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
3844
3845   int left_x  = x + left_dx,  left_y  = y + left_dy;
3846   int right_x = x + right_dx, right_y = y + right_dy;
3847   int move_x  = x + move_dx,  move_y  = y + move_dy;
3848
3849   int xx, yy;
3850
3851   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3852   {
3853     TestIfBadThingTouchesOtherBadThing(x, y);
3854
3855     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
3856       MovDir[x][y] = right_dir;
3857     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3858       MovDir[x][y] = left_dir;
3859
3860     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3861       MovDelay[x][y] = 9;
3862     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
3863       MovDelay[x][y] = 1;
3864   }
3865   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3866            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3867   {
3868     TestIfBadThingTouchesOtherBadThing(x, y);
3869
3870     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
3871       MovDir[x][y] = left_dir;
3872     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
3873       MovDir[x][y] = right_dir;
3874
3875     if ((element == EL_SPACESHIP ||
3876          element == EL_SP_SNIKSNAK ||
3877          element == EL_SP_ELECTRON)
3878         && MovDir[x][y] != old_move_dir)
3879       MovDelay[x][y] = 9;
3880     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
3881       MovDelay[x][y] = 1;
3882   }
3883   else if (element == EL_YAMYAM)
3884   {
3885     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3886     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3887
3888     if (can_turn_left && can_turn_right)
3889       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3890     else if (can_turn_left)
3891       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3892     else if (can_turn_right)
3893       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3894     else
3895       MovDir[x][y] = back_dir;
3896
3897     MovDelay[x][y] = 16 + 16 * RND(3);
3898   }
3899   else if (element == EL_DARK_YAMYAM)
3900   {
3901     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3902     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3903
3904     if (can_turn_left && can_turn_right)
3905       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3906     else if (can_turn_left)
3907       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3908     else if (can_turn_right)
3909       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3910     else
3911       MovDir[x][y] = back_dir;
3912
3913     MovDelay[x][y] = 16 + 16 * RND(3);
3914   }
3915   else if (element == EL_PACMAN)
3916   {
3917     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3918     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3919
3920     if (can_turn_left && can_turn_right)
3921       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3922     else if (can_turn_left)
3923       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3924     else if (can_turn_right)
3925       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3926     else
3927       MovDir[x][y] = back_dir;
3928
3929     MovDelay[x][y] = 6 + RND(40);
3930   }
3931   else if (element == EL_PIG)
3932   {
3933     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(left_x, left_y);
3934     boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3935     boolean can_move_on    = PIG_CAN_ENTER_FIELD(move_x, move_y);
3936     boolean should_turn_left, should_turn_right, should_move_on;
3937     int rnd_value = 24;
3938     int rnd = RND(rnd_value);
3939
3940     should_turn_left = (can_turn_left &&
3941                         (!can_move_on ||
3942                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3943                                                    y + back_dy + left_dy)));
3944     should_turn_right = (can_turn_right &&
3945                          (!can_move_on ||
3946                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3947                                                     y + back_dy + right_dy)));
3948     should_move_on = (can_move_on &&
3949                       (!can_turn_left ||
3950                        !can_turn_right ||
3951                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3952                                                  y + move_dy + left_dy) ||
3953                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3954                                                  y + move_dy + right_dy)));
3955
3956     if (should_turn_left || should_turn_right || should_move_on)
3957     {
3958       if (should_turn_left && should_turn_right && should_move_on)
3959         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
3960                         rnd < 2 * rnd_value / 3 ? right_dir :
3961                         old_move_dir);
3962       else if (should_turn_left && should_turn_right)
3963         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3964       else if (should_turn_left && should_move_on)
3965         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3966       else if (should_turn_right && should_move_on)
3967         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3968       else if (should_turn_left)
3969         MovDir[x][y] = left_dir;
3970       else if (should_turn_right)
3971         MovDir[x][y] = right_dir;
3972       else if (should_move_on)
3973         MovDir[x][y] = old_move_dir;
3974     }
3975     else if (can_move_on && rnd > rnd_value / 8)
3976       MovDir[x][y] = old_move_dir;
3977     else if (can_turn_left && can_turn_right)
3978       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3979     else if (can_turn_left && rnd > rnd_value / 8)
3980       MovDir[x][y] = left_dir;
3981     else if (can_turn_right && rnd > rnd_value/8)
3982       MovDir[x][y] = right_dir;
3983     else
3984       MovDir[x][y] = back_dir;
3985
3986     xx = x + move_xy[MovDir[x][y]].x;
3987     yy = y + move_xy[MovDir[x][y]].y;
3988
3989     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3990       MovDir[x][y] = old_move_dir;
3991
3992     MovDelay[x][y] = 0;
3993   }
3994   else if (element == EL_DRAGON)
3995   {
3996     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(left_x, left_y);
3997     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(right_x, right_y);
3998     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(move_x, move_y);
3999     int rnd_value = 24;
4000     int rnd = RND(rnd_value);
4001
4002 #if 0
4003     if (FrameCounter < 1 && x == 0 && y == 29)
4004       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4005 #endif
4006
4007     if (can_move_on && rnd > rnd_value / 8)
4008       MovDir[x][y] = old_move_dir;
4009     else if (can_turn_left && can_turn_right)
4010       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4011     else if (can_turn_left && rnd > rnd_value / 8)
4012       MovDir[x][y] = left_dir;
4013     else if (can_turn_right && rnd > rnd_value / 8)
4014       MovDir[x][y] = right_dir;
4015     else
4016       MovDir[x][y] = back_dir;
4017
4018     xx = x + move_xy[MovDir[x][y]].x;
4019     yy = y + move_xy[MovDir[x][y]].y;
4020
4021 #if 0
4022     if (FrameCounter < 1 && x == 0 && y == 29)
4023       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4024              xx, yy, Feld[xx][yy],
4025              FrameCounter);
4026 #endif
4027
4028 #if 1
4029     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4030       MovDir[x][y] = old_move_dir;
4031 #else
4032     if (!IS_FREE(xx, yy))
4033       MovDir[x][y] = old_move_dir;
4034 #endif
4035
4036 #if 0
4037     if (FrameCounter < 1 && x == 0 && y == 29)
4038       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4039 #endif
4040
4041     MovDelay[x][y] = 0;
4042   }
4043   else if (element == EL_MOLE)
4044   {
4045     boolean can_move_on =
4046       (MOLE_CAN_ENTER_FIELD(move_x, move_y,
4047                             IS_AMOEBOID(Feld[move_x][move_y]) ||
4048                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4049     if (!can_move_on)
4050     {
4051       boolean can_turn_left =
4052         (MOLE_CAN_ENTER_FIELD(left_x, left_y,
4053                               IS_AMOEBOID(Feld[left_x][left_y])));
4054
4055       boolean can_turn_right =
4056         (MOLE_CAN_ENTER_FIELD(right_x, right_y,
4057                               IS_AMOEBOID(Feld[right_x][right_y])));
4058
4059       if (can_turn_left && can_turn_right)
4060         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4061       else if (can_turn_left)
4062         MovDir[x][y] = left_dir;
4063       else
4064         MovDir[x][y] = right_dir;
4065     }
4066
4067     if (MovDir[x][y] != old_move_dir)
4068       MovDelay[x][y] = 9;
4069   }
4070   else if (element == EL_BALLOON)
4071   {
4072     MovDir[x][y] = game.balloon_dir;
4073     MovDelay[x][y] = 0;
4074   }
4075   else if (element == EL_SPRING)
4076   {
4077 #if 0
4078     if (MovDir[x][y] & MV_HORIZONTAL &&
4079         !SPRING_CAN_ENTER_FIELD(move_x, move_y))
4080       MovDir[x][y] = MV_NO_MOVING;
4081 #else
4082     if (MovDir[x][y] & MV_HORIZONTAL &&
4083         (!SPRING_CAN_ENTER_FIELD(move_x, move_y) ||
4084          SPRING_CAN_ENTER_FIELD(x, y + 1)))
4085       MovDir[x][y] = MV_NO_MOVING;
4086 #endif
4087
4088     MovDelay[x][y] = 0;
4089   }
4090   else if (element == EL_ROBOT ||
4091            element == EL_SATELLITE ||
4092            element == EL_PENGUIN)
4093   {
4094     int attr_x = -1, attr_y = -1;
4095
4096     if (AllPlayersGone)
4097     {
4098       attr_x = ExitX;
4099       attr_y = ExitY;
4100     }
4101     else
4102     {
4103       int i;
4104
4105       for (i = 0; i < MAX_PLAYERS; i++)
4106       {
4107         struct PlayerInfo *player = &stored_player[i];
4108         int jx = player->jx, jy = player->jy;
4109
4110         if (!player->active)
4111           continue;
4112
4113         if (attr_x == -1 ||
4114             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4115         {
4116           attr_x = jx;
4117           attr_y = jy;
4118         }
4119       }
4120     }
4121
4122     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4123     {
4124       attr_x = ZX;
4125       attr_y = ZY;
4126     }
4127
4128     if (element == EL_PENGUIN)
4129     {
4130       int i;
4131       static int xy[4][2] =
4132       {
4133         { 0, -1 },
4134         { -1, 0 },
4135         { +1, 0 },
4136         { 0, +1 }
4137       };
4138
4139       for (i = 0; i < NUM_DIRECTIONS; i++)
4140       {
4141         int ex = x + xy[i][0];
4142         int ey = y + xy[i][1];
4143
4144         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4145         {
4146           attr_x = ex;
4147           attr_y = ey;
4148           break;
4149         }
4150       }
4151     }
4152
4153     MovDir[x][y] = MV_NO_MOVING;
4154     if (attr_x < x)
4155       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4156     else if (attr_x > x)
4157       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4158     if (attr_y < y)
4159       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4160     else if (attr_y > y)
4161       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4162
4163     if (element == EL_ROBOT)
4164     {
4165       int newx, newy;
4166
4167       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4168         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4169       Moving2Blocked(x, y, &newx, &newy);
4170
4171       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4172         MovDelay[x][y] = 8 + 8 * !RND(3);
4173       else
4174         MovDelay[x][y] = 16;
4175     }
4176     else if (element == EL_PENGUIN)
4177     {
4178       int newx, newy;
4179
4180       MovDelay[x][y] = 1;
4181
4182       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4183       {
4184         boolean first_horiz = RND(2);
4185         int new_move_dir = MovDir[x][y];
4186
4187         MovDir[x][y] =
4188           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4189         Moving2Blocked(x, y, &newx, &newy);
4190
4191         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4192           return;
4193
4194         MovDir[x][y] =
4195           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4196         Moving2Blocked(x, y, &newx, &newy);
4197
4198         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4199           return;
4200
4201         MovDir[x][y] = old_move_dir;
4202         return;
4203       }
4204     }
4205     else        /* (element == EL_SATELLITE) */
4206     {
4207       int newx, newy;
4208
4209       MovDelay[x][y] = 1;
4210
4211       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4212       {
4213         boolean first_horiz = RND(2);
4214         int new_move_dir = MovDir[x][y];
4215
4216         MovDir[x][y] =
4217           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4218         Moving2Blocked(x, y, &newx, &newy);
4219
4220         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4221           return;
4222
4223         MovDir[x][y] =
4224           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4225         Moving2Blocked(x, y, &newx, &newy);
4226
4227         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4228           return;
4229
4230         MovDir[x][y] = old_move_dir;
4231         return;
4232       }
4233     }
4234   }
4235   else if (move_pattern == MV_TURNING_LEFT ||
4236            move_pattern == MV_TURNING_RIGHT ||
4237            move_pattern == MV_TURNING_LEFT_RIGHT ||
4238            move_pattern == MV_TURNING_RIGHT_LEFT ||
4239            move_pattern == MV_TURNING_RANDOM ||
4240            move_pattern == MV_ALL_DIRECTIONS)
4241   {
4242     boolean can_turn_left =
4243       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4244     boolean can_turn_right =
4245       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4246
4247     if (move_pattern == MV_TURNING_LEFT)
4248       MovDir[x][y] = left_dir;
4249     else if (move_pattern == MV_TURNING_RIGHT)
4250       MovDir[x][y] = right_dir;
4251     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4252       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4253     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4254       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4255     else if (move_pattern == MV_TURNING_RANDOM)
4256       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4257                       can_turn_right && !can_turn_left ? right_dir :
4258                       RND(2) ? left_dir : right_dir);
4259     else if (can_turn_left && can_turn_right)
4260       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4261     else if (can_turn_left)
4262       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4263     else if (can_turn_right)
4264       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4265     else
4266       MovDir[x][y] = back_dir;
4267
4268     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4269   }
4270   else if (move_pattern == MV_HORIZONTAL ||
4271            move_pattern == MV_VERTICAL)
4272   {
4273     if (move_pattern & old_move_dir)
4274       MovDir[x][y] = back_dir;
4275     else if (move_pattern == MV_HORIZONTAL)
4276       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4277     else if (move_pattern == MV_VERTICAL)
4278       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4279
4280     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4281   }
4282   else if (move_pattern & MV_ANY_DIRECTION)
4283   {
4284     MovDir[x][y] = move_pattern;
4285     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4286   }
4287   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4288   {
4289     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4290       MovDir[x][y] = left_dir;
4291     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4292       MovDir[x][y] = right_dir;
4293
4294     if (MovDir[x][y] != old_move_dir)
4295       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4296   }
4297   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4298   {
4299     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4300       MovDir[x][y] = right_dir;
4301     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4302       MovDir[x][y] = left_dir;
4303
4304     if (MovDir[x][y] != old_move_dir)
4305       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4306   }
4307   else if (move_pattern == MV_TOWARDS_PLAYER ||
4308            move_pattern == MV_AWAY_FROM_PLAYER)
4309   {
4310     int attr_x = -1, attr_y = -1;
4311     int newx, newy;
4312     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4313
4314     if (AllPlayersGone)
4315     {
4316       attr_x = ExitX;
4317       attr_y = ExitY;
4318     }
4319     else
4320     {
4321       int i;
4322
4323       for (i = 0; i < MAX_PLAYERS; i++)
4324       {
4325         struct PlayerInfo *player = &stored_player[i];
4326         int jx = player->jx, jy = player->jy;
4327
4328         if (!player->active)
4329           continue;
4330
4331         if (attr_x == -1 ||
4332             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4333         {
4334           attr_x = jx;
4335           attr_y = jy;
4336         }
4337       }
4338     }
4339
4340     MovDir[x][y] = MV_NO_MOVING;
4341     if (attr_x < x)
4342       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4343     else if (attr_x > x)
4344       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4345     if (attr_y < y)
4346       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4347     else if (attr_y > y)
4348       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4349
4350     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4351
4352     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4353     {
4354       boolean first_horiz = RND(2);
4355       int new_move_dir = MovDir[x][y];
4356
4357       MovDir[x][y] =
4358         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4359       Moving2Blocked(x, y, &newx, &newy);
4360
4361       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4362         return;
4363
4364       MovDir[x][y] =
4365         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4366       Moving2Blocked(x, y, &newx, &newy);
4367
4368       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4369         return;
4370
4371       MovDir[x][y] = old_move_dir;
4372     }
4373   }
4374   else if (move_pattern == MV_WHEN_PUSHED ||
4375            move_pattern == MV_WHEN_DROPPED)
4376   {
4377     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4378       MovDir[x][y] = MV_NO_MOVING;
4379
4380     MovDelay[x][y] = 0;
4381   }
4382   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4383   {
4384     static int test_xy[7][2] =
4385     {
4386       { 0, -1 },
4387       { -1, 0 },
4388       { +1, 0 },
4389       { 0, +1 },
4390       { 0, -1 },
4391       { -1, 0 },
4392       { +1, 0 },
4393     };
4394     static int test_dir[7] =
4395     {
4396       MV_UP,
4397       MV_LEFT,
4398       MV_RIGHT,
4399       MV_DOWN,
4400       MV_UP,
4401       MV_LEFT,
4402       MV_RIGHT,
4403     };
4404     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4405     int move_preference = -1000000;     /* start with very low preference */
4406     int new_move_dir = MV_NO_MOVING;
4407     int start_test = RND(4);
4408     int i;
4409
4410     for (i = 0; i < NUM_DIRECTIONS; i++)
4411     {
4412       int move_dir = test_dir[start_test + i];
4413       int move_dir_preference;
4414
4415       xx = x + test_xy[start_test + i][0];
4416       yy = y + test_xy[start_test + i][1];
4417
4418       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4419           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4420       {
4421         new_move_dir = move_dir;
4422
4423         break;
4424       }
4425
4426       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4427         continue;
4428
4429       move_dir_preference = -1 * RunnerVisit[xx][yy];
4430       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4431         move_dir_preference = PlayerVisit[xx][yy];
4432
4433       if (move_dir_preference > move_preference)
4434       {
4435         /* prefer field that has not been visited for the longest time */
4436         move_preference = move_dir_preference;
4437         new_move_dir = move_dir;
4438       }
4439       else if (move_dir_preference == move_preference &&
4440                move_dir == old_move_dir)
4441       {
4442         /* prefer last direction when all directions are preferred equally */
4443         move_preference = move_dir_preference;
4444         new_move_dir = move_dir;
4445       }
4446     }
4447
4448     MovDir[x][y] = new_move_dir;
4449     if (old_move_dir != new_move_dir)
4450       MovDelay[x][y] = 9;
4451   }
4452 }
4453
4454 static void TurnRound(int x, int y)
4455 {
4456   int direction = MovDir[x][y];
4457
4458 #if 0
4459   GfxDir[x][y] = MovDir[x][y];
4460 #endif
4461
4462   TurnRoundExt(x, y);
4463
4464 #if 1
4465   GfxDir[x][y] = MovDir[x][y];
4466 #endif
4467
4468   if (direction != MovDir[x][y])
4469     GfxFrame[x][y] = 0;
4470
4471 #if 1
4472   if (MovDelay[x][y])
4473     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4474 #else
4475   if (MovDelay[x][y])
4476     GfxAction[x][y] = ACTION_WAITING;
4477 #endif
4478 }
4479
4480 static boolean JustBeingPushed(int x, int y)
4481 {
4482   int i;
4483
4484   for (i = 0; i < MAX_PLAYERS; i++)
4485   {
4486     struct PlayerInfo *player = &stored_player[i];
4487
4488     if (player->active && player->is_pushing && player->MovPos)
4489     {
4490       int next_jx = player->jx + (player->jx - player->last_jx);
4491       int next_jy = player->jy + (player->jy - player->last_jy);
4492
4493       if (x == next_jx && y == next_jy)
4494         return TRUE;
4495     }
4496   }
4497
4498   return FALSE;
4499 }
4500
4501 void StartMoving(int x, int y)
4502 {
4503 #if 0
4504   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4505 #endif
4506   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4507   int element = Feld[x][y];
4508
4509   if (Stop[x][y])
4510     return;
4511
4512 #if 1
4513   if (MovDelay[x][y] == 0)
4514     GfxAction[x][y] = ACTION_DEFAULT;
4515 #else
4516   /* !!! this should be handled more generic (not only for mole) !!! */
4517   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4518     GfxAction[x][y] = ACTION_DEFAULT;
4519 #endif
4520
4521   if (CAN_FALL(element) && y < lev_fieldy - 1)
4522   {
4523     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
4524         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4525       if (JustBeingPushed(x, y))
4526         return;
4527
4528     if (element == EL_QUICKSAND_FULL)
4529     {
4530       if (IS_FREE(x, y + 1))
4531       {
4532         InitMovingField(x, y, MV_DOWN);
4533         started_moving = TRUE;
4534
4535         Feld[x][y] = EL_QUICKSAND_EMPTYING;
4536         Store[x][y] = EL_ROCK;
4537 #if 1
4538         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4539 #else
4540         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4541 #endif
4542       }
4543       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4544       {
4545         if (!MovDelay[x][y])
4546           MovDelay[x][y] = TILEY + 1;
4547
4548         if (MovDelay[x][y])
4549         {
4550           MovDelay[x][y]--;
4551           if (MovDelay[x][y])
4552             return;
4553         }
4554
4555         Feld[x][y] = EL_QUICKSAND_EMPTY;
4556         Feld[x][y + 1] = EL_QUICKSAND_FULL;
4557         Store[x][y + 1] = Store[x][y];
4558         Store[x][y] = 0;
4559 #if 1
4560         PlayLevelSoundAction(x, y, ACTION_FILLING);
4561 #else
4562         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4563 #endif
4564       }
4565     }
4566     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4567              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4568     {
4569       InitMovingField(x, y, MV_DOWN);
4570       started_moving = TRUE;
4571
4572       Feld[x][y] = EL_QUICKSAND_FILLING;
4573       Store[x][y] = element;
4574 #if 1
4575       PlayLevelSoundAction(x, y, ACTION_FILLING);
4576 #else
4577       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4578 #endif
4579     }
4580     else if (element == EL_MAGIC_WALL_FULL)
4581     {
4582       if (IS_FREE(x, y + 1))
4583       {
4584         InitMovingField(x, y, MV_DOWN);
4585         started_moving = TRUE;
4586
4587         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4588         Store[x][y] = EL_CHANGED(Store[x][y]);
4589       }
4590       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4591       {
4592         if (!MovDelay[x][y])
4593           MovDelay[x][y] = TILEY/4 + 1;
4594
4595         if (MovDelay[x][y])
4596         {
4597           MovDelay[x][y]--;
4598           if (MovDelay[x][y])
4599             return;
4600         }
4601
4602         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4603         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4604         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4605         Store[x][y] = 0;
4606       }
4607     }
4608     else if (element == EL_BD_MAGIC_WALL_FULL)
4609     {
4610       if (IS_FREE(x, y + 1))
4611       {
4612         InitMovingField(x, y, MV_DOWN);
4613         started_moving = TRUE;
4614
4615         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4616         Store[x][y] = EL_CHANGED2(Store[x][y]);
4617       }
4618       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4619       {
4620         if (!MovDelay[x][y])
4621           MovDelay[x][y] = TILEY/4 + 1;
4622
4623         if (MovDelay[x][y])
4624         {
4625           MovDelay[x][y]--;
4626           if (MovDelay[x][y])
4627             return;
4628         }
4629
4630         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4631         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4632         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4633         Store[x][y] = 0;
4634       }
4635     }
4636     else if (CAN_PASS_MAGIC_WALL(element) &&
4637              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4638               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4639     {
4640       InitMovingField(x, y, MV_DOWN);
4641       started_moving = TRUE;
4642
4643       Feld[x][y] =
4644         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4645          EL_BD_MAGIC_WALL_FILLING);
4646       Store[x][y] = element;
4647     }
4648 #if 0
4649     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4650 #else
4651     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4652 #endif
4653     {
4654       SplashAcid(x, y + 1);
4655
4656       InitMovingField(x, y, MV_DOWN);
4657       started_moving = TRUE;
4658
4659       Store[x][y] = EL_ACID;
4660 #if 0
4661       /* !!! TEST !!! better use "_FALLING" etc. !!! */
4662       GfxAction[x][y + 1] = ACTION_ACTIVE;
4663 #endif
4664     }
4665 #if 1
4666     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4667               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4668               (Feld[x][y + 1] == EL_BLOCKED)) ||
4669              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4670               CAN_SMASH(element) && WasJustFalling[x][y] &&
4671               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4672
4673 #else
4674 #if 1
4675     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4676              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4677              WasJustMoving[x][y] && !Pushed[x][y + 1])
4678 #else
4679     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4680              WasJustMoving[x][y])
4681 #endif
4682 #endif
4683
4684     {
4685       /* this is needed for a special case not covered by calling "Impact()"
4686          from "ContinueMoving()": if an element moves to a tile directly below
4687          another element which was just falling on that tile (which was empty
4688          in the previous frame), the falling element above would just stop
4689          instead of smashing the element below (in previous version, the above
4690          element was just checked for "moving" instead of "falling", resulting
4691          in incorrect smashes caused by horizontal movement of the above
4692          element; also, the case of the player being the element to smash was
4693          simply not covered here... :-/ ) */
4694
4695 #if 0
4696       WasJustMoving[x][y] = 0;
4697       WasJustFalling[x][y] = 0;
4698 #endif
4699
4700       Impact(x, y);
4701     }
4702     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4703     {
4704       if (MovDir[x][y] == MV_NO_MOVING)
4705       {
4706         InitMovingField(x, y, MV_DOWN);
4707         started_moving = TRUE;
4708       }
4709     }
4710     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4711     {
4712       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4713         MovDir[x][y] = MV_DOWN;
4714
4715       InitMovingField(x, y, MV_DOWN);
4716       started_moving = TRUE;
4717     }
4718     else if (element == EL_AMOEBA_DROP)
4719     {
4720       Feld[x][y] = EL_AMOEBA_GROWING;
4721       Store[x][y] = EL_AMOEBA_WET;
4722     }
4723     /* Store[x][y + 1] must be zero, because:
4724        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4725     */
4726 #if 0
4727 #if OLD_GAME_BEHAVIOUR
4728     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4729 #else
4730     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4731              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4732              element != EL_DX_SUPABOMB)
4733 #endif
4734 #else
4735     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4736               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4737              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4738              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4739 #endif
4740     {
4741       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
4742                                 (IS_FREE(x - 1, y + 1) ||
4743                                  Feld[x - 1][y + 1] == EL_ACID));
4744       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4745                                 (IS_FREE(x + 1, y + 1) ||
4746                                  Feld[x + 1][y + 1] == EL_ACID));
4747       boolean can_fall_any  = (can_fall_left || can_fall_right);
4748       boolean can_fall_both = (can_fall_left && can_fall_right);
4749
4750       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4751       {
4752         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4753
4754         if (slippery_type == SLIPPERY_ONLY_LEFT)
4755           can_fall_right = FALSE;
4756         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4757           can_fall_left = FALSE;
4758         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4759           can_fall_right = FALSE;
4760         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4761           can_fall_left = FALSE;
4762
4763         can_fall_any  = (can_fall_left || can_fall_right);
4764         can_fall_both = (can_fall_left && can_fall_right);
4765       }
4766
4767       if (can_fall_any)
4768       {
4769         if (can_fall_both &&
4770             (game.emulation != EMU_BOULDERDASH &&
4771              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4772           can_fall_left = !(can_fall_right = RND(2));
4773
4774         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4775         started_moving = TRUE;
4776       }
4777     }
4778 #if 0
4779     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4780 #else
4781     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4782 #endif
4783     {
4784       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4785       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4786       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4787       int belt_dir = game.belt_dir[belt_nr];
4788
4789       if ((belt_dir == MV_LEFT  && left_is_free) ||
4790           (belt_dir == MV_RIGHT && right_is_free))
4791       {
4792 #if 1
4793         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4794 #endif
4795
4796         InitMovingField(x, y, belt_dir);
4797         started_moving = TRUE;
4798
4799 #if 1
4800         Pushed[x][y] = TRUE;
4801         Pushed[nextx][y] = TRUE;
4802 #endif
4803
4804         GfxAction[x][y] = ACTION_DEFAULT;
4805       }
4806       else
4807       {
4808         MovDir[x][y] = 0;       /* if element was moving, stop it */
4809       }
4810     }
4811   }
4812
4813   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4814   if (CAN_MOVE(element) && !started_moving)
4815   {
4816     int move_pattern = element_info[element].move_pattern;
4817     int newx, newy;
4818
4819     Moving2Blocked(x, y, &newx, &newy);
4820
4821 #if 1
4822     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4823       return;
4824 #else
4825     if ((element == EL_SATELLITE ||
4826          element == EL_BALLOON ||
4827          element == EL_SPRING)
4828         && JustBeingPushed(x, y))
4829       return;
4830 #endif
4831
4832 #if 1
4833     if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4834         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4835         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4836     {
4837 #if 0
4838       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4839              element, element_info[element].token_name,
4840              WasJustMoving[x][y],
4841              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4842              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4843              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4844              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4845 #endif
4846
4847 #if 1
4848       WasJustMoving[x][y] = 0;
4849 #endif
4850
4851       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4852
4853 #if 0
4854       if (Feld[x][y] != element)        /* element has changed */
4855       {
4856         element = Feld[x][y];
4857         move_pattern = element_info[element].move_pattern;
4858
4859         if (!CAN_MOVE(element))
4860           return;
4861       }
4862 #else
4863       if (Feld[x][y] != element)        /* element has changed */
4864         return;
4865 #endif
4866     }
4867 #endif
4868
4869 #if 0
4870 #if 0
4871     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4872       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
4873 #else
4874     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4875     {
4876       Moving2Blocked(x, y, &newx, &newy);
4877       if (Feld[newx][newy] == EL_BLOCKED)
4878         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
4879     }
4880 #endif
4881 #endif
4882
4883 #if 0
4884     if (FrameCounter < 1 && x == 0 && y == 29)
4885       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4886 #endif
4887
4888     if (!MovDelay[x][y])        /* start new movement phase */
4889     {
4890       /* all objects that can change their move direction after each step
4891          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4892
4893       if (element != EL_YAMYAM &&
4894           element != EL_DARK_YAMYAM &&
4895           element != EL_PACMAN &&
4896           !(move_pattern & MV_ANY_DIRECTION) &&
4897           move_pattern != MV_TURNING_LEFT &&
4898           move_pattern != MV_TURNING_RIGHT &&
4899           move_pattern != MV_TURNING_LEFT_RIGHT &&
4900           move_pattern != MV_TURNING_RIGHT_LEFT &&
4901           move_pattern != MV_TURNING_RANDOM)
4902       {
4903         TurnRound(x, y);
4904
4905 #if 0
4906         if (FrameCounter < 1 && x == 0 && y == 29)
4907           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4908 #endif
4909
4910         if (MovDelay[x][y] && (element == EL_BUG ||
4911                                element == EL_SPACESHIP ||
4912                                element == EL_SP_SNIKSNAK ||
4913                                element == EL_SP_ELECTRON ||
4914                                element == EL_MOLE))
4915           DrawLevelField(x, y);
4916       }
4917     }
4918
4919     if (MovDelay[x][y])         /* wait some time before next movement */
4920     {
4921       MovDelay[x][y]--;
4922
4923 #if 0
4924       if (element == EL_YAMYAM)
4925       {
4926         printf("::: %d\n",
4927                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4928         DrawLevelElementAnimation(x, y, element);
4929       }
4930 #endif
4931
4932       if (MovDelay[x][y])       /* element still has to wait some time */
4933       {
4934 #if 0
4935         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4936         ResetGfxAnimation(x, y);
4937 #endif
4938
4939 #if 0
4940         if (GfxAction[x][y] != ACTION_WAITING)
4941           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4942
4943         GfxAction[x][y] = ACTION_WAITING;
4944 #endif
4945       }
4946
4947       if (element == EL_ROBOT ||
4948 #if 0
4949           element == EL_PACMAN ||
4950 #endif
4951           element == EL_YAMYAM ||
4952           element == EL_DARK_YAMYAM)
4953       {
4954 #if 0
4955         DrawLevelElementAnimation(x, y, element);
4956 #else
4957         DrawLevelElementAnimationIfNeeded(x, y, element);
4958 #endif
4959         PlayLevelSoundAction(x, y, ACTION_WAITING);
4960       }
4961       else if (element == EL_SP_ELECTRON)
4962         DrawLevelElementAnimationIfNeeded(x, y, element);
4963       else if (element == EL_DRAGON)
4964       {
4965         int i;
4966         int dir = MovDir[x][y];
4967         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4968         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
4969         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
4970                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
4971                        dir == MV_UP     ? IMG_FLAMES_1_UP :
4972                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4973         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4974
4975 #if 0
4976         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4977 #endif
4978
4979         GfxAction[x][y] = ACTION_ATTACKING;
4980
4981         if (IS_PLAYER(x, y))
4982           DrawPlayerField(x, y);
4983         else
4984           DrawLevelField(x, y);
4985
4986         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4987
4988         for (i = 1; i <= 3; i++)
4989         {
4990           int xx = x + i * dx;
4991           int yy = y + i * dy;
4992           int sx = SCREENX(xx);
4993           int sy = SCREENY(yy);
4994           int flame_graphic = graphic + (i - 1);
4995
4996           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4997             break;
4998
4999           if (MovDelay[x][y])
5000           {
5001             int flamed = MovingOrBlocked2Element(xx, yy);
5002
5003             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5004               Bang(xx, yy);
5005             else
5006               RemoveMovingField(xx, yy);
5007
5008             Feld[xx][yy] = EL_FLAMES;
5009             if (IN_SCR_FIELD(sx, sy))
5010             {
5011               DrawLevelFieldCrumbledSand(xx, yy);
5012               DrawGraphic(sx, sy, flame_graphic, frame);
5013             }
5014           }
5015           else
5016           {
5017             if (Feld[xx][yy] == EL_FLAMES)
5018               Feld[xx][yy] = EL_EMPTY;
5019             DrawLevelField(xx, yy);
5020           }
5021         }
5022       }
5023
5024       if (MovDelay[x][y])       /* element still has to wait some time */
5025       {
5026         PlayLevelSoundAction(x, y, ACTION_WAITING);
5027
5028         return;
5029       }
5030
5031 #if 0
5032       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5033          for all other elements GfxAction will be set by InitMovingField() */
5034       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5035         GfxAction[x][y] = ACTION_MOVING;
5036 #endif
5037     }
5038
5039     /* now make next step */
5040
5041     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5042
5043     if (DONT_COLLIDE_WITH(element) &&
5044         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5045         !PLAYER_ENEMY_PROTECTED(newx, newy))
5046     {
5047 #if 1
5048       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5049
5050       return;
5051 #else
5052       /* player killed by element which is deadly when colliding with */
5053       MovDir[x][y] = 0;
5054       KillHero(PLAYERINFO(newx, newy));
5055       return;
5056 #endif
5057
5058     }
5059 #if 1
5060 #if 1
5061     else if (CAN_MOVE_INTO_ACID(element) &&
5062              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5063              (MovDir[x][y] == MV_DOWN ||
5064               game.engine_version > VERSION_IDENT(3,0,8,0)))
5065 #else
5066     else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5067              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5068 #endif
5069 #else
5070
5071     else if ((element == EL_PENGUIN ||
5072               element == EL_ROBOT ||
5073               element == EL_SATELLITE ||
5074               element == EL_BALLOON ||
5075               IS_CUSTOM_ELEMENT(element)) &&
5076              IN_LEV_FIELD(newx, newy) &&
5077              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5078 #endif
5079     {
5080       SplashAcid(newx, newy);
5081       Store[x][y] = EL_ACID;
5082     }
5083     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5084     {
5085       if (Feld[newx][newy] == EL_EXIT_OPEN)
5086       {
5087 #if 1
5088         RemoveField(x, y);
5089         DrawLevelField(x, y);
5090 #else
5091         Feld[x][y] = EL_EMPTY;
5092         DrawLevelField(x, y);
5093 #endif
5094
5095         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5096         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5097           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5098
5099         local_player->friends_still_needed--;
5100         if (!local_player->friends_still_needed &&
5101             !local_player->GameOver && AllPlayersGone)
5102           local_player->LevelSolved = local_player->GameOver = TRUE;
5103
5104         return;
5105       }
5106       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5107       {
5108         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5109           DrawLevelField(newx, newy);
5110         else
5111           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5112       }
5113       else if (!IS_FREE(newx, newy))
5114       {
5115         GfxAction[x][y] = ACTION_WAITING;
5116
5117         if (IS_PLAYER(x, y))
5118           DrawPlayerField(x, y);
5119         else
5120           DrawLevelField(x, y);
5121
5122         return;
5123       }
5124     }
5125     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5126     {
5127       if (IS_FOOD_PIG(Feld[newx][newy]))
5128       {
5129         if (IS_MOVING(newx, newy))
5130           RemoveMovingField(newx, newy);
5131         else
5132         {
5133           Feld[newx][newy] = EL_EMPTY;
5134           DrawLevelField(newx, newy);
5135         }
5136
5137         PlayLevelSound(x, y, SND_PIG_DIGGING);
5138       }
5139       else if (!IS_FREE(newx, newy))
5140       {
5141         if (IS_PLAYER(x, y))
5142           DrawPlayerField(x, y);
5143         else
5144           DrawLevelField(x, y);
5145
5146         return;
5147       }
5148     }
5149
5150 #if 1
5151
5152     /*
5153     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5154     */
5155
5156     else if (IS_CUSTOM_ELEMENT(element) &&
5157              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5158
5159 #if 0
5160  &&
5161              !IS_FREE(newx, newy)
5162 #endif
5163
5164 )
5165     {
5166       int new_element = Feld[newx][newy];
5167
5168 #if 0
5169       printf("::: '%s' digs '%s' [%d]\n",
5170              element_info[element].token_name,
5171              element_info[Feld[newx][newy]].token_name,
5172              StorePlayer[newx][newy]);
5173 #endif
5174
5175       if (!IS_FREE(newx, newy))
5176       {
5177         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5178                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5179                       ACTION_BREAKING);
5180
5181         /* no element can dig solid indestructible elements */
5182         if (IS_INDESTRUCTIBLE(new_element) &&
5183             !IS_DIGGABLE(new_element) &&
5184             !IS_COLLECTIBLE(new_element))
5185           return;
5186
5187         if (AmoebaNr[newx][newy] &&
5188             (new_element == EL_AMOEBA_FULL ||
5189              new_element == EL_BD_AMOEBA ||
5190              new_element == EL_AMOEBA_GROWING))
5191         {
5192           AmoebaCnt[AmoebaNr[newx][newy]]--;
5193           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5194         }
5195
5196         if (IS_MOVING(newx, newy))
5197           RemoveMovingField(newx, newy);
5198         else
5199         {
5200           RemoveField(newx, newy);
5201           DrawLevelField(newx, newy);
5202         }
5203
5204         PlayLevelSoundAction(x, y, action);
5205       }
5206
5207       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5208         element_info[element].can_leave_element = TRUE;
5209
5210       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5211       {
5212         RunnerVisit[x][y] = FrameCounter;
5213         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5214       }
5215     }
5216
5217 #endif
5218
5219     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5220     {
5221       if (!IS_FREE(newx, newy))
5222       {
5223         if (IS_PLAYER(x, y))
5224           DrawPlayerField(x, y);
5225         else
5226           DrawLevelField(x, y);
5227
5228         return;
5229       }
5230       else
5231       {
5232         boolean wanna_flame = !RND(10);
5233         int dx = newx - x, dy = newy - y;
5234         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5235         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5236         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5237                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5238         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5239                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5240
5241         if ((wanna_flame ||
5242              IS_CLASSIC_ENEMY(element1) ||
5243              IS_CLASSIC_ENEMY(element2)) &&
5244             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5245             element1 != EL_FLAMES && element2 != EL_FLAMES)
5246         {
5247 #if 1
5248           ResetGfxAnimation(x, y);
5249           GfxAction[x][y] = ACTION_ATTACKING;
5250 #endif
5251
5252           if (IS_PLAYER(x, y))
5253             DrawPlayerField(x, y);
5254           else
5255             DrawLevelField(x, y);
5256
5257           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5258
5259           MovDelay[x][y] = 50;
5260
5261           Feld[newx][newy] = EL_FLAMES;
5262           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5263             Feld[newx1][newy1] = EL_FLAMES;
5264           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5265             Feld[newx2][newy2] = EL_FLAMES;
5266
5267           return;
5268         }
5269       }
5270     }
5271     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5272              Feld[newx][newy] == EL_DIAMOND)
5273     {
5274       if (IS_MOVING(newx, newy))
5275         RemoveMovingField(newx, newy);
5276       else
5277       {
5278         Feld[newx][newy] = EL_EMPTY;
5279         DrawLevelField(newx, newy);
5280       }
5281
5282       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5283     }
5284     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5285              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5286     {
5287       if (AmoebaNr[newx][newy])
5288       {
5289         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5290         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5291             Feld[newx][newy] == EL_BD_AMOEBA)
5292           AmoebaCnt[AmoebaNr[newx][newy]]--;
5293       }
5294
5295 #if 0
5296       /* !!! test !!! */
5297       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5298 #else
5299       if (IS_MOVING(newx, newy))
5300 #endif
5301       {
5302         RemoveMovingField(newx, newy);
5303       }
5304       else
5305       {
5306         Feld[newx][newy] = EL_EMPTY;
5307         DrawLevelField(newx, newy);
5308       }
5309
5310       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5311     }
5312     else if ((element == EL_PACMAN || element == EL_MOLE)
5313              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5314     {
5315       if (AmoebaNr[newx][newy])
5316       {
5317         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5318         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5319             Feld[newx][newy] == EL_BD_AMOEBA)
5320           AmoebaCnt[AmoebaNr[newx][newy]]--;
5321       }
5322
5323       if (element == EL_MOLE)
5324       {
5325         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5326         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5327
5328         ResetGfxAnimation(x, y);
5329         GfxAction[x][y] = ACTION_DIGGING;
5330         DrawLevelField(x, y);
5331
5332         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5333
5334         return;                         /* wait for shrinking amoeba */
5335       }
5336       else      /* element == EL_PACMAN */
5337       {
5338         Feld[newx][newy] = EL_EMPTY;
5339         DrawLevelField(newx, newy);
5340         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5341       }
5342     }
5343     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5344              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5345               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5346     {
5347       /* wait for shrinking amoeba to completely disappear */
5348       return;
5349     }
5350     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5351     {
5352       /* object was running against a wall */
5353
5354       TurnRound(x, y);
5355
5356 #if 1
5357       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5358         DrawLevelElementAnimation(x, y, element);
5359 #else
5360       if (element == EL_BUG ||
5361           element == EL_SPACESHIP ||
5362           element == EL_SP_SNIKSNAK)
5363         DrawLevelField(x, y);
5364       else if (element == EL_MOLE)
5365         DrawLevelField(x, y);
5366       else if (element == EL_BD_BUTTERFLY ||
5367                element == EL_BD_FIREFLY)
5368         DrawLevelElementAnimationIfNeeded(x, y, element);
5369       else if (element == EL_SATELLITE)
5370         DrawLevelElementAnimationIfNeeded(x, y, element);
5371       else if (element == EL_SP_ELECTRON)
5372         DrawLevelElementAnimationIfNeeded(x, y, element);
5373 #endif
5374
5375       if (DONT_TOUCH(element))
5376         TestIfBadThingTouchesHero(x, y);
5377
5378 #if 0
5379       PlayLevelSoundAction(x, y, ACTION_WAITING);
5380 #endif
5381
5382       return;
5383     }
5384
5385     InitMovingField(x, y, MovDir[x][y]);
5386
5387     PlayLevelSoundAction(x, y, ACTION_MOVING);
5388   }
5389
5390   if (MovDir[x][y])
5391     ContinueMoving(x, y);
5392 }
5393
5394 void ContinueMoving(int x, int y)
5395 {
5396   int element = Feld[x][y];
5397   struct ElementInfo *ei = &element_info[element];
5398   int direction = MovDir[x][y];
5399   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5400   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5401   int newx = x + dx, newy = y + dy;
5402 #if 0
5403   int nextx = newx + dx, nexty = newy + dy;
5404 #endif
5405 #if 1
5406   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
5407   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5408 #else
5409   boolean pushed_by_player = Pushed[x][y];
5410 #endif
5411
5412   MovPos[x][y] += getElementMoveStepsize(x, y);
5413
5414 #if 0
5415   if (pushed_by_player && IS_PLAYER(x, y))
5416   {
5417     /* special case: moving object pushed by player */
5418     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5419   }
5420 #else
5421   if (pushed_by_player) /* special case: moving object pushed by player */
5422     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5423 #endif
5424
5425   if (ABS(MovPos[x][y]) < TILEX)
5426   {
5427     DrawLevelField(x, y);
5428
5429     return;     /* element is still moving */
5430   }
5431
5432   /* element reached destination field */
5433
5434   Feld[x][y] = EL_EMPTY;
5435   Feld[newx][newy] = element;
5436   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
5437
5438   if (element == EL_MOLE)
5439   {
5440     Feld[x][y] = EL_SAND;
5441
5442     DrawLevelFieldCrumbledSandNeighbours(x, y);
5443   }
5444   else if (element == EL_QUICKSAND_FILLING)
5445   {
5446     element = Feld[newx][newy] = get_next_element(element);
5447     Store[newx][newy] = Store[x][y];
5448   }
5449   else if (element == EL_QUICKSAND_EMPTYING)
5450   {
5451     Feld[x][y] = get_next_element(element);
5452     element = Feld[newx][newy] = Store[x][y];
5453   }
5454   else if (element == EL_MAGIC_WALL_FILLING)
5455   {
5456     element = Feld[newx][newy] = get_next_element(element);
5457     if (!game.magic_wall_active)
5458       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5459     Store[newx][newy] = Store[x][y];
5460   }
5461   else if (element == EL_MAGIC_WALL_EMPTYING)
5462   {
5463     Feld[x][y] = get_next_element(element);
5464     if (!game.magic_wall_active)
5465       Feld[x][y] = EL_MAGIC_WALL_DEAD;
5466     element = Feld[newx][newy] = Store[x][y];
5467   }
5468   else if (element == EL_BD_MAGIC_WALL_FILLING)
5469   {
5470     element = Feld[newx][newy] = get_next_element(element);
5471     if (!game.magic_wall_active)
5472       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5473     Store[newx][newy] = Store[x][y];
5474   }
5475   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5476   {
5477     Feld[x][y] = get_next_element(element);
5478     if (!game.magic_wall_active)
5479       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5480     element = Feld[newx][newy] = Store[x][y];
5481   }
5482   else if (element == EL_AMOEBA_DROPPING)
5483   {
5484     Feld[x][y] = get_next_element(element);
5485     element = Feld[newx][newy] = Store[x][y];
5486   }
5487   else if (element == EL_SOKOBAN_OBJECT)
5488   {
5489     if (Back[x][y])
5490       Feld[x][y] = Back[x][y];
5491
5492     if (Back[newx][newy])
5493       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5494
5495     Back[x][y] = Back[newx][newy] = 0;
5496   }
5497   else if (Store[x][y] == EL_ACID)
5498   {
5499     element = Feld[newx][newy] = EL_ACID;
5500   }
5501
5502   Store[x][y] = 0;
5503   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5504   MovDelay[newx][newy] = 0;
5505
5506   /* copy element change control values to new field */
5507   ChangeDelay[newx][newy] = ChangeDelay[x][y];
5508   ChangePage[newx][newy] = ChangePage[x][y];
5509   Changed[newx][newy] = Changed[x][y];
5510   ChangeEvent[newx][newy] = ChangeEvent[x][y];
5511
5512   ChangeDelay[x][y] = 0;
5513   ChangePage[x][y] = -1;
5514   Changed[x][y] = CE_BITMASK_DEFAULT;
5515   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5516
5517   /* copy animation control values to new field */
5518   GfxFrame[newx][newy]  = GfxFrame[x][y];
5519   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
5520   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
5521   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
5522
5523   Pushed[x][y] = Pushed[newx][newy] = FALSE;
5524
5525   ResetGfxAnimation(x, y);      /* reset animation values for old field */
5526
5527 #if 1
5528   /* some elements can leave other elements behind after moving */
5529   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5530       ei->move_leave_element != EL_EMPTY &&
5531       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5532        ei->can_leave_element_last))
5533   {
5534     Feld[x][y] = ei->move_leave_element;
5535     InitField(x, y, FALSE);
5536
5537     if (GFX_CRUMBLED(Feld[x][y]))
5538       DrawLevelFieldCrumbledSandNeighbours(x, y);
5539   }
5540
5541   ei->can_leave_element_last = ei->can_leave_element;
5542   ei->can_leave_element = FALSE;
5543 #endif
5544
5545 #if 0
5546   /* 2.1.1 (does not work correctly for spring) */
5547   if (!CAN_MOVE(element))
5548     MovDir[newx][newy] = 0;
5549 #else
5550
5551 #if 0
5552   /* (does not work for falling objects that slide horizontally) */
5553   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5554     MovDir[newx][newy] = 0;
5555 #else
5556   /*
5557   if (!CAN_MOVE(element) ||
5558       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5559     MovDir[newx][newy] = 0;
5560   */
5561
5562   if (!CAN_MOVE(element) ||
5563       (CAN_FALL(element) && direction == MV_DOWN))
5564     GfxDir[x][y] = MovDir[newx][newy] = 0;
5565
5566 #endif
5567 #endif
5568
5569   DrawLevelField(x, y);
5570   DrawLevelField(newx, newy);
5571
5572   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
5573
5574   /* prevent pushed element from moving on in pushed direction */
5575   if (pushed_by_player && CAN_MOVE(element) &&
5576       element_info[element].move_pattern & MV_ANY_DIRECTION &&
5577       !(element_info[element].move_pattern & direction))
5578     TurnRound(newx, newy);
5579
5580 #if 1
5581   /* prevent elements on conveyor belt from moving on in last direction */
5582   if (pushed_by_conveyor && CAN_FALL(element) &&
5583       direction & MV_HORIZONTAL)
5584     MovDir[newx][newy] = 0;
5585 #endif
5586
5587   if (!pushed_by_player)
5588   {
5589     WasJustMoving[newx][newy] = 3;
5590
5591     if (CAN_FALL(element) && direction == MV_DOWN)
5592       WasJustFalling[newx][newy] = 3;
5593   }
5594
5595   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
5596   {
5597     TestIfBadThingTouchesHero(newx, newy);
5598     TestIfBadThingTouchesFriend(newx, newy);
5599
5600     if (!IS_CUSTOM_ELEMENT(element))
5601       TestIfBadThingTouchesOtherBadThing(newx, newy);
5602   }
5603   else if (element == EL_PENGUIN)
5604     TestIfFriendTouchesBadThing(newx, newy);
5605
5606   if (CAN_FALL(element) && direction == MV_DOWN &&
5607       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5608     Impact(x, newy);
5609
5610 #if 1
5611   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
5612 #endif
5613
5614 #if 0
5615   if (ChangePage[newx][newy] != -1)                     /* delayed change */
5616     ChangeElement(newx, newy, ChangePage[newx][newy]);
5617 #endif
5618
5619 #if 1
5620
5621   TestIfElementHitsCustomElement(newx, newy, direction);
5622
5623 #else
5624
5625   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5626   {
5627     int hitting_element = Feld[newx][newy];
5628
5629     /* !!! fix side (direction) orientation here and elsewhere !!! */
5630     CheckElementSideChange(newx, newy, hitting_element,
5631                            direction, CE_HITTING_SOMETHING, -1);
5632
5633 #if 0
5634     if (IN_LEV_FIELD(nextx, nexty))
5635     {
5636       int opposite_direction = MV_DIR_OPPOSITE(direction);
5637       int hitting_side = direction;
5638       int touched_side = opposite_direction;
5639       int touched_element = MovingOrBlocked2Element(nextx, nexty);
5640       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5641                             MovDir[nextx][nexty] != direction ||
5642                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5643
5644       if (object_hit)
5645       {
5646         int i;
5647
5648         CheckElementSideChange(nextx, nexty, touched_element,
5649                                opposite_direction, CE_HIT_BY_SOMETHING, -1);
5650
5651         if (IS_CUSTOM_ELEMENT(hitting_element) &&
5652             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5653         {
5654           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5655           {
5656             struct ElementChangeInfo *change =
5657               &element_info[hitting_element].change_page[i];
5658
5659             if (change->can_change &&
5660                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5661                 change->sides & touched_side &&
5662                 change->trigger_element == touched_element)
5663             {
5664               CheckElementSideChange(newx, newy, hitting_element,
5665                                      CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5666               break;
5667             }
5668           }
5669         }
5670
5671         if (IS_CUSTOM_ELEMENT(touched_element) &&
5672             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5673         {
5674           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5675           {
5676             struct ElementChangeInfo *change =
5677               &element_info[touched_element].change_page[i];
5678
5679             if (change->can_change &&
5680                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5681                 change->sides & hitting_side &&
5682                 change->trigger_element == hitting_element)
5683             {
5684               CheckElementSideChange(nextx, nexty, touched_element,
5685                                      CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5686               break;
5687             }
5688           }
5689         }
5690       }
5691     }
5692 #endif
5693   }
5694 #endif
5695
5696   TestIfPlayerTouchesCustomElement(newx, newy);
5697   TestIfElementTouchesCustomElement(newx, newy);
5698 }
5699
5700 int AmoebeNachbarNr(int ax, int ay)
5701 {
5702   int i;
5703   int element = Feld[ax][ay];
5704   int group_nr = 0;
5705   static int xy[4][2] =
5706   {
5707     { 0, -1 },
5708     { -1, 0 },
5709     { +1, 0 },
5710     { 0, +1 }
5711   };
5712
5713   for (i = 0; i < NUM_DIRECTIONS; i++)
5714   {
5715     int x = ax + xy[i][0];
5716     int y = ay + xy[i][1];
5717
5718     if (!IN_LEV_FIELD(x, y))
5719       continue;
5720
5721     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5722       group_nr = AmoebaNr[x][y];
5723   }
5724
5725   return group_nr;
5726 }
5727
5728 void AmoebenVereinigen(int ax, int ay)
5729 {
5730   int i, x, y, xx, yy;
5731   int new_group_nr = AmoebaNr[ax][ay];
5732   static int xy[4][2] =
5733   {
5734     { 0, -1 },
5735     { -1, 0 },
5736     { +1, 0 },
5737     { 0, +1 }
5738   };
5739
5740   if (new_group_nr == 0)
5741     return;
5742
5743   for (i = 0; i < NUM_DIRECTIONS; i++)
5744   {
5745     x = ax + xy[i][0];
5746     y = ay + xy[i][1];
5747
5748     if (!IN_LEV_FIELD(x, y))
5749       continue;
5750
5751     if ((Feld[x][y] == EL_AMOEBA_FULL ||
5752          Feld[x][y] == EL_BD_AMOEBA ||
5753          Feld[x][y] == EL_AMOEBA_DEAD) &&
5754         AmoebaNr[x][y] != new_group_nr)
5755     {
5756       int old_group_nr = AmoebaNr[x][y];
5757
5758       if (old_group_nr == 0)
5759         return;
5760
5761       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5762       AmoebaCnt[old_group_nr] = 0;
5763       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5764       AmoebaCnt2[old_group_nr] = 0;
5765
5766       for (yy = 0; yy < lev_fieldy; yy++)
5767       {
5768         for (xx = 0; xx < lev_fieldx; xx++)
5769         {
5770           if (AmoebaNr[xx][yy] == old_group_nr)
5771             AmoebaNr[xx][yy] = new_group_nr;
5772         }
5773       }
5774     }
5775   }
5776 }
5777
5778 void AmoebeUmwandeln(int ax, int ay)
5779 {
5780   int i, x, y;
5781
5782   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5783   {
5784     int group_nr = AmoebaNr[ax][ay];
5785
5786 #ifdef DEBUG
5787     if (group_nr == 0)
5788     {
5789       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5790       printf("AmoebeUmwandeln(): This should never happen!\n");
5791       return;
5792     }
5793 #endif
5794
5795     for (y = 0; y < lev_fieldy; y++)
5796     {
5797       for (x = 0; x < lev_fieldx; x++)
5798       {
5799         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5800         {
5801           AmoebaNr[x][y] = 0;
5802           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5803         }
5804       }
5805     }
5806     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5807                             SND_AMOEBA_TURNING_TO_GEM :
5808                             SND_AMOEBA_TURNING_TO_ROCK));
5809     Bang(ax, ay);
5810   }
5811   else
5812   {
5813     static int xy[4][2] =
5814     {
5815       { 0, -1 },
5816       { -1, 0 },
5817       { +1, 0 },
5818       { 0, +1 }
5819     };
5820
5821     for (i = 0; i < NUM_DIRECTIONS; i++)
5822     {
5823       x = ax + xy[i][0];
5824       y = ay + xy[i][1];
5825
5826       if (!IN_LEV_FIELD(x, y))
5827         continue;
5828
5829       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5830       {
5831         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5832                               SND_AMOEBA_TURNING_TO_GEM :
5833                               SND_AMOEBA_TURNING_TO_ROCK));
5834         Bang(x, y);
5835       }
5836     }
5837   }
5838 }
5839
5840 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5841 {
5842   int x, y;
5843   int group_nr = AmoebaNr[ax][ay];
5844   boolean done = FALSE;
5845
5846 #ifdef DEBUG
5847   if (group_nr == 0)
5848   {
5849     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5850     printf("AmoebeUmwandelnBD(): This should never happen!\n");
5851     return;
5852   }
5853 #endif
5854
5855   for (y = 0; y < lev_fieldy; y++)
5856   {
5857     for (x = 0; x < lev_fieldx; x++)
5858     {
5859       if (AmoebaNr[x][y] == group_nr &&
5860           (Feld[x][y] == EL_AMOEBA_DEAD ||
5861            Feld[x][y] == EL_BD_AMOEBA ||
5862            Feld[x][y] == EL_AMOEBA_GROWING))
5863       {
5864         AmoebaNr[x][y] = 0;
5865         Feld[x][y] = new_element;
5866         InitField(x, y, FALSE);
5867         DrawLevelField(x, y);
5868         done = TRUE;
5869       }
5870     }
5871   }
5872
5873   if (done)
5874     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5875                             SND_BD_AMOEBA_TURNING_TO_ROCK :
5876                             SND_BD_AMOEBA_TURNING_TO_GEM));
5877 }
5878
5879 void AmoebeWaechst(int x, int y)
5880 {
5881   static unsigned long sound_delay = 0;
5882   static unsigned long sound_delay_value = 0;
5883
5884   if (!MovDelay[x][y])          /* start new growing cycle */
5885   {
5886     MovDelay[x][y] = 7;
5887
5888     if (DelayReached(&sound_delay, sound_delay_value))
5889     {
5890 #if 1
5891       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5892 #else
5893       if (Store[x][y] == EL_BD_AMOEBA)
5894         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5895       else
5896         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5897 #endif
5898       sound_delay_value = 30;
5899     }
5900   }
5901
5902   if (MovDelay[x][y])           /* wait some time before growing bigger */
5903   {
5904     MovDelay[x][y]--;
5905     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5906     {
5907       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5908                                            6 - MovDelay[x][y]);
5909
5910       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5911     }
5912
5913     if (!MovDelay[x][y])
5914     {
5915       Feld[x][y] = Store[x][y];
5916       Store[x][y] = 0;
5917       DrawLevelField(x, y);
5918     }
5919   }
5920 }
5921
5922 void AmoebaDisappearing(int x, int y)
5923 {
5924   static unsigned long sound_delay = 0;
5925   static unsigned long sound_delay_value = 0;
5926
5927   if (!MovDelay[x][y])          /* start new shrinking cycle */
5928   {
5929     MovDelay[x][y] = 7;
5930
5931     if (DelayReached(&sound_delay, sound_delay_value))
5932       sound_delay_value = 30;
5933   }
5934
5935   if (MovDelay[x][y])           /* wait some time before shrinking */
5936   {
5937     MovDelay[x][y]--;
5938     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5939     {
5940       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5941                                            6 - MovDelay[x][y]);
5942
5943       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5944     }
5945
5946     if (!MovDelay[x][y])
5947     {
5948       Feld[x][y] = EL_EMPTY;
5949       DrawLevelField(x, y);
5950
5951       /* don't let mole enter this field in this cycle;
5952          (give priority to objects falling to this field from above) */
5953       Stop[x][y] = TRUE;
5954     }
5955   }
5956 }
5957
5958 void AmoebeAbleger(int ax, int ay)
5959 {
5960   int i;
5961   int element = Feld[ax][ay];
5962   int graphic = el2img(element);
5963   int newax = ax, neway = ay;
5964   static int xy[4][2] =
5965   {
5966     { 0, -1 },
5967     { -1, 0 },
5968     { +1, 0 },
5969     { 0, +1 }
5970   };
5971
5972   if (!level.amoeba_speed)
5973   {
5974     Feld[ax][ay] = EL_AMOEBA_DEAD;
5975     DrawLevelField(ax, ay);
5976     return;
5977   }
5978
5979   if (IS_ANIMATED(graphic))
5980     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5981
5982   if (!MovDelay[ax][ay])        /* start making new amoeba field */
5983     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5984
5985   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
5986   {
5987     MovDelay[ax][ay]--;
5988     if (MovDelay[ax][ay])
5989       return;
5990   }
5991
5992   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5993   {
5994     int start = RND(4);
5995     int x = ax + xy[start][0];
5996     int y = ay + xy[start][1];
5997
5998     if (!IN_LEV_FIELD(x, y))
5999       return;
6000
6001     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6002     if (IS_FREE(x, y) ||
6003         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6004     {
6005       newax = x;
6006       neway = y;
6007     }
6008
6009     if (newax == ax && neway == ay)
6010       return;
6011   }
6012   else                          /* normal or "filled" (BD style) amoeba */
6013   {
6014     int start = RND(4);
6015     boolean waiting_for_player = FALSE;
6016
6017     for (i = 0; i < NUM_DIRECTIONS; i++)
6018     {
6019       int j = (start + i) % 4;
6020       int x = ax + xy[j][0];
6021       int y = ay + xy[j][1];
6022
6023       if (!IN_LEV_FIELD(x, y))
6024         continue;
6025
6026       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6027       if (IS_FREE(x, y) ||
6028           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6029       {
6030         newax = x;
6031         neway = y;
6032         break;
6033       }
6034       else if (IS_PLAYER(x, y))
6035         waiting_for_player = TRUE;
6036     }
6037
6038     if (newax == ax && neway == ay)             /* amoeba cannot grow */
6039     {
6040       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6041       {
6042         Feld[ax][ay] = EL_AMOEBA_DEAD;
6043         DrawLevelField(ax, ay);
6044         AmoebaCnt[AmoebaNr[ax][ay]]--;
6045
6046         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
6047         {
6048           if (element == EL_AMOEBA_FULL)
6049             AmoebeUmwandeln(ax, ay);
6050           else if (element == EL_BD_AMOEBA)
6051             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6052         }
6053       }
6054       return;
6055     }
6056     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6057     {
6058       /* amoeba gets larger by growing in some direction */
6059
6060       int new_group_nr = AmoebaNr[ax][ay];
6061
6062 #ifdef DEBUG
6063   if (new_group_nr == 0)
6064   {
6065     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6066     printf("AmoebeAbleger(): This should never happen!\n");
6067     return;
6068   }
6069 #endif
6070
6071       AmoebaNr[newax][neway] = new_group_nr;
6072       AmoebaCnt[new_group_nr]++;
6073       AmoebaCnt2[new_group_nr]++;
6074
6075       /* if amoeba touches other amoeba(s) after growing, unify them */
6076       AmoebenVereinigen(newax, neway);
6077
6078       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6079       {
6080         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6081         return;
6082       }
6083     }
6084   }
6085
6086   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6087       (neway == lev_fieldy - 1 && newax != ax))
6088   {
6089     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6090     Store[newax][neway] = element;
6091   }
6092   else if (neway == ay)
6093   {
6094     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6095 #if 1
6096     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6097 #else
6098     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6099 #endif
6100   }
6101   else
6102   {
6103     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6104     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6105     Store[ax][ay] = EL_AMOEBA_DROP;
6106     ContinueMoving(ax, ay);
6107     return;
6108   }
6109
6110   DrawLevelField(newax, neway);
6111 }
6112
6113 void Life(int ax, int ay)
6114 {
6115   int x1, y1, x2, y2;
6116   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6117   int life_time = 40;
6118   int element = Feld[ax][ay];
6119   int graphic = el2img(element);
6120   boolean changed = FALSE;
6121
6122   if (IS_ANIMATED(graphic))
6123     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6124
6125   if (Stop[ax][ay])
6126     return;
6127
6128   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6129     MovDelay[ax][ay] = life_time;
6130
6131   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6132   {
6133     MovDelay[ax][ay]--;
6134     if (MovDelay[ax][ay])
6135       return;
6136   }
6137
6138   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6139   {
6140     int xx = ax+x1, yy = ay+y1;
6141     int nachbarn = 0;
6142
6143     if (!IN_LEV_FIELD(xx, yy))
6144       continue;
6145
6146     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6147     {
6148       int x = xx+x2, y = yy+y2;
6149
6150       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6151         continue;
6152
6153       if (((Feld[x][y] == element ||
6154             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6155            !Stop[x][y]) ||
6156           (IS_FREE(x, y) && Stop[x][y]))
6157         nachbarn++;
6158     }
6159
6160     if (xx == ax && yy == ay)           /* field in the middle */
6161     {
6162       if (nachbarn < life[0] || nachbarn > life[1])
6163       {
6164         Feld[xx][yy] = EL_EMPTY;
6165         if (!Stop[xx][yy])
6166           DrawLevelField(xx, yy);
6167         Stop[xx][yy] = TRUE;
6168         changed = TRUE;
6169       }
6170     }
6171     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6172     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6173     {                                   /* free border field */
6174       if (nachbarn >= life[2] && nachbarn <= life[3])
6175       {
6176         Feld[xx][yy] = element;
6177         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6178         if (!Stop[xx][yy])
6179           DrawLevelField(xx, yy);
6180         Stop[xx][yy] = TRUE;
6181         changed = TRUE;
6182       }
6183     }
6184   }
6185
6186   if (changed)
6187     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6188                    SND_GAME_OF_LIFE_GROWING);
6189 }
6190
6191 static void InitRobotWheel(int x, int y)
6192 {
6193   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6194 }
6195
6196 static void RunRobotWheel(int x, int y)
6197 {
6198   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6199 }
6200
6201 static void StopRobotWheel(int x, int y)
6202 {
6203   if (ZX == x && ZY == y)
6204     ZX = ZY = -1;
6205 }
6206
6207 static void InitTimegateWheel(int x, int y)
6208 {
6209   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6210 }
6211
6212 static void RunTimegateWheel(int x, int y)
6213 {
6214   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6215 }
6216
6217 void CheckExit(int x, int y)
6218 {
6219   if (local_player->gems_still_needed > 0 ||
6220       local_player->sokobanfields_still_needed > 0 ||
6221       local_player->lights_still_needed > 0)
6222   {
6223     int element = Feld[x][y];
6224     int graphic = el2img(element);
6225
6226     if (IS_ANIMATED(graphic))
6227       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6228
6229     return;
6230   }
6231
6232   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6233     return;
6234
6235   Feld[x][y] = EL_EXIT_OPENING;
6236
6237   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6238 }
6239
6240 void CheckExitSP(int x, int y)
6241 {
6242   if (local_player->gems_still_needed > 0)
6243   {
6244     int element = Feld[x][y];
6245     int graphic = el2img(element);
6246
6247     if (IS_ANIMATED(graphic))
6248       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6249
6250     return;
6251   }
6252
6253   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6254     return;
6255
6256   Feld[x][y] = EL_SP_EXIT_OPENING;
6257
6258   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6259 }
6260
6261 static void CloseAllOpenTimegates()
6262 {
6263   int x, y;
6264
6265   for (y = 0; y < lev_fieldy; y++)
6266   {
6267     for (x = 0; x < lev_fieldx; x++)
6268     {
6269       int element = Feld[x][y];
6270
6271       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6272       {
6273         Feld[x][y] = EL_TIMEGATE_CLOSING;
6274 #if 1
6275         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6276 #else
6277         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6278 #endif
6279       }
6280     }
6281   }
6282 }
6283
6284 void EdelsteinFunkeln(int x, int y)
6285 {
6286   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6287     return;
6288
6289   if (Feld[x][y] == EL_BD_DIAMOND)
6290     return;
6291
6292   if (MovDelay[x][y] == 0)      /* next animation frame */
6293     MovDelay[x][y] = 11 * !SimpleRND(500);
6294
6295   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6296   {
6297     MovDelay[x][y]--;
6298
6299     if (setup.direct_draw && MovDelay[x][y])
6300       SetDrawtoField(DRAW_BUFFERED);
6301
6302     DrawLevelElementAnimation(x, y, Feld[x][y]);
6303
6304     if (MovDelay[x][y] != 0)
6305     {
6306       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6307                                            10 - MovDelay[x][y]);
6308
6309       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6310
6311       if (setup.direct_draw)
6312       {
6313         int dest_x, dest_y;
6314
6315         dest_x = FX + SCREENX(x) * TILEX;
6316         dest_y = FY + SCREENY(y) * TILEY;
6317
6318         BlitBitmap(drawto_field, window,
6319                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6320         SetDrawtoField(DRAW_DIRECT);
6321       }
6322     }
6323   }
6324 }
6325
6326 void MauerWaechst(int x, int y)
6327 {
6328   int delay = 6;
6329
6330   if (!MovDelay[x][y])          /* next animation frame */
6331     MovDelay[x][y] = 3 * delay;
6332
6333   if (MovDelay[x][y])           /* wait some time before next frame */
6334   {
6335     MovDelay[x][y]--;
6336
6337     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6338     {
6339       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6340       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6341
6342       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6343     }
6344
6345     if (!MovDelay[x][y])
6346     {
6347       if (MovDir[x][y] == MV_LEFT)
6348       {
6349         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6350           DrawLevelField(x - 1, y);
6351       }
6352       else if (MovDir[x][y] == MV_RIGHT)
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_UP)
6358       {
6359         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6360           DrawLevelField(x, y - 1);
6361       }
6362       else
6363       {
6364         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6365           DrawLevelField(x, y + 1);
6366       }
6367
6368       Feld[x][y] = Store[x][y];
6369       Store[x][y] = 0;
6370       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6371       DrawLevelField(x, y);
6372     }
6373   }
6374 }
6375
6376 void MauerAbleger(int ax, int ay)
6377 {
6378   int element = Feld[ax][ay];
6379   int graphic = el2img(element);
6380   boolean oben_frei = FALSE, unten_frei = FALSE;
6381   boolean links_frei = FALSE, rechts_frei = FALSE;
6382   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6383   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6384   boolean new_wall = FALSE;
6385
6386   if (IS_ANIMATED(graphic))
6387     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6388
6389   if (!MovDelay[ax][ay])        /* start building new wall */
6390     MovDelay[ax][ay] = 6;
6391
6392   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6393   {
6394     MovDelay[ax][ay]--;
6395     if (MovDelay[ax][ay])
6396       return;
6397   }
6398
6399   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6400     oben_frei = TRUE;
6401   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6402     unten_frei = TRUE;
6403   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6404     links_frei = TRUE;
6405   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6406     rechts_frei = TRUE;
6407
6408   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6409       element == EL_EXPANDABLE_WALL_ANY)
6410   {
6411     if (oben_frei)
6412     {
6413       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6414       Store[ax][ay-1] = element;
6415       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6416       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6417         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6418                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6419       new_wall = TRUE;
6420     }
6421     if (unten_frei)
6422     {
6423       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6424       Store[ax][ay+1] = element;
6425       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6426       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6427         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6428                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6429       new_wall = TRUE;
6430     }
6431   }
6432
6433   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6434       element == EL_EXPANDABLE_WALL_ANY ||
6435       element == EL_EXPANDABLE_WALL)
6436   {
6437     if (links_frei)
6438     {
6439       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6440       Store[ax-1][ay] = element;
6441       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6442       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6443         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6444                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6445       new_wall = TRUE;
6446     }
6447
6448     if (rechts_frei)
6449     {
6450       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6451       Store[ax+1][ay] = element;
6452       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6453       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6454         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6455                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6456       new_wall = TRUE;
6457     }
6458   }
6459
6460   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6461     DrawLevelField(ax, ay);
6462
6463   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6464     oben_massiv = TRUE;
6465   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6466     unten_massiv = TRUE;
6467   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6468     links_massiv = TRUE;
6469   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6470     rechts_massiv = TRUE;
6471
6472   if (((oben_massiv && unten_massiv) ||
6473        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6474        element == EL_EXPANDABLE_WALL) &&
6475       ((links_massiv && rechts_massiv) ||
6476        element == EL_EXPANDABLE_WALL_VERTICAL))
6477     Feld[ax][ay] = EL_WALL;
6478
6479   if (new_wall)
6480 #if 1
6481     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6482 #else
6483     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6484 #endif
6485 }
6486
6487 void CheckForDragon(int x, int y)
6488 {
6489   int i, j;
6490   boolean dragon_found = FALSE;
6491   static int xy[4][2] =
6492   {
6493     { 0, -1 },
6494     { -1, 0 },
6495     { +1, 0 },
6496     { 0, +1 }
6497   };
6498
6499   for (i = 0; i < NUM_DIRECTIONS; i++)
6500   {
6501     for (j = 0; j < 4; j++)
6502     {
6503       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6504
6505       if (IN_LEV_FIELD(xx, yy) &&
6506           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6507       {
6508         if (Feld[xx][yy] == EL_DRAGON)
6509           dragon_found = TRUE;
6510       }
6511       else
6512         break;
6513     }
6514   }
6515
6516   if (!dragon_found)
6517   {
6518     for (i = 0; i < NUM_DIRECTIONS; i++)
6519     {
6520       for (j = 0; j < 3; j++)
6521       {
6522         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6523   
6524         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6525         {
6526           Feld[xx][yy] = EL_EMPTY;
6527           DrawLevelField(xx, yy);
6528         }
6529         else
6530           break;
6531       }
6532     }
6533   }
6534 }
6535
6536 static void InitBuggyBase(int x, int y)
6537 {
6538   int element = Feld[x][y];
6539   int activating_delay = FRAMES_PER_SECOND / 4;
6540
6541   ChangeDelay[x][y] =
6542     (element == EL_SP_BUGGY_BASE ?
6543      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6544      element == EL_SP_BUGGY_BASE_ACTIVATING ?
6545      activating_delay :
6546      element == EL_SP_BUGGY_BASE_ACTIVE ?
6547      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6548 }
6549
6550 static void WarnBuggyBase(int x, int y)
6551 {
6552   int i;
6553   static int xy[4][2] =
6554   {
6555     { 0, -1 },
6556     { -1, 0 },
6557     { +1, 0 },
6558     { 0, +1 }
6559   };
6560
6561   for (i = 0; i < NUM_DIRECTIONS; i++)
6562   {
6563     int xx = x + xy[i][0], yy = y + xy[i][1];
6564
6565     if (IS_PLAYER(xx, yy))
6566     {
6567       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6568
6569       break;
6570     }
6571   }
6572 }
6573
6574 static void InitTrap(int x, int y)
6575 {
6576   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6577 }
6578
6579 static void ActivateTrap(int x, int y)
6580 {
6581   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6582 }
6583
6584 static void ChangeActiveTrap(int x, int y)
6585 {
6586   int graphic = IMG_TRAP_ACTIVE;
6587
6588   /* if new animation frame was drawn, correct crumbled sand border */
6589   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6590     DrawLevelFieldCrumbledSand(x, y);
6591 }
6592
6593 static void ChangeElementNowExt(int x, int y, int target_element)
6594 {
6595   int previous_move_direction = MovDir[x][y];
6596
6597   /* check if element under player changes from accessible to unaccessible
6598      (needed for special case of dropping element which then changes) */
6599   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6600       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6601   {
6602     Bang(x, y);
6603     return;
6604   }
6605
6606   RemoveField(x, y);
6607   Feld[x][y] = target_element;
6608
6609   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6610
6611   ResetGfxAnimation(x, y);
6612   ResetRandomAnimationValue(x, y);
6613
6614   if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6615     MovDir[x][y] = previous_move_direction;
6616
6617 #if 1
6618   InitField_WithBug1(x, y, FALSE);
6619 #else
6620   InitField(x, y, FALSE);
6621   if (CAN_MOVE(Feld[x][y]))
6622     InitMovDir(x, y);
6623 #endif
6624
6625   DrawLevelField(x, y);
6626
6627   if (GFX_CRUMBLED(Feld[x][y]))
6628     DrawLevelFieldCrumbledSandNeighbours(x, y);
6629
6630   TestIfBadThingTouchesHero(x, y);
6631   TestIfPlayerTouchesCustomElement(x, y);
6632   TestIfElementTouchesCustomElement(x, y);
6633
6634   if (ELEM_IS_PLAYER(target_element))
6635     RelocatePlayer(x, y, target_element);
6636 }
6637
6638 static boolean ChangeElementNow(int x, int y, int element, int page)
6639 {
6640   struct ElementChangeInfo *change = &element_info[element].change_page[page];
6641
6642   /* always use default change event to prevent running into a loop */
6643   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6644     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6645
6646   /* do not change already changed elements with same change event */
6647 #if 0
6648   if (Changed[x][y] & ChangeEvent[x][y])
6649     return FALSE;
6650 #else
6651   if (Changed[x][y])
6652     return FALSE;
6653 #endif
6654
6655   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6656
6657   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6658
6659   if (change->explode)
6660   {
6661     Bang(x, y);
6662
6663     return TRUE;
6664   }
6665
6666   if (change->use_content)
6667   {
6668     boolean complete_change = TRUE;
6669     boolean can_change[3][3];
6670     int xx, yy;
6671
6672     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6673     {
6674       boolean half_destructible;
6675       int ex = x + xx - 1;
6676       int ey = y + yy - 1;
6677       int e;
6678
6679       can_change[xx][yy] = TRUE;
6680
6681       if (ex == x && ey == y)   /* do not check changing element itself */
6682         continue;
6683
6684       if (change->content[xx][yy] == EL_EMPTY_SPACE)
6685       {
6686         can_change[xx][yy] = FALSE;     /* do not change empty borders */
6687
6688         continue;
6689       }
6690
6691       if (!IN_LEV_FIELD(ex, ey))
6692       {
6693         can_change[xx][yy] = FALSE;
6694         complete_change = FALSE;
6695
6696         continue;
6697       }
6698
6699       e = Feld[ex][ey];
6700
6701       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6702         e = MovingOrBlocked2Element(ex, ey);
6703
6704       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6705
6706       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
6707           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6708           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6709       {
6710         can_change[xx][yy] = FALSE;
6711         complete_change = FALSE;
6712       }
6713     }
6714
6715     if (!change->only_complete || complete_change)
6716     {
6717       boolean something_has_changed = FALSE;
6718
6719       if (change->only_complete && change->use_random_change &&
6720           RND(100) < change->random)
6721         return FALSE;
6722
6723       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6724       {
6725         int ex = x + xx - 1;
6726         int ey = y + yy - 1;
6727
6728         if (can_change[xx][yy] && (!change->use_random_change ||
6729                                    RND(100) < change->random))
6730         {
6731           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6732             RemoveMovingField(ex, ey);
6733
6734           ChangeEvent[ex][ey] = ChangeEvent[x][y];
6735
6736           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6737
6738           something_has_changed = TRUE;
6739
6740           /* for symmetry reasons, freeze newly created border elements */
6741           if (ex != x || ey != y)
6742             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
6743         }
6744       }
6745
6746       if (something_has_changed)
6747         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6748     }
6749   }
6750   else
6751   {
6752     ChangeElementNowExt(x, y, change->target_element);
6753
6754     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6755   }
6756
6757   return TRUE;
6758 }
6759
6760 static void ChangeElement(int x, int y, int page)
6761 {
6762   int element = MovingOrBlocked2Element(x, y);
6763   struct ElementInfo *ei = &element_info[element];
6764   struct ElementChangeInfo *change = &ei->change_page[page];
6765
6766 #if 0
6767 #ifdef DEBUG
6768   if (!CAN_CHANGE(element))
6769   {
6770     printf("\n\n");
6771     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6772            x, y, element, element_info[element].token_name);
6773     printf("ChangeElement(): This should never happen!\n");
6774     printf("\n\n");
6775   }
6776 #endif
6777 #endif
6778
6779   if (ChangeDelay[x][y] == 0)           /* initialize element change */
6780   {
6781     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
6782                          RND(change->delay_random * change->delay_frames)) + 1;
6783
6784     ResetGfxAnimation(x, y);
6785     ResetRandomAnimationValue(x, y);
6786
6787     if (change->pre_change_function)
6788       change->pre_change_function(x, y);
6789   }
6790
6791   ChangeDelay[x][y]--;
6792
6793   if (ChangeDelay[x][y] != 0)           /* continue element change */
6794   {
6795     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6796
6797     if (IS_ANIMATED(graphic))
6798       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6799
6800     if (change->change_function)
6801       change->change_function(x, y);
6802   }
6803   else                                  /* finish element change */
6804   {
6805     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
6806     {
6807       page = ChangePage[x][y];
6808       ChangePage[x][y] = -1;
6809     }
6810
6811 #if 0
6812     if (IS_MOVING(x, y) && !change->explode)
6813 #else
6814     if (IS_MOVING(x, y))                /* never change a running system ;-) */
6815 #endif
6816     {
6817       ChangeDelay[x][y] = 1;            /* try change after next move step */
6818       ChangePage[x][y] = page;          /* remember page to use for change */
6819
6820       return;
6821     }
6822
6823     if (ChangeElementNow(x, y, element, page))
6824     {
6825       if (change->post_change_function)
6826         change->post_change_function(x, y);
6827     }
6828   }
6829 }
6830
6831 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6832                                                int trigger_element,
6833                                                int trigger_side,
6834                                                int trigger_event)
6835 {
6836   int i, j, x, y;
6837
6838   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6839     return FALSE;
6840
6841   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6842   {
6843     int element = EL_CUSTOM_START + i;
6844
6845     boolean change_element = FALSE;
6846     int page = 0;
6847
6848     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6849       continue;
6850
6851     for (j = 0; j < element_info[element].num_change_pages; j++)
6852     {
6853       struct ElementChangeInfo *change = &element_info[element].change_page[j];
6854
6855       if (change->can_change &&
6856 #if 1
6857           change->events & CH_EVENT_BIT(trigger_event) &&
6858 #endif
6859           change->sides & trigger_side &&
6860 #if 1
6861           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6862 #else
6863           change->trigger_element == trigger_element
6864 #endif
6865           )
6866       {
6867 #if 0
6868         if (!(change->events & CH_EVENT_BIT(trigger_event)))
6869           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6870                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6871 #endif
6872
6873         change_element = TRUE;
6874         page = j;
6875
6876         break;
6877       }
6878     }
6879
6880     if (!change_element)
6881       continue;
6882
6883     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6884     {
6885 #if 0
6886       if (x == lx && y == ly)   /* do not change trigger element itself */
6887         continue;
6888 #endif
6889
6890       if (Feld[x][y] == element)
6891       {
6892         ChangeDelay[x][y] = 1;
6893         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6894         ChangeElement(x, y, page);
6895       }
6896     }
6897   }
6898
6899   return TRUE;
6900 }
6901
6902 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6903                                            int trigger_event)
6904 {
6905   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6906                                          trigger_event);
6907 }
6908
6909 static boolean CheckElementSideChange(int x, int y, int element, int side,
6910                                       int trigger_event, int page)
6911 {
6912   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6913     return FALSE;
6914
6915   if (Feld[x][y] == EL_BLOCKED)
6916   {
6917     Blocked2Moving(x, y, &x, &y);
6918     element = Feld[x][y];
6919   }
6920
6921 #if 1
6922   if (page < 0)
6923   {
6924     boolean change_element = FALSE;
6925     int i;
6926
6927     for (i = 0; i < element_info[element].num_change_pages; i++)
6928     {
6929       struct ElementChangeInfo *change = &element_info[element].change_page[i];
6930
6931       if (change->can_change &&
6932           change->events & CH_EVENT_BIT(trigger_event) &&
6933           change->sides & side)
6934       {
6935         change_element = TRUE;
6936         page = i;
6937
6938         break;
6939       }
6940     }
6941
6942     if (!change_element)
6943       return FALSE;
6944   }
6945
6946 #else
6947
6948   /* !!! this check misses pages with same event, but different side !!! */
6949
6950   if (page < 0)
6951     page = element_info[element].event_page_nr[trigger_event];
6952
6953   if (!(element_info[element].change_page[page].sides & side))
6954     return FALSE;
6955 #endif
6956
6957   ChangeDelay[x][y] = 1;
6958   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6959   ChangeElement(x, y, page);
6960
6961   return TRUE;
6962 }
6963
6964 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6965 {
6966   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6967 }
6968
6969 static void PlayPlayerSound(struct PlayerInfo *player)
6970 {
6971   int jx = player->jx, jy = player->jy;
6972   int element = player->element_nr;
6973   int last_action = player->last_action_waiting;
6974   int action = player->action_waiting;
6975
6976   if (player->is_waiting)
6977   {
6978     if (action != last_action)
6979       PlayLevelSoundElementAction(jx, jy, element, action);
6980     else
6981       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6982   }
6983   else
6984   {
6985     if (action != last_action)
6986       StopSound(element_info[element].sound[last_action]);
6987
6988     if (last_action == ACTION_SLEEPING)
6989       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6990   }
6991 }
6992
6993 static void PlayAllPlayersSound()
6994 {
6995   int i;
6996
6997   for (i = 0; i < MAX_PLAYERS; i++)
6998     if (stored_player[i].active)
6999       PlayPlayerSound(&stored_player[i]);
7000 }
7001
7002 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7003 {
7004   boolean last_waiting = player->is_waiting;
7005   int move_dir = player->MovDir;
7006
7007   player->last_action_waiting = player->action_waiting;
7008
7009   if (is_waiting)
7010   {
7011     if (!last_waiting)          /* not waiting -> waiting */
7012     {
7013       player->is_waiting = TRUE;
7014
7015       player->frame_counter_bored =
7016         FrameCounter +
7017         game.player_boring_delay_fixed +
7018         SimpleRND(game.player_boring_delay_random);
7019       player->frame_counter_sleeping =
7020         FrameCounter +
7021         game.player_sleeping_delay_fixed +
7022         SimpleRND(game.player_sleeping_delay_random);
7023
7024       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7025     }
7026
7027     if (game.player_sleeping_delay_fixed +
7028         game.player_sleeping_delay_random > 0 &&
7029         player->anim_delay_counter == 0 &&
7030         player->post_delay_counter == 0 &&
7031         FrameCounter >= player->frame_counter_sleeping)
7032       player->is_sleeping = TRUE;
7033     else if (game.player_boring_delay_fixed +
7034              game.player_boring_delay_random > 0 &&
7035              FrameCounter >= player->frame_counter_bored)
7036       player->is_bored = TRUE;
7037
7038     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7039                               player->is_bored ? ACTION_BORING :
7040                               ACTION_WAITING);
7041
7042     if (player->is_sleeping)
7043     {
7044       if (player->num_special_action_sleeping > 0)
7045       {
7046         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7047         {
7048           int last_special_action = player->special_action_sleeping;
7049           int num_special_action = player->num_special_action_sleeping;
7050           int special_action =
7051             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7052              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7053              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7054              last_special_action + 1 : ACTION_SLEEPING);
7055           int special_graphic =
7056             el_act_dir2img(player->element_nr, special_action, move_dir);
7057
7058           player->anim_delay_counter =
7059             graphic_info[special_graphic].anim_delay_fixed +
7060             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7061           player->post_delay_counter =
7062             graphic_info[special_graphic].post_delay_fixed +
7063             SimpleRND(graphic_info[special_graphic].post_delay_random);
7064
7065           player->special_action_sleeping = special_action;
7066         }
7067
7068         if (player->anim_delay_counter > 0)
7069         {
7070           player->action_waiting = player->special_action_sleeping;
7071           player->anim_delay_counter--;
7072         }
7073         else if (player->post_delay_counter > 0)
7074         {
7075           player->post_delay_counter--;
7076         }
7077       }
7078     }
7079     else if (player->is_bored)
7080     {
7081       if (player->num_special_action_bored > 0)
7082       {
7083         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7084         {
7085           int special_action =
7086             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7087           int special_graphic =
7088             el_act_dir2img(player->element_nr, special_action, move_dir);
7089
7090           player->anim_delay_counter =
7091             graphic_info[special_graphic].anim_delay_fixed +
7092             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7093           player->post_delay_counter =
7094             graphic_info[special_graphic].post_delay_fixed +
7095             SimpleRND(graphic_info[special_graphic].post_delay_random);
7096
7097           player->special_action_bored = special_action;
7098         }
7099
7100         if (player->anim_delay_counter > 0)
7101         {
7102           player->action_waiting = player->special_action_bored;
7103           player->anim_delay_counter--;
7104         }
7105         else if (player->post_delay_counter > 0)
7106         {
7107           player->post_delay_counter--;
7108         }
7109       }
7110     }
7111   }
7112   else if (last_waiting)        /* waiting -> not waiting */
7113   {
7114     player->is_waiting = FALSE;
7115     player->is_bored = FALSE;
7116     player->is_sleeping = FALSE;
7117
7118     player->frame_counter_bored = -1;
7119     player->frame_counter_sleeping = -1;
7120
7121     player->anim_delay_counter = 0;
7122     player->post_delay_counter = 0;
7123
7124     player->action_waiting = ACTION_DEFAULT;
7125
7126     player->special_action_bored = ACTION_DEFAULT;
7127     player->special_action_sleeping = ACTION_DEFAULT;
7128   }
7129 }
7130
7131 #if 1
7132 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7133 {
7134 #if 0
7135   static byte stored_player_action[MAX_PLAYERS];
7136   static int num_stored_actions = 0;
7137 #endif
7138   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7139   int left      = player_action & JOY_LEFT;
7140   int right     = player_action & JOY_RIGHT;
7141   int up        = player_action & JOY_UP;
7142   int down      = player_action & JOY_DOWN;
7143   int button1   = player_action & JOY_BUTTON_1;
7144   int button2   = player_action & JOY_BUTTON_2;
7145   int dx        = (left ? -1    : right ? 1     : 0);
7146   int dy        = (up   ? -1    : down  ? 1     : 0);
7147
7148 #if 0
7149   stored_player_action[player->index_nr] = 0;
7150   num_stored_actions++;
7151 #endif
7152
7153 #if 0
7154   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7155 #endif
7156
7157   if (!player->active || tape.pausing)
7158     return 0;
7159
7160 #if 0
7161   printf("::: [%d %d %d %d] [%d %d]\n",
7162          left, right, up, down, button1, button2);
7163 #endif
7164
7165   if (player_action)
7166   {
7167 #if 0
7168     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7169 #endif
7170
7171     if (button1)
7172       snapped = SnapField(player, dx, dy);
7173     else
7174     {
7175       if (button2)
7176         dropped = DropElement(player);
7177
7178       moved = MovePlayer(player, dx, dy);
7179     }
7180
7181     if (tape.single_step && tape.recording && !tape.pausing)
7182     {
7183       if (button1 || (dropped && !moved))
7184       {
7185         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7186         SnapField(player, 0, 0);                /* stop snapping */
7187       }
7188     }
7189
7190     SetPlayerWaiting(player, FALSE);
7191
7192 #if 1
7193     return player_action;
7194 #else
7195     stored_player_action[player->index_nr] = player_action;
7196 #endif
7197   }
7198   else
7199   {
7200 #if 0
7201     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7202 #endif
7203
7204     /* no actions for this player (no input at player's configured device) */
7205
7206     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7207     SnapField(player, 0, 0);
7208     CheckGravityMovement(player);
7209
7210     if (player->MovPos == 0)
7211       SetPlayerWaiting(player, TRUE);
7212
7213     if (player->MovPos == 0)    /* needed for tape.playing */
7214       player->is_moving = FALSE;
7215
7216     player->is_dropping = FALSE;
7217
7218     return 0;
7219   }
7220
7221 #if 0
7222   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7223   {
7224     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7225
7226     TapeRecordAction(stored_player_action);
7227     num_stored_actions = 0;
7228   }
7229 #endif
7230 }
7231
7232 #else
7233
7234 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7235 {
7236   static byte stored_player_action[MAX_PLAYERS];
7237   static int num_stored_actions = 0;
7238   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7239   int left      = player_action & JOY_LEFT;
7240   int right     = player_action & JOY_RIGHT;
7241   int up        = player_action & JOY_UP;
7242   int down      = player_action & JOY_DOWN;
7243   int button1   = player_action & JOY_BUTTON_1;
7244   int button2   = player_action & JOY_BUTTON_2;
7245   int dx        = (left ? -1    : right ? 1     : 0);
7246   int dy        = (up   ? -1    : down  ? 1     : 0);
7247
7248   stored_player_action[player->index_nr] = 0;
7249   num_stored_actions++;
7250
7251   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7252
7253   if (!player->active || tape.pausing)
7254     return;
7255
7256   if (player_action)
7257   {
7258     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7259
7260     if (button1)
7261       snapped = SnapField(player, dx, dy);
7262     else
7263     {
7264       if (button2)
7265         dropped = DropElement(player);
7266
7267       moved = MovePlayer(player, dx, dy);
7268     }
7269
7270     if (tape.single_step && tape.recording && !tape.pausing)
7271     {
7272       if (button1 || (dropped && !moved))
7273       {
7274         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7275         SnapField(player, 0, 0);                /* stop snapping */
7276       }
7277     }
7278
7279     stored_player_action[player->index_nr] = player_action;
7280   }
7281   else
7282   {
7283     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7284
7285     /* no actions for this player (no input at player's configured device) */
7286
7287     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7288     SnapField(player, 0, 0);
7289     CheckGravityMovement(player);
7290
7291     if (player->MovPos == 0)
7292       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7293
7294     if (player->MovPos == 0)    /* needed for tape.playing */
7295       player->is_moving = FALSE;
7296   }
7297
7298   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7299   {
7300     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7301
7302     TapeRecordAction(stored_player_action);
7303     num_stored_actions = 0;
7304   }
7305 }
7306 #endif
7307
7308 void GameActions()
7309 {
7310   static unsigned long action_delay = 0;
7311   unsigned long action_delay_value;
7312   int magic_wall_x = 0, magic_wall_y = 0;
7313   int i, x, y, element, graphic;
7314   byte *recorded_player_action;
7315   byte summarized_player_action = 0;
7316 #if 1
7317   byte tape_action[MAX_PLAYERS];
7318 #endif
7319
7320   if (game_status != GAME_MODE_PLAYING)
7321     return;
7322
7323   action_delay_value =
7324     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7325
7326   if (tape.playing && tape.index_search && !tape.pausing)
7327     action_delay_value = 0;
7328
7329   /* ---------- main game synchronization point ---------- */
7330
7331   WaitUntilDelayReached(&action_delay, action_delay_value);
7332
7333   if (network_playing && !network_player_action_received)
7334   {
7335     /*
7336 #ifdef DEBUG
7337     printf("DEBUG: try to get network player actions in time\n");
7338 #endif
7339     */
7340
7341 #if defined(PLATFORM_UNIX)
7342     /* last chance to get network player actions without main loop delay */
7343     HandleNetworking();
7344 #endif
7345
7346     if (game_status != GAME_MODE_PLAYING)
7347       return;
7348
7349     if (!network_player_action_received)
7350     {
7351       /*
7352 #ifdef DEBUG
7353       printf("DEBUG: failed to get network player actions in time\n");
7354 #endif
7355       */
7356       return;
7357     }
7358   }
7359
7360   if (tape.pausing)
7361     return;
7362
7363 #if 0
7364   printf("::: getting new tape action [%d]\n", FrameCounter);
7365 #endif
7366
7367   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7368
7369   for (i = 0; i < MAX_PLAYERS; i++)
7370   {
7371     summarized_player_action |= stored_player[i].action;
7372
7373     if (!network_playing)
7374       stored_player[i].effective_action = stored_player[i].action;
7375   }
7376
7377 #if defined(PLATFORM_UNIX)
7378   if (network_playing)
7379     SendToServer_MovePlayer(summarized_player_action);
7380 #endif
7381
7382   if (!options.network && !setup.team_mode)
7383     local_player->effective_action = summarized_player_action;
7384
7385   for (i = 0; i < MAX_PLAYERS; i++)
7386   {
7387     int actual_player_action = stored_player[i].effective_action;
7388
7389     if (stored_player[i].programmed_action)
7390       actual_player_action = stored_player[i].programmed_action;
7391
7392     if (recorded_player_action)
7393       actual_player_action = recorded_player_action[i];
7394
7395     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7396
7397     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7398       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7399
7400     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7401   }
7402
7403 #if 1
7404   if (tape.recording)
7405     TapeRecordAction(tape_action);
7406 #endif
7407
7408   network_player_action_received = FALSE;
7409
7410   ScrollScreen(NULL, SCROLL_GO_ON);
7411
7412 #if 0
7413   FrameCounter++;
7414   TimeFrames++;
7415
7416   for (i = 0; i < MAX_PLAYERS; i++)
7417     stored_player[i].Frame++;
7418 #endif
7419
7420 #if 1
7421   /* for downwards compatibility, the following code emulates a fixed bug that
7422      occured when pushing elements (causing elements that just made their last
7423      pushing step to already (if possible) make their first falling step in the
7424      same game frame, which is bad); this code is also needed to use the famous
7425      "spring push bug" which is used in older levels and might be wanted to be
7426      used also in newer levels, but in this case the buggy pushing code is only
7427      affecting the "spring" element and no other elements */
7428
7429 #if 1
7430   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7431 #else
7432   if (game.engine_version < VERSION_IDENT(2,2,0,7))
7433 #endif
7434   {
7435     for (i = 0; i < MAX_PLAYERS; i++)
7436     {
7437       struct PlayerInfo *player = &stored_player[i];
7438       int x = player->jx;
7439       int y = player->jy;
7440
7441 #if 1
7442       if (player->active && player->is_pushing && player->is_moving &&
7443           IS_MOVING(x, y) &&
7444           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7445            Feld[x][y] == EL_SPRING))
7446 #else
7447       if (player->active && player->is_pushing && player->is_moving &&
7448           IS_MOVING(x, y))
7449 #endif
7450       {
7451         ContinueMoving(x, y);
7452
7453         /* continue moving after pushing (this is actually a bug) */
7454         if (!IS_MOVING(x, y))
7455         {
7456           Stop[x][y] = FALSE;
7457         }
7458       }
7459     }
7460   }
7461 #endif
7462
7463   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7464   {
7465     Changed[x][y] = CE_BITMASK_DEFAULT;
7466     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7467
7468 #if DEBUG
7469     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7470     {
7471       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7472       printf("GameActions(): This should never happen!\n");
7473
7474       ChangePage[x][y] = -1;
7475     }
7476 #endif
7477
7478     Stop[x][y] = FALSE;
7479     if (WasJustMoving[x][y] > 0)
7480       WasJustMoving[x][y]--;
7481     if (WasJustFalling[x][y] > 0)
7482       WasJustFalling[x][y]--;
7483
7484     GfxFrame[x][y]++;
7485
7486 #if 1
7487     /* reset finished pushing action (not done in ContinueMoving() to allow
7488        continous pushing animation for elements with zero push delay) */
7489     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7490     {
7491       ResetGfxAnimation(x, y);
7492       DrawLevelField(x, y);
7493     }
7494 #endif
7495
7496 #if DEBUG
7497     if (IS_BLOCKED(x, y))
7498     {
7499       int oldx, oldy;
7500
7501       Blocked2Moving(x, y, &oldx, &oldy);
7502       if (!IS_MOVING(oldx, oldy))
7503       {
7504         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7505         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7506         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7507         printf("GameActions(): This should never happen!\n");
7508       }
7509     }
7510 #endif
7511   }
7512
7513   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7514   {
7515     element = Feld[x][y];
7516 #if 1
7517     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7518 #else
7519     graphic = el2img(element);
7520 #endif
7521
7522 #if 0
7523     if (element == -1)
7524     {
7525       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7526
7527       element = graphic = 0;
7528     }
7529 #endif
7530
7531     if (graphic_info[graphic].anim_global_sync)
7532       GfxFrame[x][y] = FrameCounter;
7533
7534     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7535         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7536       ResetRandomAnimationValue(x, y);
7537
7538     SetRandomAnimationValue(x, y);
7539
7540 #if 1
7541     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7542 #endif
7543
7544     if (IS_INACTIVE(element))
7545     {
7546       if (IS_ANIMATED(graphic))
7547         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7548
7549       continue;
7550     }
7551
7552 #if 1
7553     /* this may take place after moving, so 'element' may have changed */
7554 #if 0
7555     if (IS_CHANGING(x, y))
7556 #else
7557     if (IS_CHANGING(x, y) &&
7558         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7559 #endif
7560     {
7561 #if 0
7562       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7563                     element_info[element].event_page_nr[CE_DELAY]);
7564 #else
7565       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7566 #endif
7567
7568       element = Feld[x][y];
7569       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7570     }
7571 #endif
7572
7573     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7574     {
7575       StartMoving(x, y);
7576
7577 #if 1
7578       element = Feld[x][y];
7579       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7580 #if 0
7581       if (element == EL_MOLE)
7582         printf("::: %d, %d, %d [%d]\n",
7583                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7584                GfxAction[x][y]);
7585 #endif
7586 #if 0
7587       if (element == EL_YAMYAM)
7588         printf("::: %d, %d, %d\n",
7589                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7590 #endif
7591 #endif
7592
7593       if (IS_ANIMATED(graphic) &&
7594           !IS_MOVING(x, y) &&
7595           !Stop[x][y])
7596       {
7597         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7598
7599 #if 0
7600         if (element == EL_BUG)
7601           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7602 #endif
7603
7604 #if 0
7605         if (element == EL_MOLE)
7606           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7607 #endif
7608       }
7609
7610       if (IS_GEM(element) || element == EL_SP_INFOTRON)
7611         EdelsteinFunkeln(x, y);
7612     }
7613     else if ((element == EL_ACID ||
7614               element == EL_EXIT_OPEN ||
7615               element == EL_SP_EXIT_OPEN ||
7616               element == EL_SP_TERMINAL ||
7617               element == EL_SP_TERMINAL_ACTIVE ||
7618               element == EL_EXTRA_TIME ||
7619               element == EL_SHIELD_NORMAL ||
7620               element == EL_SHIELD_DEADLY) &&
7621              IS_ANIMATED(graphic))
7622       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7623     else if (IS_MOVING(x, y))
7624       ContinueMoving(x, y);
7625     else if (IS_ACTIVE_BOMB(element))
7626       CheckDynamite(x, y);
7627 #if 0
7628     else if (element == EL_EXPLOSION && !game.explosions_delayed)
7629       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7630 #endif
7631     else if (element == EL_AMOEBA_GROWING)
7632       AmoebeWaechst(x, y);
7633     else if (element == EL_AMOEBA_SHRINKING)
7634       AmoebaDisappearing(x, y);
7635
7636 #if !USE_NEW_AMOEBA_CODE
7637     else if (IS_AMOEBALIVE(element))
7638       AmoebeAbleger(x, y);
7639 #endif
7640
7641     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7642       Life(x, y);
7643     else if (element == EL_EXIT_CLOSED)
7644       CheckExit(x, y);
7645     else if (element == EL_SP_EXIT_CLOSED)
7646       CheckExitSP(x, y);
7647     else if (element == EL_EXPANDABLE_WALL_GROWING)
7648       MauerWaechst(x, y);
7649     else if (element == EL_EXPANDABLE_WALL ||
7650              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7651              element == EL_EXPANDABLE_WALL_VERTICAL ||
7652              element == EL_EXPANDABLE_WALL_ANY)
7653       MauerAbleger(x, y);
7654     else if (element == EL_FLAMES)
7655       CheckForDragon(x, y);
7656 #if 0
7657     else if (IS_AUTO_CHANGING(element))
7658       ChangeElement(x, y);
7659 #endif
7660     else if (element == EL_EXPLOSION)
7661       ; /* drawing of correct explosion animation is handled separately */
7662     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7663       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7664
7665 #if 0
7666     /* this may take place after moving, so 'element' may have changed */
7667     if (IS_AUTO_CHANGING(Feld[x][y]))
7668       ChangeElement(x, y);
7669 #endif
7670
7671     if (IS_BELT_ACTIVE(element))
7672       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7673
7674     if (game.magic_wall_active)
7675     {
7676       int jx = local_player->jx, jy = local_player->jy;
7677
7678       /* play the element sound at the position nearest to the player */
7679       if ((element == EL_MAGIC_WALL_FULL ||
7680            element == EL_MAGIC_WALL_ACTIVE ||
7681            element == EL_MAGIC_WALL_EMPTYING ||
7682            element == EL_BD_MAGIC_WALL_FULL ||
7683            element == EL_BD_MAGIC_WALL_ACTIVE ||
7684            element == EL_BD_MAGIC_WALL_EMPTYING) &&
7685           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7686       {
7687         magic_wall_x = x;
7688         magic_wall_y = y;
7689       }
7690     }
7691   }
7692
7693 #if USE_NEW_AMOEBA_CODE
7694   /* new experimental amoeba growth stuff */
7695 #if 1
7696   if (!(FrameCounter % 8))
7697 #endif
7698   {
7699     static unsigned long random = 1684108901;
7700
7701     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7702     {
7703 #if 0
7704       x = (random >> 10) % lev_fieldx;
7705       y = (random >> 20) % lev_fieldy;
7706 #else
7707       x = RND(lev_fieldx);
7708       y = RND(lev_fieldy);
7709 #endif
7710       element = Feld[x][y];
7711
7712       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7713       if (!IS_PLAYER(x,y) &&
7714           (element == EL_EMPTY ||
7715            element == EL_SAND ||
7716            element == EL_QUICKSAND_EMPTY ||
7717            element == EL_ACID_SPLASH_LEFT ||
7718            element == EL_ACID_SPLASH_RIGHT))
7719       {
7720         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7721             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7722             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7723             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7724           Feld[x][y] = EL_AMOEBA_DROP;
7725       }
7726
7727       random = random * 129 + 1;
7728     }
7729   }
7730 #endif
7731
7732 #if 0
7733   if (game.explosions_delayed)
7734 #endif
7735   {
7736     game.explosions_delayed = FALSE;
7737
7738     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7739     {
7740       element = Feld[x][y];
7741
7742       if (ExplodeField[x][y])
7743         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7744       else if (element == EL_EXPLOSION)
7745         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7746
7747       ExplodeField[x][y] = EX_NO_EXPLOSION;
7748     }
7749
7750     game.explosions_delayed = TRUE;
7751   }
7752
7753   if (game.magic_wall_active)
7754   {
7755     if (!(game.magic_wall_time_left % 4))
7756     {
7757       int element = Feld[magic_wall_x][magic_wall_y];
7758
7759       if (element == EL_BD_MAGIC_WALL_FULL ||
7760           element == EL_BD_MAGIC_WALL_ACTIVE ||
7761           element == EL_BD_MAGIC_WALL_EMPTYING)
7762         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7763       else
7764         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7765     }
7766
7767     if (game.magic_wall_time_left > 0)
7768     {
7769       game.magic_wall_time_left--;
7770       if (!game.magic_wall_time_left)
7771       {
7772         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7773         {
7774           element = Feld[x][y];
7775
7776           if (element == EL_MAGIC_WALL_ACTIVE ||
7777               element == EL_MAGIC_WALL_FULL)
7778           {
7779             Feld[x][y] = EL_MAGIC_WALL_DEAD;
7780             DrawLevelField(x, y);
7781           }
7782           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7783                    element == EL_BD_MAGIC_WALL_FULL)
7784           {
7785             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7786             DrawLevelField(x, y);
7787           }
7788         }
7789
7790         game.magic_wall_active = FALSE;
7791       }
7792     }
7793   }
7794
7795   if (game.light_time_left > 0)
7796   {
7797     game.light_time_left--;
7798
7799     if (game.light_time_left == 0)
7800       RedrawAllLightSwitchesAndInvisibleElements();
7801   }
7802
7803   if (game.timegate_time_left > 0)
7804   {
7805     game.timegate_time_left--;
7806
7807     if (game.timegate_time_left == 0)
7808       CloseAllOpenTimegates();
7809   }
7810
7811   for (i = 0; i < MAX_PLAYERS; i++)
7812   {
7813     struct PlayerInfo *player = &stored_player[i];
7814
7815     if (SHIELD_ON(player))
7816     {
7817       if (player->shield_deadly_time_left)
7818         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7819       else if (player->shield_normal_time_left)
7820         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7821     }
7822   }
7823
7824   if (TimeFrames >= FRAMES_PER_SECOND)
7825   {
7826     TimeFrames = 0;
7827     TimePlayed++;
7828
7829     for (i = 0; i < MAX_PLAYERS; i++)
7830     {
7831       struct PlayerInfo *player = &stored_player[i];
7832
7833       if (SHIELD_ON(player))
7834       {
7835         player->shield_normal_time_left--;
7836
7837         if (player->shield_deadly_time_left > 0)
7838           player->shield_deadly_time_left--;
7839       }
7840     }
7841
7842     if (tape.recording || tape.playing)
7843       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7844
7845     if (TimeLeft > 0)
7846     {
7847       TimeLeft--;
7848
7849       if (TimeLeft <= 10 && setup.time_limit)
7850         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7851
7852       DrawGameValue_Time(TimeLeft);
7853
7854       if (!TimeLeft && setup.time_limit)
7855         for (i = 0; i < MAX_PLAYERS; i++)
7856           KillHero(&stored_player[i]);
7857     }
7858     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7859       DrawGameValue_Time(TimePlayed);
7860   }
7861
7862   DrawAllPlayers();
7863   PlayAllPlayersSound();
7864
7865   if (options.debug)                    /* calculate frames per second */
7866   {
7867     static unsigned long fps_counter = 0;
7868     static int fps_frames = 0;
7869     unsigned long fps_delay_ms = Counter() - fps_counter;
7870
7871     fps_frames++;
7872
7873     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
7874     {
7875       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7876
7877       fps_frames = 0;
7878       fps_counter = Counter();
7879     }
7880
7881     redraw_mask |= REDRAW_FPS;
7882   }
7883
7884 #if 0
7885   if (stored_player[0].jx != stored_player[0].last_jx ||
7886       stored_player[0].jy != stored_player[0].last_jy)
7887     printf("::: %d, %d, %d, %d, %d\n",
7888            stored_player[0].MovDir,
7889            stored_player[0].MovPos,
7890            stored_player[0].GfxPos,
7891            stored_player[0].Frame,
7892            stored_player[0].StepFrame);
7893 #endif
7894
7895 #if 1
7896   FrameCounter++;
7897   TimeFrames++;
7898
7899   for (i = 0; i < MAX_PLAYERS; i++)
7900   {
7901     int move_frames =
7902       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
7903
7904     stored_player[i].Frame += move_frames;
7905
7906     if (stored_player[i].MovPos != 0)
7907       stored_player[i].StepFrame += move_frames;
7908
7909     if (stored_player[i].drop_delay > 0)
7910       stored_player[i].drop_delay--;
7911   }
7912 #endif
7913
7914 #if 1
7915   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7916   {
7917     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7918
7919     local_player->show_envelope = 0;
7920   }
7921 #endif
7922 }
7923
7924 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7925 {
7926   int min_x = x, min_y = y, max_x = x, max_y = y;
7927   int i;
7928
7929   for (i = 0; i < MAX_PLAYERS; i++)
7930   {
7931     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7932
7933     if (!stored_player[i].active || &stored_player[i] == player)
7934       continue;
7935
7936     min_x = MIN(min_x, jx);
7937     min_y = MIN(min_y, jy);
7938     max_x = MAX(max_x, jx);
7939     max_y = MAX(max_y, jy);
7940   }
7941
7942   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7943 }
7944
7945 static boolean AllPlayersInVisibleScreen()
7946 {
7947   int i;
7948
7949   for (i = 0; i < MAX_PLAYERS; i++)
7950   {
7951     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7952
7953     if (!stored_player[i].active)
7954       continue;
7955
7956     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7957       return FALSE;
7958   }
7959
7960   return TRUE;
7961 }
7962
7963 void ScrollLevel(int dx, int dy)
7964 {
7965   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7966   int x, y;
7967
7968   BlitBitmap(drawto_field, drawto_field,
7969              FX + TILEX * (dx == -1) - softscroll_offset,
7970              FY + TILEY * (dy == -1) - softscroll_offset,
7971              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7972              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7973              FX + TILEX * (dx == 1) - softscroll_offset,
7974              FY + TILEY * (dy == 1) - softscroll_offset);
7975
7976   if (dx)
7977   {
7978     x = (dx == 1 ? BX1 : BX2);
7979     for (y = BY1; y <= BY2; y++)
7980       DrawScreenField(x, y);
7981   }
7982
7983   if (dy)
7984   {
7985     y = (dy == 1 ? BY1 : BY2);
7986     for (x = BX1; x <= BX2; x++)
7987       DrawScreenField(x, y);
7988   }
7989
7990   redraw_mask |= REDRAW_FIELD;
7991 }
7992
7993 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7994 {
7995   int nextx = x + dx, nexty = y + dy;
7996   int element = Feld[x][y];
7997
7998   if ((dx == -1 &&
7999        element != EL_SP_PORT_LEFT &&
8000        element != EL_SP_GRAVITY_PORT_LEFT &&
8001        element != EL_SP_PORT_HORIZONTAL &&
8002        element != EL_SP_PORT_ANY) ||
8003       (dx == +1 &&
8004        element != EL_SP_PORT_RIGHT &&
8005        element != EL_SP_GRAVITY_PORT_RIGHT &&
8006        element != EL_SP_PORT_HORIZONTAL &&
8007        element != EL_SP_PORT_ANY) ||
8008       (dy == -1 &&
8009        element != EL_SP_PORT_UP &&
8010        element != EL_SP_GRAVITY_PORT_UP &&
8011        element != EL_SP_PORT_VERTICAL &&
8012        element != EL_SP_PORT_ANY) ||
8013       (dy == +1 &&
8014        element != EL_SP_PORT_DOWN &&
8015        element != EL_SP_GRAVITY_PORT_DOWN &&
8016        element != EL_SP_PORT_VERTICAL &&
8017        element != EL_SP_PORT_ANY) ||
8018       !IN_LEV_FIELD(nextx, nexty) ||
8019       !IS_FREE(nextx, nexty))
8020     return FALSE;
8021
8022   return TRUE;
8023 }
8024
8025 static void CheckGravityMovement(struct PlayerInfo *player)
8026 {
8027   if (game.gravity && !player->programmed_action)
8028   {
8029     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
8030     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
8031     int move_dir =
8032       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
8033        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8034        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8035     int jx = player->jx, jy = player->jy;
8036     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8037     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8038     int new_jx = jx + dx, new_jy = jy + dy;
8039     boolean field_under_player_is_free =
8040       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8041     boolean player_is_moving_to_valid_field =
8042       (IN_LEV_FIELD(new_jx, new_jy) &&
8043        (Feld[new_jx][new_jy] == EL_SP_BASE ||
8044         Feld[new_jx][new_jy] == EL_SAND ||
8045         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8046          canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8047     /* !!! extend EL_SAND to anything diggable !!! */
8048
8049     boolean player_is_standing_on_valid_field =
8050       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8051        (IS_WALKABLE(Feld[jx][jy]) &&
8052         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8053
8054     if (field_under_player_is_free &&
8055         !player_is_standing_on_valid_field &&
8056         !player_is_moving_to_valid_field)
8057       player->programmed_action = MV_DOWN;
8058   }
8059 }
8060
8061 /*
8062   MovePlayerOneStep()
8063   -----------------------------------------------------------------------------
8064   dx, dy:               direction (non-diagonal) to try to move the player to
8065   real_dx, real_dy:     direction as read from input device (can be diagonal)
8066 */
8067
8068 boolean MovePlayerOneStep(struct PlayerInfo *player,
8069                           int dx, int dy, int real_dx, int real_dy)
8070 {
8071 #if 0
8072   static int change_sides[4][2] =
8073   {
8074     /* enter side        leave side */
8075     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
8076     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
8077     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
8078     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
8079   };
8080   int move_direction = (dx == -1 ? MV_LEFT :
8081                         dx == +1 ? MV_RIGHT :
8082                         dy == -1 ? MV_UP :
8083                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8084   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8085   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8086 #endif
8087   int jx = player->jx, jy = player->jy;
8088   int new_jx = jx + dx, new_jy = jy + dy;
8089   int element;
8090   int can_move;
8091
8092   if (!player->active || (!dx && !dy))
8093     return MF_NO_ACTION;
8094
8095   player->MovDir = (dx < 0 ? MV_LEFT :
8096                     dx > 0 ? MV_RIGHT :
8097                     dy < 0 ? MV_UP :
8098                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
8099
8100   if (!IN_LEV_FIELD(new_jx, new_jy))
8101     return MF_NO_ACTION;
8102
8103   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8104     return MF_NO_ACTION;
8105
8106 #if 0
8107   element = MovingOrBlocked2Element(new_jx, new_jy);
8108 #else
8109   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8110 #endif
8111
8112   if (DONT_RUN_INTO(element))
8113   {
8114     if (element == EL_ACID && dx == 0 && dy == 1)
8115     {
8116       SplashAcid(new_jx, new_jy);
8117       Feld[jx][jy] = EL_PLAYER_1;
8118       InitMovingField(jx, jy, MV_DOWN);
8119       Store[jx][jy] = EL_ACID;
8120       ContinueMoving(jx, jy);
8121       BuryHero(player);
8122     }
8123     else
8124       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8125
8126     return MF_MOVING;
8127   }
8128
8129   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8130   if (can_move != MF_MOVING)
8131     return can_move;
8132
8133   /* check if DigField() has caused relocation of the player */
8134   if (player->jx != jx || player->jy != jy)
8135     return MF_NO_ACTION;
8136
8137   StorePlayer[jx][jy] = 0;
8138   player->last_jx = jx;
8139   player->last_jy = jy;
8140   player->jx = new_jx;
8141   player->jy = new_jy;
8142   StorePlayer[new_jx][new_jy] = player->element_nr;
8143
8144   player->MovPos =
8145     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8146
8147   player->step_counter++;
8148
8149   player->drop_delay = 0;
8150
8151   PlayerVisit[jx][jy] = FrameCounter;
8152
8153   ScrollPlayer(player, SCROLL_INIT);
8154
8155 #if 0
8156   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8157   {
8158     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8159                                     CE_OTHER_GETS_LEFT);
8160     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
8161                            CE_LEFT_BY_PLAYER, -1);
8162   }
8163
8164   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8165   {
8166     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
8167                                     enter_side, CE_OTHER_GETS_ENTERED);
8168     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
8169                            CE_ENTERED_BY_PLAYER, -1);
8170   }
8171 #endif
8172
8173   return MF_MOVING;
8174 }
8175
8176 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8177 {
8178   int jx = player->jx, jy = player->jy;
8179   int old_jx = jx, old_jy = jy;
8180   int moved = MF_NO_ACTION;
8181
8182 #if 1
8183   if (!player->active)
8184     return FALSE;
8185
8186   if (!dx && !dy)
8187   {
8188     if (player->MovPos == 0)
8189     {
8190       player->is_moving = FALSE;
8191       player->is_digging = FALSE;
8192       player->is_collecting = FALSE;
8193       player->is_snapping = FALSE;
8194       player->is_pushing = FALSE;
8195     }
8196
8197     return FALSE;
8198   }
8199 #else
8200   if (!player->active || (!dx && !dy))
8201     return FALSE;
8202 #endif
8203
8204 #if 0
8205   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8206       !tape.playing)
8207     return FALSE;
8208 #else
8209   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8210       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8211     return FALSE;
8212 #endif
8213
8214   /* remove the last programmed player action */
8215   player->programmed_action = 0;
8216
8217   if (player->MovPos)
8218   {
8219     /* should only happen if pre-1.2 tape recordings are played */
8220     /* this is only for backward compatibility */
8221
8222     int original_move_delay_value = player->move_delay_value;
8223
8224 #if DEBUG
8225     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8226            tape.counter);
8227 #endif
8228
8229     /* scroll remaining steps with finest movement resolution */
8230     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8231
8232     while (player->MovPos)
8233     {
8234       ScrollPlayer(player, SCROLL_GO_ON);
8235       ScrollScreen(NULL, SCROLL_GO_ON);
8236       FrameCounter++;
8237       DrawAllPlayers();
8238       BackToFront();
8239     }
8240
8241     player->move_delay_value = original_move_delay_value;
8242   }
8243
8244   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8245   {
8246     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8247       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8248   }
8249   else
8250   {
8251     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8252       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8253   }
8254
8255   jx = player->jx;
8256   jy = player->jy;
8257
8258   if (moved & MF_MOVING && !ScreenMovPos &&
8259       (player == local_player || !options.network))
8260   {
8261     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8262     int offset = (setup.scroll_delay ? 3 : 0);
8263
8264     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8265     {
8266       /* actual player has left the screen -- scroll in that direction */
8267       if (jx != old_jx)         /* player has moved horizontally */
8268         scroll_x += (jx - old_jx);
8269       else                      /* player has moved vertically */
8270         scroll_y += (jy - old_jy);
8271     }
8272     else
8273     {
8274       if (jx != old_jx)         /* player has moved horizontally */
8275       {
8276         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8277             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8278           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8279
8280         /* don't scroll over playfield boundaries */
8281         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8282           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8283
8284         /* don't scroll more than one field at a time */
8285         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8286
8287         /* don't scroll against the player's moving direction */
8288         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8289             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8290           scroll_x = old_scroll_x;
8291       }
8292       else                      /* player has moved vertically */
8293       {
8294         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8295             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8296           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8297
8298         /* don't scroll over playfield boundaries */
8299         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8300           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8301
8302         /* don't scroll more than one field at a time */
8303         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8304
8305         /* don't scroll against the player's moving direction */
8306         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8307             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8308           scroll_y = old_scroll_y;
8309       }
8310     }
8311
8312     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8313     {
8314       if (!options.network && !AllPlayersInVisibleScreen())
8315       {
8316         scroll_x = old_scroll_x;
8317         scroll_y = old_scroll_y;
8318       }
8319       else
8320       {
8321         ScrollScreen(player, SCROLL_INIT);
8322         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8323       }
8324     }
8325   }
8326
8327 #if 0
8328 #if 1
8329   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8330 #else
8331   if (!(moved & MF_MOVING) && !player->is_pushing)
8332     player->Frame = 0;
8333 #endif
8334 #endif
8335
8336   player->StepFrame = 0;
8337
8338   if (moved & MF_MOVING)
8339   {
8340     if (old_jx != jx && old_jy == jy)
8341       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8342     else if (old_jx == jx && old_jy != jy)
8343       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8344
8345     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8346
8347     player->last_move_dir = player->MovDir;
8348     player->is_moving = TRUE;
8349 #if 1
8350     player->is_snapping = FALSE;
8351 #endif
8352
8353 #if 1
8354     player->is_switching = FALSE;
8355 #endif
8356
8357     player->is_dropping = FALSE;
8358
8359
8360 #if 1
8361     {
8362       static int change_sides[4][2] =
8363       {
8364         /* enter side           leave side */
8365         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8366         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8367         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8368         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8369       };
8370       int move_direction = player->MovDir;
8371       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8372       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8373
8374 #if 1
8375       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8376       {
8377         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8378                                         leave_side, CE_OTHER_GETS_LEFT);
8379         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8380                                leave_side, CE_LEFT_BY_PLAYER, -1);
8381       }
8382
8383       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8384       {
8385         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8386                                         enter_side, CE_OTHER_GETS_ENTERED);
8387         CheckElementSideChange(jx, jy, Feld[jx][jy],
8388                                enter_side, CE_ENTERED_BY_PLAYER, -1);
8389       }
8390 #endif
8391
8392     }
8393 #endif
8394
8395
8396   }
8397   else
8398   {
8399     CheckGravityMovement(player);
8400
8401     /*
8402     player->last_move_dir = MV_NO_MOVING;
8403     */
8404     player->is_moving = FALSE;
8405   }
8406
8407   if (game.engine_version < VERSION_IDENT(3,0,7,0))
8408   {
8409     TestIfHeroTouchesBadThing(jx, jy);
8410     TestIfPlayerTouchesCustomElement(jx, jy);
8411   }
8412
8413   if (!player->active)
8414     RemoveHero(player);
8415
8416   return moved;
8417 }
8418
8419 void ScrollPlayer(struct PlayerInfo *player, int mode)
8420 {
8421   int jx = player->jx, jy = player->jy;
8422   int last_jx = player->last_jx, last_jy = player->last_jy;
8423   int move_stepsize = TILEX / player->move_delay_value;
8424
8425   if (!player->active || !player->MovPos)
8426     return;
8427
8428   if (mode == SCROLL_INIT)
8429   {
8430     player->actual_frame_counter = FrameCounter;
8431     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8432
8433     if (Feld[last_jx][last_jy] == EL_EMPTY)
8434       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8435
8436 #if 0
8437     DrawPlayer(player);
8438 #endif
8439
8440     return;
8441   }
8442   else if (!FrameReached(&player->actual_frame_counter, 1))
8443     return;
8444
8445   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8446   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8447
8448   if (!player->block_last_field &&
8449       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8450     Feld[last_jx][last_jy] = EL_EMPTY;
8451
8452   /* before DrawPlayer() to draw correct player graphic for this case */
8453   if (player->MovPos == 0)
8454     CheckGravityMovement(player);
8455
8456 #if 0
8457   DrawPlayer(player);   /* needed here only to cleanup last field */
8458 #endif
8459
8460   if (player->MovPos == 0)      /* player reached destination field */
8461   {
8462 #if 1
8463     if (player->move_delay_reset_counter > 0)
8464     {
8465       player->move_delay_reset_counter--;
8466
8467       if (player->move_delay_reset_counter == 0)
8468       {
8469         /* continue with normal speed after quickly moving through gate */
8470         HALVE_PLAYER_SPEED(player);
8471
8472         /* be able to make the next move without delay */
8473         player->move_delay = 0;
8474       }
8475     }
8476 #else
8477     if (IS_PASSABLE(Feld[last_jx][last_jy]))
8478     {
8479       /* continue with normal speed after quickly moving through gate */
8480       HALVE_PLAYER_SPEED(player);
8481
8482       /* be able to make the next move without delay */
8483       player->move_delay = 0;
8484     }
8485 #endif
8486
8487     if (player->block_last_field &&
8488         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8489       Feld[last_jx][last_jy] = EL_EMPTY;
8490
8491     player->last_jx = jx;
8492     player->last_jy = jy;
8493
8494     if (Feld[jx][jy] == EL_EXIT_OPEN ||
8495         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8496         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
8497     {
8498       DrawPlayer(player);       /* needed here only to cleanup last field */
8499       RemoveHero(player);
8500
8501       if (local_player->friends_still_needed == 0 ||
8502           IS_SP_ELEMENT(Feld[jx][jy]))
8503         player->LevelSolved = player->GameOver = TRUE;
8504     }
8505
8506     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8507     {
8508       TestIfHeroTouchesBadThing(jx, jy);
8509       TestIfPlayerTouchesCustomElement(jx, jy);
8510 #if 1
8511       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
8512 #endif
8513
8514       if (!player->active)
8515         RemoveHero(player);
8516     }
8517
8518     if (tape.single_step && tape.recording && !tape.pausing &&
8519         !player->programmed_action)
8520       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8521   }
8522 }
8523
8524 void ScrollScreen(struct PlayerInfo *player, int mode)
8525 {
8526   static unsigned long screen_frame_counter = 0;
8527
8528   if (mode == SCROLL_INIT)
8529   {
8530     /* set scrolling step size according to actual player's moving speed */
8531     ScrollStepSize = TILEX / player->move_delay_value;
8532
8533     screen_frame_counter = FrameCounter;
8534     ScreenMovDir = player->MovDir;
8535     ScreenMovPos = player->MovPos;
8536     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8537     return;
8538   }
8539   else if (!FrameReached(&screen_frame_counter, 1))
8540     return;
8541
8542   if (ScreenMovPos)
8543   {
8544     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8545     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8546     redraw_mask |= REDRAW_FIELD;
8547   }
8548   else
8549     ScreenMovDir = MV_NO_MOVING;
8550 }
8551
8552 void TestIfPlayerTouchesCustomElement(int x, int y)
8553 {
8554   static int xy[4][2] =
8555   {
8556     { 0, -1 },
8557     { -1, 0 },
8558     { +1, 0 },
8559     { 0, +1 }
8560   };
8561   static int change_sides[4][2] =
8562   {
8563     /* center side       border side */
8564     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8565     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8566     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8567     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8568   };
8569   static int touch_dir[4] =
8570   {
8571     MV_LEFT | MV_RIGHT,
8572     MV_UP   | MV_DOWN,
8573     MV_UP   | MV_DOWN,
8574     MV_LEFT | MV_RIGHT
8575   };
8576   int center_element = Feld[x][y];      /* should always be non-moving! */
8577   int i;
8578
8579   for (i = 0; i < NUM_DIRECTIONS; i++)
8580   {
8581     int xx = x + xy[i][0];
8582     int yy = y + xy[i][1];
8583     int center_side = change_sides[i][0];
8584     int border_side = change_sides[i][1];
8585     int border_element;
8586
8587     if (!IN_LEV_FIELD(xx, yy))
8588       continue;
8589
8590     if (IS_PLAYER(x, y))
8591     {
8592       if (game.engine_version < VERSION_IDENT(3,0,7,0))
8593         border_element = Feld[xx][yy];          /* may be moving! */
8594       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8595         border_element = Feld[xx][yy];
8596       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
8597         border_element = MovingOrBlocked2Element(xx, yy);
8598       else
8599         continue;               /* center and border element do not touch */
8600
8601       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8602                                       CE_OTHER_GETS_TOUCHED);
8603       CheckElementSideChange(xx, yy, border_element, border_side,
8604                              CE_TOUCHED_BY_PLAYER, -1);
8605     }
8606     else if (IS_PLAYER(xx, yy))
8607     {
8608       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8609       {
8610         struct PlayerInfo *player = PLAYERINFO(xx, yy);
8611
8612         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8613           continue;             /* center and border element do not touch */
8614       }
8615
8616       CheckTriggeredElementSideChange(x, y, center_element, center_side,
8617                                       CE_OTHER_GETS_TOUCHED);
8618       CheckElementSideChange(x, y, center_element, center_side,
8619                              CE_TOUCHED_BY_PLAYER, -1);
8620
8621       break;
8622     }
8623   }
8624 }
8625
8626 void TestIfElementTouchesCustomElement(int x, int y)
8627 {
8628   static int xy[4][2] =
8629   {
8630     { 0, -1 },
8631     { -1, 0 },
8632     { +1, 0 },
8633     { 0, +1 }
8634   };
8635   static int change_sides[4][2] =
8636   {
8637     /* center side      border side */
8638     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8639     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8640     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8641     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8642   };
8643   static int touch_dir[4] =
8644   {
8645     MV_LEFT | MV_RIGHT,
8646     MV_UP   | MV_DOWN,
8647     MV_UP   | MV_DOWN,
8648     MV_LEFT | MV_RIGHT
8649   };
8650   boolean change_center_element = FALSE;
8651   int center_element_change_page = 0;
8652   int center_element = Feld[x][y];      /* should always be non-moving! */
8653   int i, j;
8654
8655   for (i = 0; i < NUM_DIRECTIONS; i++)
8656   {
8657     int xx = x + xy[i][0];
8658     int yy = y + xy[i][1];
8659     int center_side = change_sides[i][0];
8660     int border_side = change_sides[i][1];
8661     int border_element;
8662
8663     if (!IN_LEV_FIELD(xx, yy))
8664       continue;
8665
8666     if (game.engine_version < VERSION_IDENT(3,0,7,0))
8667       border_element = Feld[xx][yy];    /* may be moving! */
8668     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8669       border_element = Feld[xx][yy];
8670     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
8671       border_element = MovingOrBlocked2Element(xx, yy);
8672     else
8673       continue;                 /* center and border element do not touch */
8674
8675     /* check for change of center element (but change it only once) */
8676     if (IS_CUSTOM_ELEMENT(center_element) &&
8677         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8678         !change_center_element)
8679     {
8680       for (j = 0; j < element_info[center_element].num_change_pages; j++)
8681       {
8682         struct ElementChangeInfo *change =
8683           &element_info[center_element].change_page[j];
8684
8685         if (change->can_change &&
8686             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8687             change->sides & border_side &&
8688 #if 1
8689             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8690 #else
8691             change->trigger_element == border_element
8692 #endif
8693             )
8694         {
8695           change_center_element = TRUE;
8696           center_element_change_page = j;
8697
8698           break;
8699         }
8700       }
8701     }
8702
8703     /* check for change of border element */
8704     if (IS_CUSTOM_ELEMENT(border_element) &&
8705         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8706     {
8707       for (j = 0; j < element_info[border_element].num_change_pages; j++)
8708       {
8709         struct ElementChangeInfo *change =
8710           &element_info[border_element].change_page[j];
8711
8712         if (change->can_change &&
8713             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8714             change->sides & center_side &&
8715 #if 1
8716             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8717 #else
8718             change->trigger_element == center_element
8719 #endif
8720             )
8721         {
8722           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8723                                  CE_OTHER_IS_TOUCHING, j);
8724           break;
8725         }
8726       }
8727     }
8728   }
8729
8730   if (change_center_element)
8731     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8732                            CE_OTHER_IS_TOUCHING, center_element_change_page);
8733 }
8734
8735 void TestIfElementHitsCustomElement(int x, int y, int direction)
8736 {
8737   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8738   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8739   int hitx = x + dx, hity = y + dy;
8740   int hitting_element = Feld[x][y];
8741 #if 0
8742   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8743                         !IS_FREE(hitx, hity) &&
8744                         (!IS_MOVING(hitx, hity) ||
8745                          MovDir[hitx][hity] != direction ||
8746                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
8747 #endif
8748
8749   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8750     return;
8751
8752 #if 0
8753   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8754     return;
8755 #endif
8756
8757   CheckElementSideChange(x, y, hitting_element,
8758                          direction, CE_HITTING_SOMETHING, -1);
8759
8760   if (IN_LEV_FIELD(hitx, hity))
8761   {
8762     int opposite_direction = MV_DIR_OPPOSITE(direction);
8763     int hitting_side = direction;
8764     int touched_side = opposite_direction;
8765     int touched_element = MovingOrBlocked2Element(hitx, hity);
8766 #if 1
8767     boolean object_hit = (!IS_MOVING(hitx, hity) ||
8768                           MovDir[hitx][hity] != direction ||
8769                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
8770
8771     object_hit = TRUE;
8772 #endif
8773
8774     if (object_hit)
8775     {
8776       int i;
8777
8778       CheckElementSideChange(hitx, hity, touched_element,
8779                              opposite_direction, CE_HIT_BY_SOMETHING, -1);
8780
8781       if (IS_CUSTOM_ELEMENT(hitting_element) &&
8782           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8783       {
8784         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8785         {
8786           struct ElementChangeInfo *change =
8787             &element_info[hitting_element].change_page[i];
8788
8789           if (change->can_change &&
8790               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8791               change->sides & touched_side &&
8792           
8793 #if 1
8794               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8795 #else
8796               change->trigger_element == touched_element
8797 #endif
8798               )
8799           {
8800             CheckElementSideChange(x, y, hitting_element,
8801                                    CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8802             break;
8803           }
8804         }
8805       }
8806
8807       if (IS_CUSTOM_ELEMENT(touched_element) &&
8808           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8809       {
8810         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8811         {
8812           struct ElementChangeInfo *change =
8813             &element_info[touched_element].change_page[i];
8814
8815           if (change->can_change &&
8816               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8817               change->sides & hitting_side &&
8818 #if 1
8819               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8820 #else
8821               change->trigger_element == hitting_element
8822 #endif
8823               )
8824           {
8825             CheckElementSideChange(hitx, hity, touched_element,
8826                                    CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8827             break;
8828           }
8829         }
8830       }
8831     }
8832   }
8833 }
8834
8835 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8836 {
8837   int i, kill_x = -1, kill_y = -1;
8838   static int test_xy[4][2] =
8839   {
8840     { 0, -1 },
8841     { -1, 0 },
8842     { +1, 0 },
8843     { 0, +1 }
8844   };
8845   static int test_dir[4] =
8846   {
8847     MV_UP,
8848     MV_LEFT,
8849     MV_RIGHT,
8850     MV_DOWN
8851   };
8852
8853   for (i = 0; i < NUM_DIRECTIONS; i++)
8854   {
8855     int test_x, test_y, test_move_dir, test_element;
8856
8857     test_x = good_x + test_xy[i][0];
8858     test_y = good_y + test_xy[i][1];
8859     if (!IN_LEV_FIELD(test_x, test_y))
8860       continue;
8861
8862     test_move_dir =
8863       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8864
8865 #if 0
8866     test_element = Feld[test_x][test_y];
8867 #else
8868     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8869 #endif
8870
8871     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8872        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8873     */
8874     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8875         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
8876     {
8877       kill_x = test_x;
8878       kill_y = test_y;
8879       break;
8880     }
8881   }
8882
8883   if (kill_x != -1 || kill_y != -1)
8884   {
8885     if (IS_PLAYER(good_x, good_y))
8886     {
8887       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8888
8889       if (player->shield_deadly_time_left > 0)
8890         Bang(kill_x, kill_y);
8891       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8892         KillHero(player);
8893     }
8894     else
8895       Bang(good_x, good_y);
8896   }
8897 }
8898
8899 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8900 {
8901   int i, kill_x = -1, kill_y = -1;
8902   int bad_element = Feld[bad_x][bad_y];
8903   static int test_xy[4][2] =
8904   {
8905     { 0, -1 },
8906     { -1, 0 },
8907     { +1, 0 },
8908     { 0, +1 }
8909   };
8910   static int touch_dir[4] =
8911   {
8912     MV_LEFT | MV_RIGHT,
8913     MV_UP   | MV_DOWN,
8914     MV_UP   | MV_DOWN,
8915     MV_LEFT | MV_RIGHT
8916   };
8917   static int test_dir[4] =
8918   {
8919     MV_UP,
8920     MV_LEFT,
8921     MV_RIGHT,
8922     MV_DOWN
8923   };
8924
8925   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
8926     return;
8927
8928   for (i = 0; i < NUM_DIRECTIONS; i++)
8929   {
8930     int test_x, test_y, test_move_dir, test_element;
8931
8932     test_x = bad_x + test_xy[i][0];
8933     test_y = bad_y + test_xy[i][1];
8934     if (!IN_LEV_FIELD(test_x, test_y))
8935       continue;
8936
8937     test_move_dir =
8938       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8939
8940     test_element = Feld[test_x][test_y];
8941
8942     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8943        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8944     */
8945     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
8946         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
8947     {
8948       /* good thing is player or penguin that does not move away */
8949       if (IS_PLAYER(test_x, test_y))
8950       {
8951         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8952
8953         if (bad_element == EL_ROBOT && player->is_moving)
8954           continue;     /* robot does not kill player if he is moving */
8955
8956         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8957         {
8958           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8959             continue;           /* center and border element do not touch */
8960         }
8961
8962         kill_x = test_x;
8963         kill_y = test_y;
8964         break;
8965       }
8966       else if (test_element == EL_PENGUIN)
8967       {
8968         kill_x = test_x;
8969         kill_y = test_y;
8970         break;
8971       }
8972     }
8973   }
8974
8975   if (kill_x != -1 || kill_y != -1)
8976   {
8977     if (IS_PLAYER(kill_x, kill_y))
8978     {
8979       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8980
8981       if (player->shield_deadly_time_left > 0)
8982         Bang(bad_x, bad_y);
8983       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8984         KillHero(player);
8985     }
8986     else
8987       Bang(kill_x, kill_y);
8988   }
8989 }
8990
8991 void TestIfHeroTouchesBadThing(int x, int y)
8992 {
8993   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8994 }
8995
8996 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8997 {
8998   TestIfGoodThingHitsBadThing(x, y, move_dir);
8999 }
9000
9001 void TestIfBadThingTouchesHero(int x, int y)
9002 {
9003   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9004 }
9005
9006 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9007 {
9008   TestIfBadThingHitsGoodThing(x, y, move_dir);
9009 }
9010
9011 void TestIfFriendTouchesBadThing(int x, int y)
9012 {
9013   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9014 }
9015
9016 void TestIfBadThingTouchesFriend(int x, int y)
9017 {
9018   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9019 }
9020
9021 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9022 {
9023   int i, kill_x = bad_x, kill_y = bad_y;
9024   static int xy[4][2] =
9025   {
9026     { 0, -1 },
9027     { -1, 0 },
9028     { +1, 0 },
9029     { 0, +1 }
9030   };
9031
9032   for (i = 0; i < NUM_DIRECTIONS; i++)
9033   {
9034     int x, y, element;
9035
9036     x = bad_x + xy[i][0];
9037     y = bad_y + xy[i][1];
9038     if (!IN_LEV_FIELD(x, y))
9039       continue;
9040
9041     element = Feld[x][y];
9042     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9043         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9044     {
9045       kill_x = x;
9046       kill_y = y;
9047       break;
9048     }
9049   }
9050
9051   if (kill_x != bad_x || kill_y != bad_y)
9052     Bang(bad_x, bad_y);
9053 }
9054
9055 void KillHero(struct PlayerInfo *player)
9056 {
9057   int jx = player->jx, jy = player->jy;
9058
9059   if (!player->active)
9060     return;
9061
9062   /* remove accessible field at the player's position */
9063   Feld[jx][jy] = EL_EMPTY;
9064
9065   /* deactivate shield (else Bang()/Explode() would not work right) */
9066   player->shield_normal_time_left = 0;
9067   player->shield_deadly_time_left = 0;
9068
9069   Bang(jx, jy);
9070   BuryHero(player);
9071 }
9072
9073 static void KillHeroUnlessEnemyProtected(int x, int y)
9074 {
9075   if (!PLAYER_ENEMY_PROTECTED(x, y))
9076     KillHero(PLAYERINFO(x, y));
9077 }
9078
9079 static void KillHeroUnlessExplosionProtected(int x, int y)
9080 {
9081   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9082     KillHero(PLAYERINFO(x, y));
9083 }
9084
9085 void BuryHero(struct PlayerInfo *player)
9086 {
9087   int jx = player->jx, jy = player->jy;
9088
9089   if (!player->active)
9090     return;
9091
9092 #if 1
9093   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9094 #else
9095   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9096 #endif
9097   PlayLevelSound(jx, jy, SND_GAME_LOSING);
9098
9099   player->GameOver = TRUE;
9100   RemoveHero(player);
9101 }
9102
9103 void RemoveHero(struct PlayerInfo *player)
9104 {
9105   int jx = player->jx, jy = player->jy;
9106   int i, found = FALSE;
9107
9108   player->present = FALSE;
9109   player->active = FALSE;
9110
9111   if (!ExplodeField[jx][jy])
9112     StorePlayer[jx][jy] = 0;
9113
9114   for (i = 0; i < MAX_PLAYERS; i++)
9115     if (stored_player[i].active)
9116       found = TRUE;
9117
9118   if (!found)
9119     AllPlayersGone = TRUE;
9120
9121   ExitX = ZX = jx;
9122   ExitY = ZY = jy;
9123 }
9124
9125 /*
9126   =============================================================================
9127   checkDiagonalPushing()
9128   -----------------------------------------------------------------------------
9129   check if diagonal input device direction results in pushing of object
9130   (by checking if the alternative direction is walkable, diggable, ...)
9131   =============================================================================
9132 */
9133
9134 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9135                                     int x, int y, int real_dx, int real_dy)
9136 {
9137   int jx, jy, dx, dy, xx, yy;
9138
9139   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
9140     return TRUE;
9141
9142   /* diagonal direction: check alternative direction */
9143   jx = player->jx;
9144   jy = player->jy;
9145   dx = x - jx;
9146   dy = y - jy;
9147   xx = jx + (dx == 0 ? real_dx : 0);
9148   yy = jy + (dy == 0 ? real_dy : 0);
9149
9150   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9151 }
9152
9153 /*
9154   =============================================================================
9155   DigField()
9156   -----------------------------------------------------------------------------
9157   x, y:                 field next to player (non-diagonal) to try to dig to
9158   real_dx, real_dy:     direction as read from input device (can be diagonal)
9159   =============================================================================
9160 */
9161
9162 int DigField(struct PlayerInfo *player,
9163              int oldx, int oldy, int x, int y,
9164              int real_dx, int real_dy, int mode)
9165 {
9166   static int change_sides[4] =
9167   {
9168     CH_SIDE_RIGHT,      /* moving left  */
9169     CH_SIDE_LEFT,       /* moving right */
9170     CH_SIDE_BOTTOM,     /* moving up    */
9171     CH_SIDE_TOP,        /* moving down  */
9172   };
9173 #if 0
9174   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
9175 #endif
9176   int jx = oldx, jy = oldy;
9177   int dx = x - jx, dy = y - jy;
9178   int nextx = x + dx, nexty = y + dy;
9179   int move_direction = (dx == -1 ? MV_LEFT :
9180                         dx == +1 ? MV_RIGHT :
9181                         dy == -1 ? MV_UP :
9182                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9183   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9184   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
9185   int old_element = Feld[jx][jy];
9186   int element;
9187
9188   if (player->MovPos == 0)
9189   {
9190     player->is_digging = FALSE;
9191     player->is_collecting = FALSE;
9192   }
9193
9194   if (player->MovPos == 0)      /* last pushing move finished */
9195     player->is_pushing = FALSE;
9196
9197   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
9198   {
9199     player->is_switching = FALSE;
9200     player->push_delay = 0;
9201
9202     return MF_NO_ACTION;
9203   }
9204
9205   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9206     return MF_NO_ACTION;
9207
9208 #if 0
9209
9210 #if 0
9211   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
9212 #else
9213   if (IS_TUBE(Feld[jx][jy]) ||
9214       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
9215 #endif
9216   {
9217     int i = 0;
9218     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
9219     int tube_leave_directions[][2] =
9220     {
9221       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9222       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
9223       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
9224       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
9225       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
9226       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
9227       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
9228       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
9229       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
9230       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
9231       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
9232       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
9233     };
9234
9235     while (tube_leave_directions[i][0] != tube_element)
9236     {
9237       i++;
9238       if (tube_leave_directions[i][0] == -1)    /* should not happen */
9239         break;
9240     }
9241
9242     if (!(tube_leave_directions[i][1] & move_direction))
9243       return MF_NO_ACTION;      /* tube has no opening in this direction */
9244   }
9245
9246 #else
9247
9248   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9249     old_element = Back[jx][jy];
9250
9251 #endif
9252
9253   if (IS_WALKABLE(old_element) &&
9254       !(element_info[old_element].access_direction & move_direction))
9255     return MF_NO_ACTION;        /* field has no opening in this direction */
9256
9257   element = Feld[x][y];
9258
9259   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9260       game.engine_version >= VERSION_IDENT(2,2,0,0))
9261     return MF_NO_ACTION;
9262
9263   switch (element)
9264   {
9265     case EL_SP_PORT_LEFT:
9266     case EL_SP_PORT_RIGHT:
9267     case EL_SP_PORT_UP:
9268     case EL_SP_PORT_DOWN:
9269     case EL_SP_PORT_HORIZONTAL:
9270     case EL_SP_PORT_VERTICAL:
9271     case EL_SP_PORT_ANY:
9272     case EL_SP_GRAVITY_PORT_LEFT:
9273     case EL_SP_GRAVITY_PORT_RIGHT:
9274     case EL_SP_GRAVITY_PORT_UP:
9275     case EL_SP_GRAVITY_PORT_DOWN:
9276 #if 1
9277       if (!canEnterSupaplexPort(x, y, dx, dy))
9278         return MF_NO_ACTION;
9279 #else
9280       if ((dx == -1 &&
9281            element != EL_SP_PORT_LEFT &&
9282            element != EL_SP_GRAVITY_PORT_LEFT &&
9283            element != EL_SP_PORT_HORIZONTAL &&
9284            element != EL_SP_PORT_ANY) ||
9285           (dx == +1 &&
9286            element != EL_SP_PORT_RIGHT &&
9287            element != EL_SP_GRAVITY_PORT_RIGHT &&
9288            element != EL_SP_PORT_HORIZONTAL &&
9289            element != EL_SP_PORT_ANY) ||
9290           (dy == -1 &&
9291            element != EL_SP_PORT_UP &&
9292            element != EL_SP_GRAVITY_PORT_UP &&
9293            element != EL_SP_PORT_VERTICAL &&
9294            element != EL_SP_PORT_ANY) ||
9295           (dy == +1 &&
9296            element != EL_SP_PORT_DOWN &&
9297            element != EL_SP_GRAVITY_PORT_DOWN &&
9298            element != EL_SP_PORT_VERTICAL &&
9299            element != EL_SP_PORT_ANY) ||
9300           !IN_LEV_FIELD(nextx, nexty) ||
9301           !IS_FREE(nextx, nexty))
9302         return MF_NO_ACTION;
9303 #endif
9304
9305       if (element == EL_SP_GRAVITY_PORT_LEFT ||
9306           element == EL_SP_GRAVITY_PORT_RIGHT ||
9307           element == EL_SP_GRAVITY_PORT_UP ||
9308           element == EL_SP_GRAVITY_PORT_DOWN)
9309         game.gravity = !game.gravity;
9310
9311       /* automatically move to the next field with double speed */
9312       player->programmed_action = move_direction;
9313 #if 1
9314       if (player->move_delay_reset_counter == 0)
9315       {
9316         player->move_delay_reset_counter = 2;   /* two double speed steps */
9317
9318         DOUBLE_PLAYER_SPEED(player);
9319       }
9320 #else
9321       player->move_delay_reset_counter = 2;
9322
9323       DOUBLE_PLAYER_SPEED(player);
9324 #endif
9325
9326       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9327       break;
9328
9329 #if 0
9330     case EL_TUBE_ANY:
9331     case EL_TUBE_VERTICAL:
9332     case EL_TUBE_HORIZONTAL:
9333     case EL_TUBE_VERTICAL_LEFT:
9334     case EL_TUBE_VERTICAL_RIGHT:
9335     case EL_TUBE_HORIZONTAL_UP:
9336     case EL_TUBE_HORIZONTAL_DOWN:
9337     case EL_TUBE_LEFT_UP:
9338     case EL_TUBE_LEFT_DOWN:
9339     case EL_TUBE_RIGHT_UP:
9340     case EL_TUBE_RIGHT_DOWN:
9341       {
9342         int i = 0;
9343         int tube_enter_directions[][2] =
9344         {
9345           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9346           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
9347           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
9348           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
9349           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
9350           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
9351           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
9352           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
9353           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
9354           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
9355           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
9356           { -1,                         MV_NO_MOVING                         }
9357         };
9358
9359         while (tube_enter_directions[i][0] != element)
9360         {
9361           i++;
9362           if (tube_enter_directions[i][0] == -1)        /* should not happen */
9363             break;
9364         }
9365
9366         if (!(tube_enter_directions[i][1] & move_direction))
9367           return MF_NO_ACTION;  /* tube has no opening in this direction */
9368
9369         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9370       }
9371       break;
9372 #endif
9373
9374     default:
9375
9376       if (IS_WALKABLE(element))
9377       {
9378         int sound_action = ACTION_WALKING;
9379
9380         if (!(element_info[element].access_direction & opposite_direction))
9381           return MF_NO_ACTION;  /* field not accessible from this direction */
9382
9383         if (element >= EL_GATE_1 && element <= EL_GATE_4)
9384         {
9385           if (!player->key[element - EL_GATE_1])
9386             return MF_NO_ACTION;
9387         }
9388         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9389         {
9390           if (!player->key[element - EL_GATE_1_GRAY])
9391             return MF_NO_ACTION;
9392         }
9393         else if (element == EL_EXIT_OPEN ||
9394                  element == EL_SP_EXIT_OPEN ||
9395                  element == EL_SP_EXIT_OPENING)
9396         {
9397           sound_action = ACTION_PASSING;        /* player is passing exit */
9398         }
9399         else if (element == EL_EMPTY)
9400         {
9401           sound_action = ACTION_MOVING;         /* nothing to walk on */
9402         }
9403
9404         /* play sound from background or player, whatever is available */
9405         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9406           PlayLevelSoundElementAction(x, y, element, sound_action);
9407         else
9408           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9409
9410         break;
9411       }
9412       else if (IS_PASSABLE(element))
9413       {
9414         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9415           return MF_NO_ACTION;
9416
9417         if (IS_CUSTOM_ELEMENT(element) &&
9418             !(element_info[element].access_direction & opposite_direction))
9419           return MF_NO_ACTION;  /* field not accessible from this direction */
9420
9421 #if 1
9422         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
9423           return MF_NO_ACTION;
9424 #endif
9425
9426         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9427         {
9428           if (!player->key[element - EL_EM_GATE_1])
9429             return MF_NO_ACTION;
9430         }
9431         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9432         {
9433           if (!player->key[element - EL_EM_GATE_1_GRAY])
9434             return MF_NO_ACTION;
9435         }
9436
9437         /* automatically move to the next field with double speed */
9438         player->programmed_action = move_direction;
9439 #if 1
9440         if (player->move_delay_reset_counter == 0)
9441         {
9442           player->move_delay_reset_counter = 2; /* two double speed steps */
9443
9444           DOUBLE_PLAYER_SPEED(player);
9445         }
9446 #else
9447         player->move_delay_reset_counter = 2;
9448
9449         DOUBLE_PLAYER_SPEED(player);
9450 #endif
9451
9452         PlayLevelSoundAction(x, y, ACTION_PASSING);
9453
9454         break;
9455       }
9456       else if (IS_DIGGABLE(element))
9457       {
9458         RemoveField(x, y);
9459
9460         if (mode != DF_SNAP)
9461         {
9462 #if 1
9463           GfxElement[x][y] = GFX_ELEMENT(element);
9464 #else
9465           GfxElement[x][y] =
9466             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9467 #endif
9468           player->is_digging = TRUE;
9469         }
9470
9471         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9472
9473         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9474
9475 #if 1
9476         if (mode == DF_SNAP)
9477           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9478 #endif
9479
9480         break;
9481       }
9482       else if (IS_COLLECTIBLE(element))
9483       {
9484         RemoveField(x, y);
9485
9486         if (mode != DF_SNAP)
9487         {
9488           GfxElement[x][y] = element;
9489           player->is_collecting = TRUE;
9490         }
9491
9492         if (element == EL_SPEED_PILL)
9493           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9494         else if (element == EL_EXTRA_TIME && level.time > 0)
9495         {
9496           TimeLeft += 10;
9497           DrawGameValue_Time(TimeLeft);
9498         }
9499         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9500         {
9501           player->shield_normal_time_left += 10;
9502           if (element == EL_SHIELD_DEADLY)
9503             player->shield_deadly_time_left += 10;
9504         }
9505         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9506         {
9507           if (player->inventory_size < MAX_INVENTORY_SIZE)
9508             player->inventory_element[player->inventory_size++] = element;
9509
9510           DrawGameValue_Dynamite(local_player->inventory_size);
9511         }
9512         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9513         {
9514           player->dynabomb_count++;
9515           player->dynabombs_left++;
9516         }
9517         else if (element == EL_DYNABOMB_INCREASE_SIZE)
9518         {
9519           player->dynabomb_size++;
9520         }
9521         else if (element == EL_DYNABOMB_INCREASE_POWER)
9522         {
9523           player->dynabomb_xl = TRUE;
9524         }
9525         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9526                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9527         {
9528           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9529                         element - EL_KEY_1 : element - EL_EM_KEY_1);
9530
9531           player->key[key_nr] = TRUE;
9532
9533           DrawGameValue_Keys(player);
9534
9535           redraw_mask |= REDRAW_DOOR_1;
9536         }
9537         else if (IS_ENVELOPE(element))
9538         {
9539 #if 1
9540           player->show_envelope = element;
9541 #else
9542           ShowEnvelope(element - EL_ENVELOPE_1);
9543 #endif
9544         }
9545         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9546         {
9547           int i;
9548
9549           for (i = 0; i < element_info[element].collect_count; i++)
9550             if (player->inventory_size < MAX_INVENTORY_SIZE)
9551               player->inventory_element[player->inventory_size++] = element;
9552
9553           DrawGameValue_Dynamite(local_player->inventory_size);
9554         }
9555         else if (element_info[element].collect_count > 0)
9556         {
9557           local_player->gems_still_needed -=
9558             element_info[element].collect_count;
9559           if (local_player->gems_still_needed < 0)
9560             local_player->gems_still_needed = 0;
9561
9562           DrawGameValue_Emeralds(local_player->gems_still_needed);
9563         }
9564
9565         RaiseScoreElement(element);
9566         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9567
9568         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9569
9570 #if 1
9571         if (mode == DF_SNAP)
9572           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9573 #endif
9574
9575         break;
9576       }
9577       else if (IS_PUSHABLE(element))
9578       {
9579         if (mode == DF_SNAP && element != EL_BD_ROCK)
9580           return MF_NO_ACTION;
9581
9582         if (CAN_FALL(element) && dy)
9583           return MF_NO_ACTION;
9584
9585         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9586             !(element == EL_SPRING && level.use_spring_bug))
9587           return MF_NO_ACTION;
9588
9589 #if 1
9590         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9591             ((move_direction & MV_VERTICAL &&
9592               ((element_info[element].move_pattern & MV_LEFT &&
9593                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9594                (element_info[element].move_pattern & MV_RIGHT &&
9595                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9596              (move_direction & MV_HORIZONTAL &&
9597               ((element_info[element].move_pattern & MV_UP &&
9598                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9599                (element_info[element].move_pattern & MV_DOWN &&
9600                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9601           return MF_NO_ACTION;
9602 #endif
9603
9604 #if 1
9605         /* do not push elements already moving away faster than player */
9606         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9607             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9608           return MF_NO_ACTION;
9609 #else
9610         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9611           return MF_NO_ACTION;
9612 #endif
9613
9614 #if 1
9615         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9616         {
9617           if (player->push_delay_value == -1)
9618             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9619         }
9620         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9621         {
9622           if (!player->is_pushing)
9623             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9624         }
9625
9626         /*
9627         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9628             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9629              !player_is_pushing))
9630           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9631         */
9632 #else
9633         if (!player->is_pushing &&
9634             game.engine_version >= VERSION_IDENT(2,2,0,7))
9635           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9636 #endif
9637
9638 #if 0
9639         printf("::: push delay: %ld [%d, %d] [%d]\n",
9640                player->push_delay_value, FrameCounter, game.engine_version,
9641                player->is_pushing);
9642 #endif
9643
9644         player->is_pushing = TRUE;
9645
9646         if (!(IN_LEV_FIELD(nextx, nexty) &&
9647               (IS_FREE(nextx, nexty) ||
9648                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9649                 IS_SB_ELEMENT(element)))))
9650           return MF_NO_ACTION;
9651
9652         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9653           return MF_NO_ACTION;
9654
9655         if (player->push_delay == 0)    /* new pushing; restart delay */
9656           player->push_delay = FrameCounter;
9657
9658         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9659             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9660             element != EL_SPRING && element != EL_BALLOON)
9661         {
9662           /* make sure that there is no move delay before next try to push */
9663           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9664             player->move_delay = INITIAL_MOVE_DELAY_OFF;
9665
9666           return MF_NO_ACTION;
9667         }
9668
9669 #if 0
9670         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9671 #endif
9672
9673         if (IS_SB_ELEMENT(element))
9674         {
9675           if (element == EL_SOKOBAN_FIELD_FULL)
9676           {
9677             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9678             local_player->sokobanfields_still_needed++;
9679           }
9680
9681           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9682           {
9683             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9684             local_player->sokobanfields_still_needed--;
9685           }
9686
9687           Feld[x][y] = EL_SOKOBAN_OBJECT;
9688
9689           if (Back[x][y] == Back[nextx][nexty])
9690             PlayLevelSoundAction(x, y, ACTION_PUSHING);
9691           else if (Back[x][y] != 0)
9692             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9693                                         ACTION_EMPTYING);
9694           else
9695             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9696                                         ACTION_FILLING);
9697
9698           if (local_player->sokobanfields_still_needed == 0 &&
9699               game.emulation == EMU_SOKOBAN)
9700           {
9701             player->LevelSolved = player->GameOver = TRUE;
9702             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9703           }
9704         }
9705         else
9706           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9707
9708         InitMovingField(x, y, move_direction);
9709         GfxAction[x][y] = ACTION_PUSHING;
9710
9711         if (mode == DF_SNAP)
9712           ContinueMoving(x, y);
9713         else
9714           MovPos[x][y] = (dx != 0 ? dx : dy);
9715
9716         Pushed[x][y] = TRUE;
9717         Pushed[nextx][nexty] = TRUE;
9718
9719         if (game.engine_version < VERSION_IDENT(2,2,0,7))
9720           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9721         else
9722           player->push_delay_value = -1;        /* get new value later */
9723
9724         CheckTriggeredElementSideChange(x, y, element, dig_side,
9725                                         CE_OTHER_GETS_PUSHED);
9726         CheckElementSideChange(x, y, element, dig_side,
9727                                CE_PUSHED_BY_PLAYER, -1);
9728
9729         break;
9730       }
9731       else if (IS_SWITCHABLE(element))
9732       {
9733         if (PLAYER_SWITCHING(player, x, y))
9734           return MF_ACTION;
9735
9736         player->is_switching = TRUE;
9737         player->switch_x = x;
9738         player->switch_y = y;
9739
9740         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9741
9742         if (element == EL_ROBOT_WHEEL)
9743         {
9744           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9745           ZX = x;
9746           ZY = y;
9747
9748           DrawLevelField(x, y);
9749         }
9750         else if (element == EL_SP_TERMINAL)
9751         {
9752           int xx, yy;
9753
9754           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9755           {
9756             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9757               Bang(xx, yy);
9758             else if (Feld[xx][yy] == EL_SP_TERMINAL)
9759               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9760           }
9761         }
9762         else if (IS_BELT_SWITCH(element))
9763         {
9764           ToggleBeltSwitch(x, y);
9765         }
9766         else if (element == EL_SWITCHGATE_SWITCH_UP ||
9767                  element == EL_SWITCHGATE_SWITCH_DOWN)
9768         {
9769           ToggleSwitchgateSwitch(x, y);
9770         }
9771         else if (element == EL_LIGHT_SWITCH ||
9772                  element == EL_LIGHT_SWITCH_ACTIVE)
9773         {
9774           ToggleLightSwitch(x, y);
9775
9776 #if 0
9777           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9778                          SND_LIGHT_SWITCH_ACTIVATING :
9779                          SND_LIGHT_SWITCH_DEACTIVATING);
9780 #endif
9781         }
9782         else if (element == EL_TIMEGATE_SWITCH)
9783         {
9784           ActivateTimegateSwitch(x, y);
9785         }
9786         else if (element == EL_BALLOON_SWITCH_LEFT ||
9787                  element == EL_BALLOON_SWITCH_RIGHT ||
9788                  element == EL_BALLOON_SWITCH_UP ||
9789                  element == EL_BALLOON_SWITCH_DOWN ||
9790                  element == EL_BALLOON_SWITCH_ANY)
9791         {
9792           if (element == EL_BALLOON_SWITCH_ANY)
9793             game.balloon_dir = move_direction;
9794           else
9795             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
9796                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9797                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
9798                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
9799                                 MV_NO_MOVING);
9800         }
9801         else if (element == EL_LAMP)
9802         {
9803           Feld[x][y] = EL_LAMP_ACTIVE;
9804           local_player->lights_still_needed--;
9805
9806           DrawLevelField(x, y);
9807         }
9808         else if (element == EL_TIME_ORB_FULL)
9809         {
9810           Feld[x][y] = EL_TIME_ORB_EMPTY;
9811           TimeLeft += 10;
9812           DrawGameValue_Time(TimeLeft);
9813
9814           DrawLevelField(x, y);
9815
9816 #if 0
9817           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9818 #endif
9819         }
9820
9821         return MF_ACTION;
9822       }
9823       else
9824       {
9825         if (!PLAYER_SWITCHING(player, x, y))
9826         {
9827           player->is_switching = TRUE;
9828           player->switch_x = x;
9829           player->switch_y = y;
9830
9831           CheckTriggeredElementSideChange(x, y, element, dig_side,
9832                                           CE_OTHER_IS_SWITCHING);
9833           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9834         }
9835
9836         CheckTriggeredElementSideChange(x, y, element, dig_side,
9837                                         CE_OTHER_GETS_PRESSED);
9838         CheckElementSideChange(x, y, element, dig_side,
9839                                CE_PRESSED_BY_PLAYER, -1);
9840       }
9841
9842       return MF_NO_ACTION;
9843   }
9844
9845   player->push_delay = 0;
9846
9847   if (Feld[x][y] != element)            /* really digged/collected something */
9848     player->is_collecting = !player->is_digging;
9849
9850   return MF_MOVING;
9851 }
9852
9853 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9854 {
9855   int jx = player->jx, jy = player->jy;
9856   int x = jx + dx, y = jy + dy;
9857   int snap_direction = (dx == -1 ? MV_LEFT :
9858                         dx == +1 ? MV_RIGHT :
9859                         dy == -1 ? MV_UP :
9860                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9861
9862   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9863     return FALSE;
9864
9865   if (!player->active || !IN_LEV_FIELD(x, y))
9866     return FALSE;
9867
9868   if (dx && dy)
9869     return FALSE;
9870
9871   if (!dx && !dy)
9872   {
9873     if (player->MovPos == 0)
9874       player->is_pushing = FALSE;
9875
9876     player->is_snapping = FALSE;
9877
9878     if (player->MovPos == 0)
9879     {
9880       player->is_moving = FALSE;
9881       player->is_digging = FALSE;
9882       player->is_collecting = FALSE;
9883     }
9884
9885     return FALSE;
9886   }
9887
9888   if (player->is_snapping)
9889     return FALSE;
9890
9891   player->MovDir = snap_direction;
9892
9893 #if 1
9894   if (player->MovPos == 0)
9895 #endif
9896   {
9897     player->is_moving = FALSE;
9898     player->is_digging = FALSE;
9899     player->is_collecting = FALSE;
9900   }
9901
9902   player->is_dropping = FALSE;
9903
9904   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9905     return FALSE;
9906
9907   player->is_snapping = TRUE;
9908
9909 #if 1
9910   if (player->MovPos == 0)
9911 #endif
9912   {
9913     player->is_moving = FALSE;
9914     player->is_digging = FALSE;
9915     player->is_collecting = FALSE;
9916   }
9917
9918   DrawLevelField(x, y);
9919   BackToFront();
9920
9921   return TRUE;
9922 }
9923
9924 boolean DropElement(struct PlayerInfo *player)
9925 {
9926   int jx = player->jx, jy = player->jy;
9927   int old_element = Feld[jx][jy];
9928   int new_element;
9929
9930   /* check if player is active, not moving and ready to drop */
9931   if (!player->active || player->MovPos || player->drop_delay > 0)
9932     return FALSE;
9933
9934   /* check if player has anything that can be dropped */
9935   if (player->inventory_size == 0 && player->dynabombs_left == 0)
9936     return FALSE;
9937
9938   /* check if anything can be dropped at the current position */
9939   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9940     return FALSE;
9941
9942   /* collected custom elements can only be dropped on empty fields */
9943   if (player->inventory_size > 0 &&
9944       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9945       && old_element != EL_EMPTY)
9946     return FALSE;
9947
9948   if (old_element != EL_EMPTY)
9949     Back[jx][jy] = old_element;         /* store old element on this field */
9950
9951   ResetGfxAnimation(jx, jy);
9952   ResetRandomAnimationValue(jx, jy);
9953
9954   if (player->inventory_size > 0)
9955   {
9956     player->inventory_size--;
9957     new_element = player->inventory_element[player->inventory_size];
9958
9959     if (new_element == EL_DYNAMITE)
9960       new_element = EL_DYNAMITE_ACTIVE;
9961     else if (new_element == EL_SP_DISK_RED)
9962       new_element = EL_SP_DISK_RED_ACTIVE;
9963
9964     Feld[jx][jy] = new_element;
9965
9966     DrawGameValue_Dynamite(local_player->inventory_size);
9967
9968     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9969       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9970
9971     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9972
9973 #if 1
9974     /* needed if previous element just changed to "empty" in the last frame */
9975     Changed[jx][jy] = 0;                /* allow another change */
9976 #endif
9977
9978     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9979     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9980
9981     TestIfElementTouchesCustomElement(jx, jy);
9982   }
9983   else          /* player is dropping a dyna bomb */
9984   {
9985     player->dynabombs_left--;
9986     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9987
9988     Feld[jx][jy] = new_element;
9989
9990     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9991       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9992
9993     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9994   }
9995
9996
9997
9998 #if 1
9999
10000   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
10001   {
10002 #if 1
10003     InitField_WithBug1(jx, jy, FALSE);
10004 #else
10005     InitField(jx, jy, FALSE);
10006     if (CAN_MOVE(Feld[jx][jy]))
10007       InitMovDir(jx, jy);
10008 #endif
10009   }
10010
10011   new_element = Feld[jx][jy];
10012
10013   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10014       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10015   {
10016     int move_stepsize = element_info[new_element].move_stepsize;
10017     int direction, dx, dy, nextx, nexty;
10018
10019     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10020       MovDir[jx][jy] = player->MovDir;
10021
10022     direction = MovDir[jx][jy];
10023     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10024     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10025     nextx = jx + dx;
10026     nexty = jy + dy;
10027
10028     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10029     {
10030 #if 0
10031       WasJustMoving[jx][jy] = 3;
10032 #else
10033       InitMovingField(jx, jy, direction);
10034       ContinueMoving(jx, jy);
10035 #endif
10036     }
10037     else
10038     {
10039       Changed[jx][jy] = 0;              /* allow another change */
10040
10041 #if 1
10042       TestIfElementHitsCustomElement(jx, jy, direction);
10043 #else
10044       CheckElementSideChange(jx, jy, new_element,
10045                              direction, CE_HITTING_SOMETHING, -1);
10046 #endif
10047     }
10048
10049     player->drop_delay = 2 * TILEX / move_stepsize + 1;
10050   }
10051
10052 #if 0
10053   player->drop_delay = 8 + 8 + 8;
10054 #endif
10055
10056 #endif
10057
10058   player->is_dropping = TRUE;
10059
10060
10061   return TRUE;
10062 }
10063
10064 /* ------------------------------------------------------------------------- */
10065 /* game sound playing functions                                              */
10066 /* ------------------------------------------------------------------------- */
10067
10068 static int *loop_sound_frame = NULL;
10069 static int *loop_sound_volume = NULL;
10070
10071 void InitPlayLevelSound()
10072 {
10073   int num_sounds = getSoundListSize();
10074
10075   checked_free(loop_sound_frame);
10076   checked_free(loop_sound_volume);
10077
10078   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
10079   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10080 }
10081
10082 static void PlayLevelSound(int x, int y, int nr)
10083 {
10084   int sx = SCREENX(x), sy = SCREENY(y);
10085   int volume, stereo_position;
10086   int max_distance = 8;
10087   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10088
10089   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10090       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10091     return;
10092
10093   if (!IN_LEV_FIELD(x, y) ||
10094       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10095       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10096     return;
10097
10098   volume = SOUND_MAX_VOLUME;
10099
10100   if (!IN_SCR_FIELD(sx, sy))
10101   {
10102     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10103     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10104
10105     volume -= volume * (dx > dy ? dx : dy) / max_distance;
10106   }
10107
10108   stereo_position = (SOUND_MAX_LEFT +
10109                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10110                      (SCR_FIELDX + 2 * max_distance));
10111
10112   if (IS_LOOP_SOUND(nr))
10113   {
10114     /* This assures that quieter loop sounds do not overwrite louder ones,
10115        while restarting sound volume comparison with each new game frame. */
10116
10117     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10118       return;
10119
10120     loop_sound_volume[nr] = volume;
10121     loop_sound_frame[nr] = FrameCounter;
10122   }
10123
10124   PlaySoundExt(nr, volume, stereo_position, type);
10125 }
10126
10127 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10128 {
10129   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10130                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
10131                  y < LEVELY(BY1) ? LEVELY(BY1) :
10132                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
10133                  sound_action);
10134 }
10135
10136 static void PlayLevelSoundAction(int x, int y, int action)
10137 {
10138   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10139 }
10140
10141 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10142 {
10143   int sound_effect = element_info[element].sound[action];
10144
10145   if (sound_effect != SND_UNDEFINED)
10146     PlayLevelSound(x, y, sound_effect);
10147 }
10148
10149 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10150                                               int action)
10151 {
10152   int sound_effect = element_info[element].sound[action];
10153
10154   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10155     PlayLevelSound(x, y, sound_effect);
10156 }
10157
10158 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10159 {
10160   int sound_effect = element_info[Feld[x][y]].sound[action];
10161
10162   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10163     PlayLevelSound(x, y, sound_effect);
10164 }
10165
10166 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10167 {
10168   int sound_effect = element_info[Feld[x][y]].sound[action];
10169
10170   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10171     StopSound(sound_effect);
10172 }
10173
10174 static void PlayLevelMusic()
10175 {
10176   if (levelset.music[level_nr] != MUS_UNDEFINED)
10177     PlayMusic(levelset.music[level_nr]);        /* from config file */
10178   else
10179     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
10180 }
10181
10182 void RaiseScore(int value)
10183 {
10184   local_player->score += value;
10185
10186   DrawGameValue_Score(local_player->score);
10187 }
10188
10189 void RaiseScoreElement(int element)
10190 {
10191   switch(element)
10192   {
10193     case EL_EMERALD:
10194     case EL_BD_DIAMOND:
10195     case EL_EMERALD_YELLOW:
10196     case EL_EMERALD_RED:
10197     case EL_EMERALD_PURPLE:
10198     case EL_SP_INFOTRON:
10199       RaiseScore(level.score[SC_EMERALD]);
10200       break;
10201     case EL_DIAMOND:
10202       RaiseScore(level.score[SC_DIAMOND]);
10203       break;
10204     case EL_CRYSTAL:
10205       RaiseScore(level.score[SC_CRYSTAL]);
10206       break;
10207     case EL_PEARL:
10208       RaiseScore(level.score[SC_PEARL]);
10209       break;
10210     case EL_BUG:
10211     case EL_BD_BUTTERFLY:
10212     case EL_SP_ELECTRON:
10213       RaiseScore(level.score[SC_BUG]);
10214       break;
10215     case EL_SPACESHIP:
10216     case EL_BD_FIREFLY:
10217     case EL_SP_SNIKSNAK:
10218       RaiseScore(level.score[SC_SPACESHIP]);
10219       break;
10220     case EL_YAMYAM:
10221     case EL_DARK_YAMYAM:
10222       RaiseScore(level.score[SC_YAMYAM]);
10223       break;
10224     case EL_ROBOT:
10225       RaiseScore(level.score[SC_ROBOT]);
10226       break;
10227     case EL_PACMAN:
10228       RaiseScore(level.score[SC_PACMAN]);
10229       break;
10230     case EL_NUT:
10231       RaiseScore(level.score[SC_NUT]);
10232       break;
10233     case EL_DYNAMITE:
10234     case EL_SP_DISK_RED:
10235     case EL_DYNABOMB_INCREASE_NUMBER:
10236     case EL_DYNABOMB_INCREASE_SIZE:
10237     case EL_DYNABOMB_INCREASE_POWER:
10238       RaiseScore(level.score[SC_DYNAMITE]);
10239       break;
10240     case EL_SHIELD_NORMAL:
10241     case EL_SHIELD_DEADLY:
10242       RaiseScore(level.score[SC_SHIELD]);
10243       break;
10244     case EL_EXTRA_TIME:
10245       RaiseScore(level.score[SC_TIME_BONUS]);
10246       break;
10247     case EL_KEY_1:
10248     case EL_KEY_2:
10249     case EL_KEY_3:
10250     case EL_KEY_4:
10251       RaiseScore(level.score[SC_KEY]);
10252       break;
10253     default:
10254       RaiseScore(element_info[element].collect_score);
10255       break;
10256   }
10257 }
10258
10259 void RequestQuitGame(boolean ask_if_really_quit)
10260 {
10261   if (AllPlayersGone ||
10262       !ask_if_really_quit ||
10263       level_editor_test_game ||
10264       Request("Do you really want to quit the game ?",
10265               REQ_ASK | REQ_STAY_CLOSED))
10266   {
10267 #if defined(PLATFORM_UNIX)
10268     if (options.network)
10269       SendToServer_StopPlaying();
10270     else
10271 #endif
10272     {
10273       game_status = GAME_MODE_MAIN;
10274       DrawMainMenu();
10275     }
10276   }
10277   else
10278   {
10279     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10280   }
10281 }
10282
10283
10284 /* ---------- new game button stuff ---------------------------------------- */
10285
10286 /* graphic position values for game buttons */
10287 #define GAME_BUTTON_XSIZE       30
10288 #define GAME_BUTTON_YSIZE       30
10289 #define GAME_BUTTON_XPOS        5
10290 #define GAME_BUTTON_YPOS        215
10291 #define SOUND_BUTTON_XPOS       5
10292 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10293
10294 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10295 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10296 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10297 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10298 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10299 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10300
10301 static struct
10302 {
10303   int x, y;
10304   int gadget_id;
10305   char *infotext;
10306 } gamebutton_info[NUM_GAME_BUTTONS] =
10307 {
10308   {
10309     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
10310     GAME_CTRL_ID_STOP,
10311     "stop game"
10312   },
10313   {
10314     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
10315     GAME_CTRL_ID_PAUSE,
10316     "pause game"
10317   },
10318   {
10319     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
10320     GAME_CTRL_ID_PLAY,
10321     "play game"
10322   },
10323   {
10324     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
10325     SOUND_CTRL_ID_MUSIC,
10326     "background music on/off"
10327   },
10328   {
10329     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
10330     SOUND_CTRL_ID_LOOPS,
10331     "sound loops on/off"
10332   },
10333   {
10334     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
10335     SOUND_CTRL_ID_SIMPLE,
10336     "normal sounds on/off"
10337   }
10338 };
10339
10340 void CreateGameButtons()
10341 {
10342   int i;
10343
10344   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10345   {
10346     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10347     struct GadgetInfo *gi;
10348     int button_type;
10349     boolean checked;
10350     unsigned long event_mask;
10351     int gd_xoffset, gd_yoffset;
10352     int gd_x1, gd_x2, gd_y1, gd_y2;
10353     int id = i;
10354
10355     gd_xoffset = gamebutton_info[i].x;
10356     gd_yoffset = gamebutton_info[i].y;
10357     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10358     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10359
10360     if (id == GAME_CTRL_ID_STOP ||
10361         id == GAME_CTRL_ID_PAUSE ||
10362         id == GAME_CTRL_ID_PLAY)
10363     {
10364       button_type = GD_TYPE_NORMAL_BUTTON;
10365       checked = FALSE;
10366       event_mask = GD_EVENT_RELEASED;
10367       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10368       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10369     }
10370     else
10371     {
10372       button_type = GD_TYPE_CHECK_BUTTON;
10373       checked =
10374         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10375          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10376          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10377       event_mask = GD_EVENT_PRESSED;
10378       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
10379       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10380     }
10381
10382     gi = CreateGadget(GDI_CUSTOM_ID, id,
10383                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
10384                       GDI_X, DX + gd_xoffset,
10385                       GDI_Y, DY + gd_yoffset,
10386                       GDI_WIDTH, GAME_BUTTON_XSIZE,
10387                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
10388                       GDI_TYPE, button_type,
10389                       GDI_STATE, GD_BUTTON_UNPRESSED,
10390                       GDI_CHECKED, checked,
10391                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10392                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10393                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10394                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10395                       GDI_EVENT_MASK, event_mask,
10396                       GDI_CALLBACK_ACTION, HandleGameButtons,
10397                       GDI_END);
10398
10399     if (gi == NULL)
10400       Error(ERR_EXIT, "cannot create gadget");
10401
10402     game_gadget[id] = gi;
10403   }
10404 }
10405
10406 void FreeGameButtons()
10407 {
10408   int i;
10409
10410   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10411     FreeGadget(game_gadget[i]);
10412 }
10413
10414 static void MapGameButtons()
10415 {
10416   int i;
10417
10418   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10419     MapGadget(game_gadget[i]);
10420 }
10421
10422 void UnmapGameButtons()
10423 {
10424   int i;
10425
10426   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10427     UnmapGadget(game_gadget[i]);
10428 }
10429
10430 static void HandleGameButtons(struct GadgetInfo *gi)
10431 {
10432   int id = gi->custom_id;
10433
10434   if (game_status != GAME_MODE_PLAYING)
10435     return;
10436
10437   switch (id)
10438   {
10439     case GAME_CTRL_ID_STOP:
10440       RequestQuitGame(TRUE);
10441       break;
10442
10443     case GAME_CTRL_ID_PAUSE:
10444       if (options.network)
10445       {
10446 #if defined(PLATFORM_UNIX)
10447         if (tape.pausing)
10448           SendToServer_ContinuePlaying();
10449         else
10450           SendToServer_PausePlaying();
10451 #endif
10452       }
10453       else
10454         TapeTogglePause(TAPE_TOGGLE_MANUAL);
10455       break;
10456
10457     case GAME_CTRL_ID_PLAY:
10458       if (tape.pausing)
10459       {
10460 #if defined(PLATFORM_UNIX)
10461         if (options.network)
10462           SendToServer_ContinuePlaying();
10463         else
10464 #endif
10465         {
10466           tape.pausing = FALSE;
10467           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10468         }
10469       }
10470       break;
10471
10472     case SOUND_CTRL_ID_MUSIC:
10473       if (setup.sound_music)
10474       { 
10475         setup.sound_music = FALSE;
10476         FadeMusic();
10477       }
10478       else if (audio.music_available)
10479       { 
10480         setup.sound = setup.sound_music = TRUE;
10481
10482         SetAudioMode(setup.sound);
10483
10484         PlayLevelMusic();
10485       }
10486       break;
10487
10488     case SOUND_CTRL_ID_LOOPS:
10489       if (setup.sound_loops)
10490         setup.sound_loops = FALSE;
10491       else if (audio.loops_available)
10492       {
10493         setup.sound = setup.sound_loops = TRUE;
10494         SetAudioMode(setup.sound);
10495       }
10496       break;
10497
10498     case SOUND_CTRL_ID_SIMPLE:
10499       if (setup.sound_simple)
10500         setup.sound_simple = FALSE;
10501       else if (audio.sound_available)
10502       {
10503         setup.sound = setup.sound_simple = TRUE;
10504         SetAudioMode(setup.sound);
10505       }
10506       break;
10507
10508     default:
10509       break;
10510   }
10511 }