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