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