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