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