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