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