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