rnd-20040409-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 CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
317         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
318 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
319         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
320 #define CheckTriggeredElementChangeByPage(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 CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
328         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
329 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
330         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
331 #define CheckElementChangeByPage(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 #if 1
4109           /* !!! TEST ONLY !!! */
4110           CheckElementChangeBySide(x, y + 1, smashed, element,
4111                                    CE_SWITCHED, CH_SIDE_TOP);
4112           CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4113                                             CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4114 #else
4115           CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4116                                             CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4117           CheckElementChangeBySide(x, y + 1, smashed, element,
4118                                    CE_SWITCHED, CH_SIDE_TOP);
4119 #endif
4120         }
4121       }
4122       else
4123       {
4124         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4125       }
4126     }
4127   }
4128
4129   /* play sound of magic wall / mill */
4130   if (!lastline &&
4131       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4132        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4133   {
4134     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4135       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4136     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4137       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4138
4139     return;
4140   }
4141
4142   /* play sound of object that hits the ground */
4143   if (lastline || object_hit)
4144     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4145 }
4146
4147 inline static void TurnRoundExt(int x, int y)
4148 {
4149   static struct
4150   {
4151     int x, y;
4152   } move_xy[] =
4153   {
4154     {  0,  0 },
4155     { -1,  0 },
4156     { +1,  0 },
4157     {  0,  0 },
4158     {  0, -1 },
4159     {  0,  0 }, { 0, 0 }, { 0, 0 },
4160     {  0, +1 }
4161   };
4162   static struct
4163   {
4164     int left, right, back;
4165   } turn[] =
4166   {
4167     { 0,        0,              0        },
4168     { MV_DOWN,  MV_UP,          MV_RIGHT },
4169     { MV_UP,    MV_DOWN,        MV_LEFT  },
4170     { 0,        0,              0        },
4171     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
4172     { 0,        0,              0        },
4173     { 0,        0,              0        },
4174     { 0,        0,              0        },
4175     { MV_RIGHT, MV_LEFT,        MV_UP    }
4176   };
4177
4178   int element = Feld[x][y];
4179   int move_pattern = element_info[element].move_pattern;
4180
4181   int old_move_dir = MovDir[x][y];
4182   int left_dir  = turn[old_move_dir].left;
4183   int right_dir = turn[old_move_dir].right;
4184   int back_dir  = turn[old_move_dir].back;
4185
4186   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
4187   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
4188   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
4189   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
4190
4191   int left_x  = x + left_dx,  left_y  = y + left_dy;
4192   int right_x = x + right_dx, right_y = y + right_dy;
4193   int move_x  = x + move_dx,  move_y  = y + move_dy;
4194
4195   int xx, yy;
4196
4197   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4198   {
4199     TestIfBadThingTouchesOtherBadThing(x, y);
4200
4201     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4202       MovDir[x][y] = right_dir;
4203     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4204       MovDir[x][y] = left_dir;
4205
4206     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4207       MovDelay[x][y] = 9;
4208     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
4209       MovDelay[x][y] = 1;
4210   }
4211 #if 0
4212   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4213            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4214   {
4215     TestIfBadThingTouchesOtherBadThing(x, y);
4216
4217     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4218       MovDir[x][y] = left_dir;
4219     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4220       MovDir[x][y] = right_dir;
4221
4222     if ((element == EL_SPACESHIP ||
4223          element == EL_SP_SNIKSNAK ||
4224          element == EL_SP_ELECTRON)
4225         && MovDir[x][y] != old_move_dir)
4226       MovDelay[x][y] = 9;
4227     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4228       MovDelay[x][y] = 1;
4229   }
4230 #else
4231   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4232   {
4233     TestIfBadThingTouchesOtherBadThing(x, y);
4234
4235     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4236       MovDir[x][y] = left_dir;
4237     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4238       MovDir[x][y] = right_dir;
4239
4240     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4241       MovDelay[x][y] = 9;
4242     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4243       MovDelay[x][y] = 1;
4244   }
4245   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4246   {
4247     TestIfBadThingTouchesOtherBadThing(x, y);
4248
4249     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4250       MovDir[x][y] = left_dir;
4251     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4252       MovDir[x][y] = right_dir;
4253
4254     if (MovDir[x][y] != old_move_dir)
4255       MovDelay[x][y] = 9;
4256   }
4257 #endif
4258   else if (element == EL_YAMYAM)
4259   {
4260     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4261     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4262
4263     if (can_turn_left && can_turn_right)
4264       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4265     else if (can_turn_left)
4266       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4267     else if (can_turn_right)
4268       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4269     else
4270       MovDir[x][y] = back_dir;
4271
4272     MovDelay[x][y] = 16 + 16 * RND(3);
4273   }
4274   else if (element == EL_DARK_YAMYAM)
4275   {
4276     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4277                                                          left_x, left_y);
4278     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4279                                                          right_x, right_y);
4280
4281     if (can_turn_left && can_turn_right)
4282       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4283     else if (can_turn_left)
4284       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4285     else if (can_turn_right)
4286       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4287     else
4288       MovDir[x][y] = back_dir;
4289
4290     MovDelay[x][y] = 16 + 16 * RND(3);
4291   }
4292   else if (element == EL_PACMAN)
4293   {
4294     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4295     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4296
4297     if (can_turn_left && can_turn_right)
4298       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4299     else if (can_turn_left)
4300       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4301     else if (can_turn_right)
4302       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4303     else
4304       MovDir[x][y] = back_dir;
4305
4306     MovDelay[x][y] = 6 + RND(40);
4307   }
4308   else if (element == EL_PIG)
4309   {
4310     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4311     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4312     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4313     boolean should_turn_left, should_turn_right, should_move_on;
4314     int rnd_value = 24;
4315     int rnd = RND(rnd_value);
4316
4317     should_turn_left = (can_turn_left &&
4318                         (!can_move_on ||
4319                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4320                                                    y + back_dy + left_dy)));
4321     should_turn_right = (can_turn_right &&
4322                          (!can_move_on ||
4323                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4324                                                     y + back_dy + right_dy)));
4325     should_move_on = (can_move_on &&
4326                       (!can_turn_left ||
4327                        !can_turn_right ||
4328                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4329                                                  y + move_dy + left_dy) ||
4330                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4331                                                  y + move_dy + right_dy)));
4332
4333     if (should_turn_left || should_turn_right || should_move_on)
4334     {
4335       if (should_turn_left && should_turn_right && should_move_on)
4336         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
4337                         rnd < 2 * rnd_value / 3 ? right_dir :
4338                         old_move_dir);
4339       else if (should_turn_left && should_turn_right)
4340         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4341       else if (should_turn_left && should_move_on)
4342         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4343       else if (should_turn_right && should_move_on)
4344         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4345       else if (should_turn_left)
4346         MovDir[x][y] = left_dir;
4347       else if (should_turn_right)
4348         MovDir[x][y] = right_dir;
4349       else if (should_move_on)
4350         MovDir[x][y] = old_move_dir;
4351     }
4352     else if (can_move_on && rnd > rnd_value / 8)
4353       MovDir[x][y] = old_move_dir;
4354     else if (can_turn_left && can_turn_right)
4355       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4356     else if (can_turn_left && rnd > rnd_value / 8)
4357       MovDir[x][y] = left_dir;
4358     else if (can_turn_right && rnd > rnd_value/8)
4359       MovDir[x][y] = right_dir;
4360     else
4361       MovDir[x][y] = back_dir;
4362
4363     xx = x + move_xy[MovDir[x][y]].x;
4364     yy = y + move_xy[MovDir[x][y]].y;
4365
4366     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4367       MovDir[x][y] = old_move_dir;
4368
4369     MovDelay[x][y] = 0;
4370   }
4371   else if (element == EL_DRAGON)
4372   {
4373     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4374     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4375     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4376     int rnd_value = 24;
4377     int rnd = RND(rnd_value);
4378
4379 #if 0
4380     if (FrameCounter < 1 && x == 0 && y == 29)
4381       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4382 #endif
4383
4384     if (can_move_on && rnd > rnd_value / 8)
4385       MovDir[x][y] = old_move_dir;
4386     else if (can_turn_left && can_turn_right)
4387       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4388     else if (can_turn_left && rnd > rnd_value / 8)
4389       MovDir[x][y] = left_dir;
4390     else if (can_turn_right && rnd > rnd_value / 8)
4391       MovDir[x][y] = right_dir;
4392     else
4393       MovDir[x][y] = back_dir;
4394
4395     xx = x + move_xy[MovDir[x][y]].x;
4396     yy = y + move_xy[MovDir[x][y]].y;
4397
4398 #if 0
4399     if (FrameCounter < 1 && x == 0 && y == 29)
4400       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4401              xx, yy, Feld[xx][yy],
4402              FrameCounter);
4403 #endif
4404
4405 #if 1
4406     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4407       MovDir[x][y] = old_move_dir;
4408 #else
4409     if (!IS_FREE(xx, yy))
4410       MovDir[x][y] = old_move_dir;
4411 #endif
4412
4413 #if 0
4414     if (FrameCounter < 1 && x == 0 && y == 29)
4415       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4416 #endif
4417
4418     MovDelay[x][y] = 0;
4419   }
4420   else if (element == EL_MOLE)
4421   {
4422     boolean can_move_on =
4423       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4424                             IS_AMOEBOID(Feld[move_x][move_y]) ||
4425                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4426     if (!can_move_on)
4427     {
4428       boolean can_turn_left =
4429         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4430                               IS_AMOEBOID(Feld[left_x][left_y])));
4431
4432       boolean can_turn_right =
4433         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4434                               IS_AMOEBOID(Feld[right_x][right_y])));
4435
4436       if (can_turn_left && can_turn_right)
4437         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4438       else if (can_turn_left)
4439         MovDir[x][y] = left_dir;
4440       else
4441         MovDir[x][y] = right_dir;
4442     }
4443
4444     if (MovDir[x][y] != old_move_dir)
4445       MovDelay[x][y] = 9;
4446   }
4447   else if (element == EL_BALLOON)
4448   {
4449     MovDir[x][y] = game.balloon_dir;
4450     MovDelay[x][y] = 0;
4451   }
4452   else if (element == EL_SPRING)
4453   {
4454 #if 0
4455     if (MovDir[x][y] & MV_HORIZONTAL &&
4456         !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4457       MovDir[x][y] = MV_NO_MOVING;
4458 #else
4459     if (MovDir[x][y] & MV_HORIZONTAL &&
4460         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4461          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4462       MovDir[x][y] = MV_NO_MOVING;
4463 #endif
4464
4465     MovDelay[x][y] = 0;
4466   }
4467   else if (element == EL_ROBOT ||
4468            element == EL_SATELLITE ||
4469            element == EL_PENGUIN)
4470   {
4471     int attr_x = -1, attr_y = -1;
4472
4473     if (AllPlayersGone)
4474     {
4475       attr_x = ExitX;
4476       attr_y = ExitY;
4477     }
4478     else
4479     {
4480       int i;
4481
4482       for (i = 0; i < MAX_PLAYERS; i++)
4483       {
4484         struct PlayerInfo *player = &stored_player[i];
4485         int jx = player->jx, jy = player->jy;
4486
4487         if (!player->active)
4488           continue;
4489
4490         if (attr_x == -1 ||
4491             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4492         {
4493           attr_x = jx;
4494           attr_y = jy;
4495         }
4496       }
4497     }
4498
4499 #if 1
4500     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4501         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4502          game.engine_version < VERSION_IDENT(3,1,0,0)))
4503 #else
4504     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4505 #endif
4506     {
4507       attr_x = ZX;
4508       attr_y = ZY;
4509     }
4510
4511     if (element == EL_PENGUIN)
4512     {
4513       int i;
4514       static int xy[4][2] =
4515       {
4516         { 0, -1 },
4517         { -1, 0 },
4518         { +1, 0 },
4519         { 0, +1 }
4520       };
4521
4522       for (i = 0; i < NUM_DIRECTIONS; i++)
4523       {
4524         int ex = x + xy[i][0];
4525         int ey = y + xy[i][1];
4526
4527         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4528         {
4529           attr_x = ex;
4530           attr_y = ey;
4531           break;
4532         }
4533       }
4534     }
4535
4536     MovDir[x][y] = MV_NO_MOVING;
4537     if (attr_x < x)
4538       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4539     else if (attr_x > x)
4540       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4541     if (attr_y < y)
4542       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4543     else if (attr_y > y)
4544       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4545
4546     if (element == EL_ROBOT)
4547     {
4548       int newx, newy;
4549
4550       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4551         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4552       Moving2Blocked(x, y, &newx, &newy);
4553
4554       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4555         MovDelay[x][y] = 8 + 8 * !RND(3);
4556       else
4557         MovDelay[x][y] = 16;
4558     }
4559     else if (element == EL_PENGUIN)
4560     {
4561       int newx, newy;
4562
4563       MovDelay[x][y] = 1;
4564
4565       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4566       {
4567         boolean first_horiz = RND(2);
4568         int new_move_dir = MovDir[x][y];
4569
4570         MovDir[x][y] =
4571           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4572         Moving2Blocked(x, y, &newx, &newy);
4573
4574         if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4575           return;
4576
4577         MovDir[x][y] =
4578           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4579         Moving2Blocked(x, y, &newx, &newy);
4580
4581         if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4582           return;
4583
4584         MovDir[x][y] = old_move_dir;
4585         return;
4586       }
4587     }
4588     else        /* (element == EL_SATELLITE) */
4589     {
4590       int newx, newy;
4591
4592       MovDelay[x][y] = 1;
4593
4594       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4595       {
4596         boolean first_horiz = RND(2);
4597         int new_move_dir = MovDir[x][y];
4598
4599         MovDir[x][y] =
4600           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4601         Moving2Blocked(x, y, &newx, &newy);
4602
4603         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4604           return;
4605
4606         MovDir[x][y] =
4607           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4608         Moving2Blocked(x, y, &newx, &newy);
4609
4610         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4611           return;
4612
4613         MovDir[x][y] = old_move_dir;
4614         return;
4615       }
4616     }
4617   }
4618   else if (move_pattern == MV_TURNING_LEFT ||
4619            move_pattern == MV_TURNING_RIGHT ||
4620            move_pattern == MV_TURNING_LEFT_RIGHT ||
4621            move_pattern == MV_TURNING_RIGHT_LEFT ||
4622            move_pattern == MV_TURNING_RANDOM ||
4623            move_pattern == MV_ALL_DIRECTIONS)
4624   {
4625     boolean can_turn_left =
4626       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4627     boolean can_turn_right =
4628       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4629
4630     if (move_pattern == MV_TURNING_LEFT)
4631       MovDir[x][y] = left_dir;
4632     else if (move_pattern == MV_TURNING_RIGHT)
4633       MovDir[x][y] = right_dir;
4634     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4635       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4636     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4637       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4638     else if (move_pattern == MV_TURNING_RANDOM)
4639       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4640                       can_turn_right && !can_turn_left ? right_dir :
4641                       RND(2) ? left_dir : right_dir);
4642     else if (can_turn_left && can_turn_right)
4643       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4644     else if (can_turn_left)
4645       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4646     else if (can_turn_right)
4647       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4648     else
4649       MovDir[x][y] = back_dir;
4650
4651     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4652   }
4653   else if (move_pattern == MV_HORIZONTAL ||
4654            move_pattern == MV_VERTICAL)
4655   {
4656     if (move_pattern & old_move_dir)
4657       MovDir[x][y] = back_dir;
4658     else if (move_pattern == MV_HORIZONTAL)
4659       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4660     else if (move_pattern == MV_VERTICAL)
4661       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4662
4663     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4664   }
4665   else if (move_pattern & MV_ANY_DIRECTION)
4666   {
4667     MovDir[x][y] = move_pattern;
4668     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4669   }
4670   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4671   {
4672     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4673       MovDir[x][y] = left_dir;
4674     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4675       MovDir[x][y] = right_dir;
4676
4677     if (MovDir[x][y] != old_move_dir)
4678       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4679   }
4680   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4681   {
4682     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4683       MovDir[x][y] = right_dir;
4684     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4685       MovDir[x][y] = left_dir;
4686
4687     if (MovDir[x][y] != old_move_dir)
4688       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4689   }
4690   else if (move_pattern == MV_TOWARDS_PLAYER ||
4691            move_pattern == MV_AWAY_FROM_PLAYER)
4692   {
4693     int attr_x = -1, attr_y = -1;
4694     int newx, newy;
4695     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4696
4697     if (AllPlayersGone)
4698     {
4699       attr_x = ExitX;
4700       attr_y = ExitY;
4701     }
4702     else
4703     {
4704       int i;
4705
4706       for (i = 0; i < MAX_PLAYERS; i++)
4707       {
4708         struct PlayerInfo *player = &stored_player[i];
4709         int jx = player->jx, jy = player->jy;
4710
4711         if (!player->active)
4712           continue;
4713
4714         if (attr_x == -1 ||
4715             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4716         {
4717           attr_x = jx;
4718           attr_y = jy;
4719         }
4720       }
4721     }
4722
4723     MovDir[x][y] = MV_NO_MOVING;
4724     if (attr_x < x)
4725       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4726     else if (attr_x > x)
4727       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4728     if (attr_y < y)
4729       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4730     else if (attr_y > y)
4731       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4732
4733     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4734
4735     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4736     {
4737       boolean first_horiz = RND(2);
4738       int new_move_dir = MovDir[x][y];
4739
4740       MovDir[x][y] =
4741         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4742       Moving2Blocked(x, y, &newx, &newy);
4743
4744       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4745         return;
4746
4747       MovDir[x][y] =
4748         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4749       Moving2Blocked(x, y, &newx, &newy);
4750
4751       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4752         return;
4753
4754       MovDir[x][y] = old_move_dir;
4755     }
4756   }
4757   else if (move_pattern == MV_WHEN_PUSHED ||
4758            move_pattern == MV_WHEN_DROPPED)
4759   {
4760     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4761       MovDir[x][y] = MV_NO_MOVING;
4762
4763     MovDelay[x][y] = 0;
4764   }
4765   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4766   {
4767     static int test_xy[7][2] =
4768     {
4769       { 0, -1 },
4770       { -1, 0 },
4771       { +1, 0 },
4772       { 0, +1 },
4773       { 0, -1 },
4774       { -1, 0 },
4775       { +1, 0 },
4776     };
4777     static int test_dir[7] =
4778     {
4779       MV_UP,
4780       MV_LEFT,
4781       MV_RIGHT,
4782       MV_DOWN,
4783       MV_UP,
4784       MV_LEFT,
4785       MV_RIGHT,
4786     };
4787     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4788     int move_preference = -1000000;     /* start with very low preference */
4789     int new_move_dir = MV_NO_MOVING;
4790     int start_test = RND(4);
4791     int i;
4792
4793     for (i = 0; i < NUM_DIRECTIONS; i++)
4794     {
4795       int move_dir = test_dir[start_test + i];
4796       int move_dir_preference;
4797
4798       xx = x + test_xy[start_test + i][0];
4799       yy = y + test_xy[start_test + i][1];
4800
4801       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4802           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4803       {
4804         new_move_dir = move_dir;
4805
4806         break;
4807       }
4808
4809       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4810         continue;
4811
4812       move_dir_preference = -1 * RunnerVisit[xx][yy];
4813       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4814         move_dir_preference = PlayerVisit[xx][yy];
4815
4816       if (move_dir_preference > move_preference)
4817       {
4818         /* prefer field that has not been visited for the longest time */
4819         move_preference = move_dir_preference;
4820         new_move_dir = move_dir;
4821       }
4822       else if (move_dir_preference == move_preference &&
4823                move_dir == old_move_dir)
4824       {
4825         /* prefer last direction when all directions are preferred equally */
4826         move_preference = move_dir_preference;
4827         new_move_dir = move_dir;
4828       }
4829     }
4830
4831     MovDir[x][y] = new_move_dir;
4832     if (old_move_dir != new_move_dir)
4833       MovDelay[x][y] = 9;
4834   }
4835 }
4836
4837 static void TurnRound(int x, int y)
4838 {
4839   int direction = MovDir[x][y];
4840
4841 #if 0
4842   GfxDir[x][y] = MovDir[x][y];
4843 #endif
4844
4845   TurnRoundExt(x, y);
4846
4847 #if 1
4848   GfxDir[x][y] = MovDir[x][y];
4849 #endif
4850
4851   if (direction != MovDir[x][y])
4852     GfxFrame[x][y] = 0;
4853
4854 #if 1
4855   if (MovDelay[x][y])
4856     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4857 #else
4858   if (MovDelay[x][y])
4859     GfxAction[x][y] = ACTION_WAITING;
4860 #endif
4861 }
4862
4863 static boolean JustBeingPushed(int x, int y)
4864 {
4865   int i;
4866
4867   for (i = 0; i < MAX_PLAYERS; i++)
4868   {
4869     struct PlayerInfo *player = &stored_player[i];
4870
4871     if (player->active && player->is_pushing && player->MovPos)
4872     {
4873       int next_jx = player->jx + (player->jx - player->last_jx);
4874       int next_jy = player->jy + (player->jy - player->last_jy);
4875
4876       if (x == next_jx && y == next_jy)
4877         return TRUE;
4878     }
4879   }
4880
4881   return FALSE;
4882 }
4883
4884 void StartMoving(int x, int y)
4885 {
4886 #if 0
4887   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4888 #endif
4889   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4890   int element = Feld[x][y];
4891
4892   if (Stop[x][y])
4893     return;
4894
4895 #if 1
4896   if (MovDelay[x][y] == 0)
4897     GfxAction[x][y] = ACTION_DEFAULT;
4898 #else
4899   /* !!! this should be handled more generic (not only for mole) !!! */
4900   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4901     GfxAction[x][y] = ACTION_DEFAULT;
4902 #endif
4903
4904   if (CAN_FALL(element) && y < lev_fieldy - 1)
4905   {
4906     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
4907         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4908       if (JustBeingPushed(x, y))
4909         return;
4910
4911     if (element == EL_QUICKSAND_FULL)
4912     {
4913       if (IS_FREE(x, y + 1))
4914       {
4915         InitMovingField(x, y, MV_DOWN);
4916         started_moving = TRUE;
4917
4918         Feld[x][y] = EL_QUICKSAND_EMPTYING;
4919         Store[x][y] = EL_ROCK;
4920 #if 1
4921         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4922 #else
4923         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4924 #endif
4925       }
4926       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4927       {
4928         if (!MovDelay[x][y])
4929           MovDelay[x][y] = TILEY + 1;
4930
4931         if (MovDelay[x][y])
4932         {
4933           MovDelay[x][y]--;
4934           if (MovDelay[x][y])
4935             return;
4936         }
4937
4938         Feld[x][y] = EL_QUICKSAND_EMPTY;
4939         Feld[x][y + 1] = EL_QUICKSAND_FULL;
4940         Store[x][y + 1] = Store[x][y];
4941         Store[x][y] = 0;
4942 #if 1
4943         PlayLevelSoundAction(x, y, ACTION_FILLING);
4944 #else
4945         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4946 #endif
4947       }
4948     }
4949     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4950              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4951     {
4952       InitMovingField(x, y, MV_DOWN);
4953       started_moving = TRUE;
4954
4955       Feld[x][y] = EL_QUICKSAND_FILLING;
4956       Store[x][y] = element;
4957 #if 1
4958       PlayLevelSoundAction(x, y, ACTION_FILLING);
4959 #else
4960       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4961 #endif
4962     }
4963     else if (element == EL_MAGIC_WALL_FULL)
4964     {
4965       if (IS_FREE(x, y + 1))
4966       {
4967         InitMovingField(x, y, MV_DOWN);
4968         started_moving = TRUE;
4969
4970         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4971         Store[x][y] = EL_CHANGED(Store[x][y]);
4972       }
4973       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4974       {
4975         if (!MovDelay[x][y])
4976           MovDelay[x][y] = TILEY/4 + 1;
4977
4978         if (MovDelay[x][y])
4979         {
4980           MovDelay[x][y]--;
4981           if (MovDelay[x][y])
4982             return;
4983         }
4984
4985         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4986         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4987         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4988         Store[x][y] = 0;
4989       }
4990     }
4991     else if (element == EL_BD_MAGIC_WALL_FULL)
4992     {
4993       if (IS_FREE(x, y + 1))
4994       {
4995         InitMovingField(x, y, MV_DOWN);
4996         started_moving = TRUE;
4997
4998         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4999         Store[x][y] = EL_CHANGED2(Store[x][y]);
5000       }
5001       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5002       {
5003         if (!MovDelay[x][y])
5004           MovDelay[x][y] = TILEY/4 + 1;
5005
5006         if (MovDelay[x][y])
5007         {
5008           MovDelay[x][y]--;
5009           if (MovDelay[x][y])
5010             return;
5011         }
5012
5013         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5014         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5015         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5016         Store[x][y] = 0;
5017       }
5018     }
5019     else if (CAN_PASS_MAGIC_WALL(element) &&
5020              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5021               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5022     {
5023       InitMovingField(x, y, MV_DOWN);
5024       started_moving = TRUE;
5025
5026       Feld[x][y] =
5027         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5028          EL_BD_MAGIC_WALL_FILLING);
5029       Store[x][y] = element;
5030     }
5031 #if 0
5032     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5033 #else
5034     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5035 #endif
5036     {
5037       SplashAcid(x, y + 1);
5038
5039       InitMovingField(x, y, MV_DOWN);
5040       started_moving = TRUE;
5041
5042       Store[x][y] = EL_ACID;
5043 #if 0
5044       /* !!! TEST !!! better use "_FALLING" etc. !!! */
5045       GfxAction[x][y + 1] = ACTION_ACTIVE;
5046 #endif
5047     }
5048 #if 1
5049     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
5050               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5051               (Feld[x][y + 1] == EL_BLOCKED)) ||
5052              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5053               CAN_SMASH(element) && WasJustFalling[x][y] &&
5054               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
5055
5056 #else
5057 #if 1
5058     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5059              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5060              WasJustMoving[x][y] && !Pushed[x][y + 1])
5061 #else
5062     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5063              WasJustMoving[x][y])
5064 #endif
5065 #endif
5066
5067     {
5068       /* this is needed for a special case not covered by calling "Impact()"
5069          from "ContinueMoving()": if an element moves to a tile directly below
5070          another element which was just falling on that tile (which was empty
5071          in the previous frame), the falling element above would just stop
5072          instead of smashing the element below (in previous version, the above
5073          element was just checked for "moving" instead of "falling", resulting
5074          in incorrect smashes caused by horizontal movement of the above
5075          element; also, the case of the player being the element to smash was
5076          simply not covered here... :-/ ) */
5077
5078 #if 0
5079       WasJustMoving[x][y] = 0;
5080       WasJustFalling[x][y] = 0;
5081 #endif
5082
5083       Impact(x, y);
5084     }
5085     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5086     {
5087       if (MovDir[x][y] == MV_NO_MOVING)
5088       {
5089         InitMovingField(x, y, MV_DOWN);
5090         started_moving = TRUE;
5091       }
5092     }
5093     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5094     {
5095       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5096         MovDir[x][y] = MV_DOWN;
5097
5098       InitMovingField(x, y, MV_DOWN);
5099       started_moving = TRUE;
5100     }
5101     else if (element == EL_AMOEBA_DROP)
5102     {
5103       Feld[x][y] = EL_AMOEBA_GROWING;
5104       Store[x][y] = EL_AMOEBA_WET;
5105     }
5106     /* Store[x][y + 1] must be zero, because:
5107        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5108     */
5109 #if 0
5110 #if OLD_GAME_BEHAVIOUR
5111     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5112 #else
5113     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5114              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5115              element != EL_DX_SUPABOMB)
5116 #endif
5117 #else
5118     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5119               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5120              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5121              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5122 #endif
5123     {
5124       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
5125                                 (IS_FREE(x - 1, y + 1) ||
5126                                  Feld[x - 1][y + 1] == EL_ACID));
5127       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5128                                 (IS_FREE(x + 1, y + 1) ||
5129                                  Feld[x + 1][y + 1] == EL_ACID));
5130       boolean can_fall_any  = (can_fall_left || can_fall_right);
5131       boolean can_fall_both = (can_fall_left && can_fall_right);
5132
5133       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5134       {
5135         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5136
5137         if (slippery_type == SLIPPERY_ONLY_LEFT)
5138           can_fall_right = FALSE;
5139         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5140           can_fall_left = FALSE;
5141         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5142           can_fall_right = FALSE;
5143         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5144           can_fall_left = FALSE;
5145
5146         can_fall_any  = (can_fall_left || can_fall_right);
5147         can_fall_both = (can_fall_left && can_fall_right);
5148       }
5149
5150       if (can_fall_any)
5151       {
5152         if (can_fall_both &&
5153             (game.emulation != EMU_BOULDERDASH &&
5154              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5155           can_fall_left = !(can_fall_right = RND(2));
5156
5157         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5158         started_moving = TRUE;
5159       }
5160     }
5161 #if 0
5162     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5163 #else
5164     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5165 #endif
5166     {
5167       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
5168       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5169       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5170       int belt_dir = game.belt_dir[belt_nr];
5171
5172       if ((belt_dir == MV_LEFT  && left_is_free) ||
5173           (belt_dir == MV_RIGHT && right_is_free))
5174       {
5175 #if 1
5176         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5177 #endif
5178
5179         InitMovingField(x, y, belt_dir);
5180         started_moving = TRUE;
5181
5182 #if 1
5183         Pushed[x][y] = TRUE;
5184         Pushed[nextx][y] = TRUE;
5185 #endif
5186
5187         GfxAction[x][y] = ACTION_DEFAULT;
5188       }
5189       else
5190       {
5191         MovDir[x][y] = 0;       /* if element was moving, stop it */
5192       }
5193     }
5194   }
5195
5196   /* not "else if" because of elements that can fall and move (EL_SPRING) */
5197   if (CAN_MOVE(element) && !started_moving)
5198   {
5199     int move_pattern = element_info[element].move_pattern;
5200     int newx, newy;
5201
5202     Moving2Blocked(x, y, &newx, &newy);
5203
5204 #if 1
5205     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5206       return;
5207 #else
5208     if ((element == EL_SATELLITE ||
5209          element == EL_BALLOON ||
5210          element == EL_SPRING)
5211         && JustBeingPushed(x, y))
5212       return;
5213 #endif
5214
5215 #if 1
5216     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5217         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5218         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5219     {
5220 #if 0
5221       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5222              element, element_info[element].token_name,
5223              WasJustMoving[x][y],
5224              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5225              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5226              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5227              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5228 #endif
5229
5230 #if 1
5231       WasJustMoving[x][y] = 0;
5232 #endif
5233
5234       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5235
5236 #if 0
5237       if (Feld[x][y] != element)        /* element has changed */
5238       {
5239         element = Feld[x][y];
5240         move_pattern = element_info[element].move_pattern;
5241
5242         if (!CAN_MOVE(element))
5243           return;
5244       }
5245 #else
5246       if (Feld[x][y] != element)        /* element has changed */
5247         return;
5248 #endif
5249     }
5250 #endif
5251
5252 #if 0
5253 #if 0
5254     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5255       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
5256 #else
5257     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5258     {
5259       Moving2Blocked(x, y, &newx, &newy);
5260       if (Feld[newx][newy] == EL_BLOCKED)
5261         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
5262     }
5263 #endif
5264 #endif
5265
5266 #if 0
5267     if (FrameCounter < 1 && x == 0 && y == 29)
5268       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5269 #endif
5270
5271     if (!MovDelay[x][y])        /* start new movement phase */
5272     {
5273       /* all objects that can change their move direction after each step
5274          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5275
5276       if (element != EL_YAMYAM &&
5277           element != EL_DARK_YAMYAM &&
5278           element != EL_PACMAN &&
5279           !(move_pattern & MV_ANY_DIRECTION) &&
5280           move_pattern != MV_TURNING_LEFT &&
5281           move_pattern != MV_TURNING_RIGHT &&
5282           move_pattern != MV_TURNING_LEFT_RIGHT &&
5283           move_pattern != MV_TURNING_RIGHT_LEFT &&
5284           move_pattern != MV_TURNING_RANDOM)
5285       {
5286         TurnRound(x, y);
5287
5288 #if 0
5289         if (FrameCounter < 1 && x == 0 && y == 29)
5290           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5291 #endif
5292
5293         if (MovDelay[x][y] && (element == EL_BUG ||
5294                                element == EL_SPACESHIP ||
5295                                element == EL_SP_SNIKSNAK ||
5296                                element == EL_SP_ELECTRON ||
5297                                element == EL_MOLE))
5298           DrawLevelField(x, y);
5299       }
5300     }
5301
5302     if (MovDelay[x][y])         /* wait some time before next movement */
5303     {
5304       MovDelay[x][y]--;
5305
5306 #if 0
5307       if (element == EL_YAMYAM)
5308       {
5309         printf("::: %d\n",
5310                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5311         DrawLevelElementAnimation(x, y, element);
5312       }
5313 #endif
5314
5315       if (MovDelay[x][y])       /* element still has to wait some time */
5316       {
5317 #if 0
5318         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5319         ResetGfxAnimation(x, y);
5320 #endif
5321
5322 #if 0
5323         if (GfxAction[x][y] != ACTION_WAITING)
5324           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5325
5326         GfxAction[x][y] = ACTION_WAITING;
5327 #endif
5328       }
5329
5330       if (element == EL_ROBOT ||
5331 #if 0
5332           element == EL_PACMAN ||
5333 #endif
5334           element == EL_YAMYAM ||
5335           element == EL_DARK_YAMYAM)
5336       {
5337 #if 0
5338         DrawLevelElementAnimation(x, y, element);
5339 #else
5340         DrawLevelElementAnimationIfNeeded(x, y, element);
5341 #endif
5342         PlayLevelSoundAction(x, y, ACTION_WAITING);
5343       }
5344       else if (element == EL_SP_ELECTRON)
5345         DrawLevelElementAnimationIfNeeded(x, y, element);
5346       else if (element == EL_DRAGON)
5347       {
5348         int i;
5349         int dir = MovDir[x][y];
5350         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5351         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5352         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
5353                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
5354                        dir == MV_UP     ? IMG_FLAMES_1_UP :
5355                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5356         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5357
5358 #if 0
5359         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5360 #endif
5361
5362         GfxAction[x][y] = ACTION_ATTACKING;
5363
5364         if (IS_PLAYER(x, y))
5365           DrawPlayerField(x, y);
5366         else
5367           DrawLevelField(x, y);
5368
5369         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5370
5371         for (i = 1; i <= 3; i++)
5372         {
5373           int xx = x + i * dx;
5374           int yy = y + i * dy;
5375           int sx = SCREENX(xx);
5376           int sy = SCREENY(yy);
5377           int flame_graphic = graphic + (i - 1);
5378
5379           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5380             break;
5381
5382           if (MovDelay[x][y])
5383           {
5384             int flamed = MovingOrBlocked2Element(xx, yy);
5385
5386             /* !!! */
5387 #if 0
5388             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5389               Bang(xx, yy);
5390             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5391               RemoveMovingField(xx, yy);
5392             else
5393               RemoveField(xx, yy);
5394 #else
5395             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5396               Bang(xx, yy);
5397             else
5398               RemoveMovingField(xx, yy);
5399 #endif
5400
5401 #if 0
5402             if (ChangeDelay[xx][yy])
5403               printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5404                                         Feld[xx][yy] == EL_BLOCKED));
5405 #endif
5406
5407 #if 1
5408             ChangeDelay[xx][yy] = 0;
5409 #endif
5410             Feld[xx][yy] = EL_FLAMES;
5411             if (IN_SCR_FIELD(sx, sy))
5412             {
5413               DrawLevelFieldCrumbledSand(xx, yy);
5414               DrawGraphic(sx, sy, flame_graphic, frame);
5415             }
5416           }
5417           else
5418           {
5419             if (Feld[xx][yy] == EL_FLAMES)
5420               Feld[xx][yy] = EL_EMPTY;
5421             DrawLevelField(xx, yy);
5422           }
5423         }
5424       }
5425
5426       if (MovDelay[x][y])       /* element still has to wait some time */
5427       {
5428         PlayLevelSoundAction(x, y, ACTION_WAITING);
5429
5430         return;
5431       }
5432
5433 #if 0
5434       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5435          for all other elements GfxAction will be set by InitMovingField() */
5436       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5437         GfxAction[x][y] = ACTION_MOVING;
5438 #endif
5439     }
5440
5441     /* now make next step */
5442
5443     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5444
5445     if (DONT_COLLIDE_WITH(element) &&
5446         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5447         !PLAYER_ENEMY_PROTECTED(newx, newy))
5448     {
5449 #if 1
5450       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5451
5452       return;
5453 #else
5454       /* player killed by element which is deadly when colliding with */
5455       MovDir[x][y] = 0;
5456       KillHero(PLAYERINFO(newx, newy));
5457       return;
5458 #endif
5459
5460     }
5461 #if 1
5462 #if 1
5463     else if (CAN_MOVE_INTO_ACID(element) &&
5464              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5465              (MovDir[x][y] == MV_DOWN ||
5466               game.engine_version >= VERSION_IDENT(3,1,0,0)))
5467 #else
5468     else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5469              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5470 #endif
5471 #else
5472
5473     else if ((element == EL_PENGUIN ||
5474               element == EL_ROBOT ||
5475               element == EL_SATELLITE ||
5476               element == EL_BALLOON ||
5477               IS_CUSTOM_ELEMENT(element)) &&
5478              IN_LEV_FIELD(newx, newy) &&
5479              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5480 #endif
5481     {
5482       SplashAcid(newx, newy);
5483       Store[x][y] = EL_ACID;
5484     }
5485     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5486     {
5487       if (Feld[newx][newy] == EL_EXIT_OPEN)
5488       {
5489 #if 1
5490         RemoveField(x, y);
5491         DrawLevelField(x, y);
5492 #else
5493         Feld[x][y] = EL_EMPTY;
5494         DrawLevelField(x, y);
5495 #endif
5496
5497         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5498         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5499           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5500
5501         local_player->friends_still_needed--;
5502         if (!local_player->friends_still_needed &&
5503             !local_player->GameOver && AllPlayersGone)
5504           local_player->LevelSolved = local_player->GameOver = TRUE;
5505
5506         return;
5507       }
5508       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5509       {
5510         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5511           DrawLevelField(newx, newy);
5512         else
5513           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5514       }
5515       else if (!IS_FREE(newx, newy))
5516       {
5517         GfxAction[x][y] = ACTION_WAITING;
5518
5519         if (IS_PLAYER(x, y))
5520           DrawPlayerField(x, y);
5521         else
5522           DrawLevelField(x, y);
5523
5524         return;
5525       }
5526     }
5527     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5528     {
5529       if (IS_FOOD_PIG(Feld[newx][newy]))
5530       {
5531         if (IS_MOVING(newx, newy))
5532           RemoveMovingField(newx, newy);
5533         else
5534         {
5535           Feld[newx][newy] = EL_EMPTY;
5536           DrawLevelField(newx, newy);
5537         }
5538
5539         PlayLevelSound(x, y, SND_PIG_DIGGING);
5540       }
5541       else if (!IS_FREE(newx, newy))
5542       {
5543         if (IS_PLAYER(x, y))
5544           DrawPlayerField(x, y);
5545         else
5546           DrawLevelField(x, y);
5547
5548         return;
5549       }
5550     }
5551
5552 #if 1
5553
5554     /*
5555     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5556     */
5557
5558     else if (IS_CUSTOM_ELEMENT(element) &&
5559              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5560
5561 #if 0
5562  &&
5563              !IS_FREE(newx, newy)
5564 #endif
5565
5566 )
5567     {
5568       int new_element = Feld[newx][newy];
5569
5570 #if 0
5571       printf("::: '%s' digs '%s' [%d]\n",
5572              element_info[element].token_name,
5573              element_info[Feld[newx][newy]].token_name,
5574              StorePlayer[newx][newy]);
5575 #endif
5576
5577       if (!IS_FREE(newx, newy))
5578       {
5579         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5580                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5581                       ACTION_BREAKING);
5582
5583         /* no element can dig solid indestructible elements */
5584         if (IS_INDESTRUCTIBLE(new_element) &&
5585             !IS_DIGGABLE(new_element) &&
5586             !IS_COLLECTIBLE(new_element))
5587           return;
5588
5589         if (AmoebaNr[newx][newy] &&
5590             (new_element == EL_AMOEBA_FULL ||
5591              new_element == EL_BD_AMOEBA ||
5592              new_element == EL_AMOEBA_GROWING))
5593         {
5594           AmoebaCnt[AmoebaNr[newx][newy]]--;
5595           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5596         }
5597
5598         if (IS_MOVING(newx, newy))
5599           RemoveMovingField(newx, newy);
5600         else
5601         {
5602           RemoveField(newx, newy);
5603           DrawLevelField(newx, newy);
5604         }
5605
5606         PlayLevelSoundAction(x, y, action);
5607       }
5608
5609 #if 1
5610 #if 1
5611       Store[newx][newy] = EL_EMPTY;
5612       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5613         Store[newx][newy] = element_info[element].move_leave_element;
5614 #else
5615       Store[newx][newy] = EL_EMPTY;
5616       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5617           element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5618         Store[newx][newy] = element_info[element].move_leave_element;
5619 #endif
5620 #else
5621       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5622         element_info[element].can_leave_element = TRUE;
5623 #endif
5624
5625       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5626       {
5627         RunnerVisit[x][y] = FrameCounter;
5628         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5629       }
5630     }
5631
5632 #endif
5633
5634     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5635     {
5636       if (!IS_FREE(newx, newy))
5637       {
5638         if (IS_PLAYER(x, y))
5639           DrawPlayerField(x, y);
5640         else
5641           DrawLevelField(x, y);
5642
5643         return;
5644       }
5645       else
5646       {
5647         boolean wanna_flame = !RND(10);
5648         int dx = newx - x, dy = newy - y;
5649         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5650         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5651         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5652                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5653         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5654                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5655
5656         if ((wanna_flame ||
5657              IS_CLASSIC_ENEMY(element1) ||
5658              IS_CLASSIC_ENEMY(element2)) &&
5659             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5660             element1 != EL_FLAMES && element2 != EL_FLAMES)
5661         {
5662 #if 1
5663           ResetGfxAnimation(x, y);
5664           GfxAction[x][y] = ACTION_ATTACKING;
5665 #endif
5666
5667           if (IS_PLAYER(x, y))
5668             DrawPlayerField(x, y);
5669           else
5670             DrawLevelField(x, y);
5671
5672           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5673
5674           MovDelay[x][y] = 50;
5675
5676           /* !!! */
5677 #if 0
5678           RemoveField(newx, newy);
5679 #endif
5680           Feld[newx][newy] = EL_FLAMES;
5681           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5682           {
5683 #if 0
5684             RemoveField(newx1, newy1);
5685 #endif
5686             Feld[newx1][newy1] = EL_FLAMES;
5687           }
5688           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5689           {
5690 #if 0
5691             RemoveField(newx2, newy2);
5692 #endif
5693             Feld[newx2][newy2] = EL_FLAMES;
5694           }
5695
5696           return;
5697         }
5698       }
5699     }
5700     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5701              Feld[newx][newy] == EL_DIAMOND)
5702     {
5703       if (IS_MOVING(newx, newy))
5704         RemoveMovingField(newx, newy);
5705       else
5706       {
5707         Feld[newx][newy] = EL_EMPTY;
5708         DrawLevelField(newx, newy);
5709       }
5710
5711       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5712     }
5713     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5714              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5715     {
5716       if (AmoebaNr[newx][newy])
5717       {
5718         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5719         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5720             Feld[newx][newy] == EL_BD_AMOEBA)
5721           AmoebaCnt[AmoebaNr[newx][newy]]--;
5722       }
5723
5724 #if 0
5725       /* !!! test !!! */
5726       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5727 #else
5728       if (IS_MOVING(newx, newy))
5729 #endif
5730       {
5731         RemoveMovingField(newx, newy);
5732       }
5733       else
5734       {
5735         Feld[newx][newy] = EL_EMPTY;
5736         DrawLevelField(newx, newy);
5737       }
5738
5739       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5740     }
5741     else if ((element == EL_PACMAN || element == EL_MOLE)
5742              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5743     {
5744       if (AmoebaNr[newx][newy])
5745       {
5746         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5747         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5748             Feld[newx][newy] == EL_BD_AMOEBA)
5749           AmoebaCnt[AmoebaNr[newx][newy]]--;
5750       }
5751
5752       if (element == EL_MOLE)
5753       {
5754         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5755         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5756
5757         ResetGfxAnimation(x, y);
5758         GfxAction[x][y] = ACTION_DIGGING;
5759         DrawLevelField(x, y);
5760
5761         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5762
5763         return;                         /* wait for shrinking amoeba */
5764       }
5765       else      /* element == EL_PACMAN */
5766       {
5767         Feld[newx][newy] = EL_EMPTY;
5768         DrawLevelField(newx, newy);
5769         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5770       }
5771     }
5772     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5773              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5774               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5775     {
5776       /* wait for shrinking amoeba to completely disappear */
5777       return;
5778     }
5779     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5780     {
5781       /* object was running against a wall */
5782
5783       TurnRound(x, y);
5784
5785 #if 1
5786       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5787         DrawLevelElementAnimation(x, y, element);
5788 #else
5789       if (element == EL_BUG ||
5790           element == EL_SPACESHIP ||
5791           element == EL_SP_SNIKSNAK)
5792         DrawLevelField(x, y);
5793       else if (element == EL_MOLE)
5794         DrawLevelField(x, y);
5795       else if (element == EL_BD_BUTTERFLY ||
5796                element == EL_BD_FIREFLY)
5797         DrawLevelElementAnimationIfNeeded(x, y, element);
5798       else if (element == EL_SATELLITE)
5799         DrawLevelElementAnimationIfNeeded(x, y, element);
5800       else if (element == EL_SP_ELECTRON)
5801         DrawLevelElementAnimationIfNeeded(x, y, element);
5802 #endif
5803
5804       if (DONT_TOUCH(element))
5805         TestIfBadThingTouchesHero(x, y);
5806
5807 #if 0
5808       PlayLevelSoundAction(x, y, ACTION_WAITING);
5809 #endif
5810
5811       return;
5812     }
5813
5814     InitMovingField(x, y, MovDir[x][y]);
5815
5816     PlayLevelSoundAction(x, y, ACTION_MOVING);
5817   }
5818
5819   if (MovDir[x][y])
5820     ContinueMoving(x, y);
5821 }
5822
5823 void ContinueMoving(int x, int y)
5824 {
5825   int element = Feld[x][y];
5826   int stored = Store[x][y];
5827   struct ElementInfo *ei = &element_info[element];
5828   int direction = MovDir[x][y];
5829   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5830   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5831   int newx = x + dx, newy = y + dy;
5832 #if 0
5833   int nextx = newx + dx, nexty = newy + dy;
5834 #endif
5835 #if 1
5836   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
5837   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5838 #else
5839   boolean pushed_by_player = Pushed[x][y];
5840 #endif
5841
5842   MovPos[x][y] += getElementMoveStepsize(x, y);
5843
5844 #if 0
5845   if (pushed_by_player && IS_PLAYER(x, y))
5846   {
5847     /* special case: moving object pushed by player */
5848     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5849   }
5850 #else
5851   if (pushed_by_player) /* special case: moving object pushed by player */
5852     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5853 #endif
5854
5855   if (ABS(MovPos[x][y]) < TILEX)
5856   {
5857     DrawLevelField(x, y);
5858
5859     return;     /* element is still moving */
5860   }
5861
5862   /* element reached destination field */
5863
5864   Feld[x][y] = EL_EMPTY;
5865   Feld[newx][newy] = element;
5866   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
5867
5868   if (element == EL_MOLE)
5869   {
5870     Feld[x][y] = EL_SAND;
5871
5872     DrawLevelFieldCrumbledSandNeighbours(x, y);
5873   }
5874   else if (element == EL_QUICKSAND_FILLING)
5875   {
5876     element = Feld[newx][newy] = get_next_element(element);
5877     Store[newx][newy] = Store[x][y];
5878   }
5879   else if (element == EL_QUICKSAND_EMPTYING)
5880   {
5881     Feld[x][y] = get_next_element(element);
5882     element = Feld[newx][newy] = Store[x][y];
5883   }
5884   else if (element == EL_MAGIC_WALL_FILLING)
5885   {
5886     element = Feld[newx][newy] = get_next_element(element);
5887     if (!game.magic_wall_active)
5888       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5889     Store[newx][newy] = Store[x][y];
5890   }
5891   else if (element == EL_MAGIC_WALL_EMPTYING)
5892   {
5893     Feld[x][y] = get_next_element(element);
5894     if (!game.magic_wall_active)
5895       Feld[x][y] = EL_MAGIC_WALL_DEAD;
5896     element = Feld[newx][newy] = Store[x][y];
5897   }
5898   else if (element == EL_BD_MAGIC_WALL_FILLING)
5899   {
5900     element = Feld[newx][newy] = get_next_element(element);
5901     if (!game.magic_wall_active)
5902       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5903     Store[newx][newy] = Store[x][y];
5904   }
5905   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5906   {
5907     Feld[x][y] = get_next_element(element);
5908     if (!game.magic_wall_active)
5909       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5910     element = Feld[newx][newy] = Store[x][y];
5911   }
5912   else if (element == EL_AMOEBA_DROPPING)
5913   {
5914     Feld[x][y] = get_next_element(element);
5915     element = Feld[newx][newy] = Store[x][y];
5916   }
5917   else if (element == EL_SOKOBAN_OBJECT)
5918   {
5919     if (Back[x][y])
5920       Feld[x][y] = Back[x][y];
5921
5922     if (Back[newx][newy])
5923       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5924
5925     Back[x][y] = Back[newx][newy] = 0;
5926   }
5927   else if (Store[x][y] == EL_ACID)
5928   {
5929     element = Feld[newx][newy] = EL_ACID;
5930   }
5931 #if 0
5932   else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5933            ei->move_leave_element != EL_EMPTY &&
5934            (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5935             Store[x][y] != EL_EMPTY))
5936   {
5937     /* some elements can leave other elements behind after moving */
5938
5939     Feld[x][y] = ei->move_leave_element;
5940     InitField(x, y, FALSE);
5941
5942     if (GFX_CRUMBLED(Feld[x][y]))
5943       DrawLevelFieldCrumbledSandNeighbours(x, y);
5944   }
5945 #endif
5946
5947   Store[x][y] = EL_EMPTY;
5948   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5949   MovDelay[newx][newy] = 0;
5950
5951   if (CAN_CHANGE(element))
5952   {
5953     /* copy element change control values to new field */
5954     ChangeDelay[newx][newy] = ChangeDelay[x][y];
5955     ChangePage[newx][newy]  = ChangePage[x][y];
5956     Changed[newx][newy]     = Changed[x][y];
5957     ChangeEvent[newx][newy] = ChangeEvent[x][y];
5958   }
5959
5960   ChangeDelay[x][y] = 0;
5961   ChangePage[x][y] = -1;
5962   Changed[x][y] = CE_BITMASK_DEFAULT;
5963   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5964
5965   /* copy animation control values to new field */
5966   GfxFrame[newx][newy]  = GfxFrame[x][y];
5967   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
5968   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
5969   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
5970
5971   Pushed[x][y] = Pushed[newx][newy] = FALSE;
5972
5973   ResetGfxAnimation(x, y);      /* reset animation values for old field */
5974
5975 #if 1
5976   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5977       ei->move_leave_element != EL_EMPTY &&
5978       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5979        stored != EL_EMPTY))
5980   {
5981     /* some elements can leave other elements behind after moving */
5982
5983     Feld[x][y] = ei->move_leave_element;
5984     InitField(x, y, FALSE);
5985
5986     if (GFX_CRUMBLED(Feld[x][y]))
5987       DrawLevelFieldCrumbledSandNeighbours(x, y);
5988   }
5989 #endif
5990
5991 #if 0
5992   /* some elements can leave other elements behind after moving */
5993   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5994       ei->move_leave_element != EL_EMPTY &&
5995       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5996        ei->can_leave_element_last))
5997   {
5998     Feld[x][y] = ei->move_leave_element;
5999     InitField(x, y, FALSE);
6000
6001     if (GFX_CRUMBLED(Feld[x][y]))
6002       DrawLevelFieldCrumbledSandNeighbours(x, y);
6003   }
6004
6005   ei->can_leave_element_last = ei->can_leave_element;
6006   ei->can_leave_element = FALSE;
6007 #endif
6008
6009 #if 0
6010   /* 2.1.1 (does not work correctly for spring) */
6011   if (!CAN_MOVE(element))
6012     MovDir[newx][newy] = 0;
6013 #else
6014
6015 #if 0
6016   /* (does not work for falling objects that slide horizontally) */
6017   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6018     MovDir[newx][newy] = 0;
6019 #else
6020   /*
6021   if (!CAN_MOVE(element) ||
6022       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6023     MovDir[newx][newy] = 0;
6024   */
6025
6026 #if 0
6027   if (!CAN_MOVE(element) ||
6028       (CAN_FALL(element) && direction == MV_DOWN))
6029     GfxDir[x][y] = MovDir[newx][newy] = 0;
6030 #else
6031   if (!CAN_MOVE(element) ||
6032       (CAN_FALL(element) && direction == MV_DOWN &&
6033        (element == EL_SPRING ||
6034         element_info[element].move_pattern == MV_WHEN_PUSHED ||
6035         element_info[element].move_pattern == MV_WHEN_DROPPED)))
6036     GfxDir[x][y] = MovDir[newx][newy] = 0;
6037 #endif
6038
6039 #endif
6040 #endif
6041
6042   DrawLevelField(x, y);
6043   DrawLevelField(newx, newy);
6044
6045   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
6046
6047   /* prevent pushed element from moving on in pushed direction */
6048   if (pushed_by_player && CAN_MOVE(element) &&
6049       element_info[element].move_pattern & MV_ANY_DIRECTION &&
6050       !(element_info[element].move_pattern & direction))
6051     TurnRound(newx, newy);
6052
6053 #if 1
6054   /* prevent elements on conveyor belt from moving on in last direction */
6055   if (pushed_by_conveyor && CAN_FALL(element) &&
6056       direction & MV_HORIZONTAL)
6057     MovDir[newx][newy] = 0;
6058 #endif
6059
6060   if (!pushed_by_player)
6061   {
6062     WasJustMoving[newx][newy] = 3;
6063
6064     if (CAN_FALL(element) && direction == MV_DOWN)
6065       WasJustFalling[newx][newy] = 3;
6066   }
6067
6068   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
6069   {
6070     TestIfBadThingTouchesHero(newx, newy);
6071     TestIfBadThingTouchesFriend(newx, newy);
6072
6073     if (!IS_CUSTOM_ELEMENT(element))
6074       TestIfBadThingTouchesOtherBadThing(newx, newy);
6075   }
6076   else if (element == EL_PENGUIN)
6077     TestIfFriendTouchesBadThing(newx, newy);
6078
6079   if (CAN_FALL(element) && direction == MV_DOWN &&
6080       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6081     Impact(x, newy);
6082
6083 #if 1
6084   if (pushed_by_player)
6085   {
6086     static int trigger_sides[4] =
6087     {
6088       CH_SIDE_RIGHT,    /* moving left  */
6089       CH_SIDE_LEFT,     /* moving right */
6090       CH_SIDE_BOTTOM,   /* moving up    */
6091       CH_SIDE_TOP,      /* moving down  */
6092     };
6093     int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6094     struct PlayerInfo *player = PLAYERINFO(x, y);
6095
6096     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6097                                player->index_bit, dig_side);
6098     CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6099                                         player->index_bit, dig_side);
6100   }
6101 #endif
6102
6103 #if 1
6104   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
6105 #endif
6106
6107 #if 0
6108   if (ChangePage[newx][newy] != -1)                     /* delayed change */
6109     ChangeElement(newx, newy, ChangePage[newx][newy]);
6110 #endif
6111
6112 #if 1
6113
6114   TestIfElementHitsCustomElement(newx, newy, direction);
6115
6116 #else
6117
6118   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6119   {
6120     int hitting_element = Feld[newx][newy];
6121
6122     /* !!! fix side (direction) orientation here and elsewhere !!! */
6123     CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6124                              direction);
6125
6126 #if 0
6127     if (IN_LEV_FIELD(nextx, nexty))
6128     {
6129       int opposite_direction = MV_DIR_OPPOSITE(direction);
6130       int hitting_side = direction;
6131       int touched_side = opposite_direction;
6132       int touched_element = MovingOrBlocked2Element(nextx, nexty);
6133       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6134                             MovDir[nextx][nexty] != direction ||
6135                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6136
6137       if (object_hit)
6138       {
6139         int i;
6140
6141         CheckElementChangeBySide(nextx, nexty, touched_element,
6142                                  CE_HIT_BY_SOMETHING, opposite_direction);
6143
6144         if (IS_CUSTOM_ELEMENT(hitting_element) &&
6145             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6146         {
6147           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6148           {
6149             struct ElementChangeInfo *change =
6150               &element_info[hitting_element].change_page[i];
6151
6152             if (change->can_change &&
6153                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6154                 change->trigger_side & touched_side &&
6155                 change->trigger_element == touched_element)
6156             {
6157               CheckElementChangeByPage(newx, newy, hitting_element,
6158                                        touched_element, CE_OTHER_IS_HITTING,i);
6159               break;
6160             }
6161           }
6162         }
6163
6164         if (IS_CUSTOM_ELEMENT(touched_element) &&
6165             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6166         {
6167           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6168           {
6169             struct ElementChangeInfo *change =
6170               &element_info[touched_element].change_page[i];
6171
6172             if (change->can_change &&
6173                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6174                 change->trigger_side & hitting_side &&
6175                 change->trigger_element == hitting_element)
6176             {
6177               CheckElementChangeByPage(nextx, nexty, touched_element,
6178                                        hitting_element, CE_OTHER_GETS_HIT, i);
6179               break;
6180             }
6181           }
6182         }
6183       }
6184     }
6185 #endif
6186   }
6187 #endif
6188
6189   TestIfPlayerTouchesCustomElement(newx, newy);
6190   TestIfElementTouchesCustomElement(newx, newy);
6191 }
6192
6193 int AmoebeNachbarNr(int ax, int ay)
6194 {
6195   int i;
6196   int element = Feld[ax][ay];
6197   int group_nr = 0;
6198   static int xy[4][2] =
6199   {
6200     { 0, -1 },
6201     { -1, 0 },
6202     { +1, 0 },
6203     { 0, +1 }
6204   };
6205
6206   for (i = 0; i < NUM_DIRECTIONS; i++)
6207   {
6208     int x = ax + xy[i][0];
6209     int y = ay + xy[i][1];
6210
6211     if (!IN_LEV_FIELD(x, y))
6212       continue;
6213
6214     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6215       group_nr = AmoebaNr[x][y];
6216   }
6217
6218   return group_nr;
6219 }
6220
6221 void AmoebenVereinigen(int ax, int ay)
6222 {
6223   int i, x, y, xx, yy;
6224   int new_group_nr = AmoebaNr[ax][ay];
6225   static int xy[4][2] =
6226   {
6227     { 0, -1 },
6228     { -1, 0 },
6229     { +1, 0 },
6230     { 0, +1 }
6231   };
6232
6233   if (new_group_nr == 0)
6234     return;
6235
6236   for (i = 0; i < NUM_DIRECTIONS; i++)
6237   {
6238     x = ax + xy[i][0];
6239     y = ay + xy[i][1];
6240
6241     if (!IN_LEV_FIELD(x, y))
6242       continue;
6243
6244     if ((Feld[x][y] == EL_AMOEBA_FULL ||
6245          Feld[x][y] == EL_BD_AMOEBA ||
6246          Feld[x][y] == EL_AMOEBA_DEAD) &&
6247         AmoebaNr[x][y] != new_group_nr)
6248     {
6249       int old_group_nr = AmoebaNr[x][y];
6250
6251       if (old_group_nr == 0)
6252         return;
6253
6254       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6255       AmoebaCnt[old_group_nr] = 0;
6256       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6257       AmoebaCnt2[old_group_nr] = 0;
6258
6259       for (yy = 0; yy < lev_fieldy; yy++)
6260       {
6261         for (xx = 0; xx < lev_fieldx; xx++)
6262         {
6263           if (AmoebaNr[xx][yy] == old_group_nr)
6264             AmoebaNr[xx][yy] = new_group_nr;
6265         }
6266       }
6267     }
6268   }
6269 }
6270
6271 void AmoebeUmwandeln(int ax, int ay)
6272 {
6273   int i, x, y;
6274
6275   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6276   {
6277     int group_nr = AmoebaNr[ax][ay];
6278
6279 #ifdef DEBUG
6280     if (group_nr == 0)
6281     {
6282       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6283       printf("AmoebeUmwandeln(): This should never happen!\n");
6284       return;
6285     }
6286 #endif
6287
6288     for (y = 0; y < lev_fieldy; y++)
6289     {
6290       for (x = 0; x < lev_fieldx; x++)
6291       {
6292         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6293         {
6294           AmoebaNr[x][y] = 0;
6295           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6296         }
6297       }
6298     }
6299     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6300                             SND_AMOEBA_TURNING_TO_GEM :
6301                             SND_AMOEBA_TURNING_TO_ROCK));
6302     Bang(ax, ay);
6303   }
6304   else
6305   {
6306     static int xy[4][2] =
6307     {
6308       { 0, -1 },
6309       { -1, 0 },
6310       { +1, 0 },
6311       { 0, +1 }
6312     };
6313
6314     for (i = 0; i < NUM_DIRECTIONS; i++)
6315     {
6316       x = ax + xy[i][0];
6317       y = ay + xy[i][1];
6318
6319       if (!IN_LEV_FIELD(x, y))
6320         continue;
6321
6322       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6323       {
6324         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6325                               SND_AMOEBA_TURNING_TO_GEM :
6326                               SND_AMOEBA_TURNING_TO_ROCK));
6327         Bang(x, y);
6328       }
6329     }
6330   }
6331 }
6332
6333 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6334 {
6335   int x, y;
6336   int group_nr = AmoebaNr[ax][ay];
6337   boolean done = FALSE;
6338
6339 #ifdef DEBUG
6340   if (group_nr == 0)
6341   {
6342     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6343     printf("AmoebeUmwandelnBD(): This should never happen!\n");
6344     return;
6345   }
6346 #endif
6347
6348   for (y = 0; y < lev_fieldy; y++)
6349   {
6350     for (x = 0; x < lev_fieldx; x++)
6351     {
6352       if (AmoebaNr[x][y] == group_nr &&
6353           (Feld[x][y] == EL_AMOEBA_DEAD ||
6354            Feld[x][y] == EL_BD_AMOEBA ||
6355            Feld[x][y] == EL_AMOEBA_GROWING))
6356       {
6357         AmoebaNr[x][y] = 0;
6358         Feld[x][y] = new_element;
6359         InitField(x, y, FALSE);
6360         DrawLevelField(x, y);
6361         done = TRUE;
6362       }
6363     }
6364   }
6365
6366   if (done)
6367     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6368                             SND_BD_AMOEBA_TURNING_TO_ROCK :
6369                             SND_BD_AMOEBA_TURNING_TO_GEM));
6370 }
6371
6372 void AmoebeWaechst(int x, int y)
6373 {
6374   static unsigned long sound_delay = 0;
6375   static unsigned long sound_delay_value = 0;
6376
6377   if (!MovDelay[x][y])          /* start new growing cycle */
6378   {
6379     MovDelay[x][y] = 7;
6380
6381     if (DelayReached(&sound_delay, sound_delay_value))
6382     {
6383 #if 1
6384       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6385 #else
6386       if (Store[x][y] == EL_BD_AMOEBA)
6387         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6388       else
6389         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6390 #endif
6391       sound_delay_value = 30;
6392     }
6393   }
6394
6395   if (MovDelay[x][y])           /* wait some time before growing bigger */
6396   {
6397     MovDelay[x][y]--;
6398     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6399     {
6400       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6401                                            6 - MovDelay[x][y]);
6402
6403       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6404     }
6405
6406     if (!MovDelay[x][y])
6407     {
6408       Feld[x][y] = Store[x][y];
6409       Store[x][y] = 0;
6410       DrawLevelField(x, y);
6411     }
6412   }
6413 }
6414
6415 void AmoebaDisappearing(int x, int y)
6416 {
6417   static unsigned long sound_delay = 0;
6418   static unsigned long sound_delay_value = 0;
6419
6420   if (!MovDelay[x][y])          /* start new shrinking cycle */
6421   {
6422     MovDelay[x][y] = 7;
6423
6424     if (DelayReached(&sound_delay, sound_delay_value))
6425       sound_delay_value = 30;
6426   }
6427
6428   if (MovDelay[x][y])           /* wait some time before shrinking */
6429   {
6430     MovDelay[x][y]--;
6431     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6432     {
6433       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6434                                            6 - MovDelay[x][y]);
6435
6436       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6437     }
6438
6439     if (!MovDelay[x][y])
6440     {
6441       Feld[x][y] = EL_EMPTY;
6442       DrawLevelField(x, y);
6443
6444       /* don't let mole enter this field in this cycle;
6445          (give priority to objects falling to this field from above) */
6446       Stop[x][y] = TRUE;
6447     }
6448   }
6449 }
6450
6451 void AmoebeAbleger(int ax, int ay)
6452 {
6453   int i;
6454   int element = Feld[ax][ay];
6455   int graphic = el2img(element);
6456   int newax = ax, neway = ay;
6457   static int xy[4][2] =
6458   {
6459     { 0, -1 },
6460     { -1, 0 },
6461     { +1, 0 },
6462     { 0, +1 }
6463   };
6464
6465   if (!level.amoeba_speed)
6466   {
6467     Feld[ax][ay] = EL_AMOEBA_DEAD;
6468     DrawLevelField(ax, ay);
6469     return;
6470   }
6471
6472   if (IS_ANIMATED(graphic))
6473     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6474
6475   if (!MovDelay[ax][ay])        /* start making new amoeba field */
6476     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6477
6478   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
6479   {
6480     MovDelay[ax][ay]--;
6481     if (MovDelay[ax][ay])
6482       return;
6483   }
6484
6485   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6486   {
6487     int start = RND(4);
6488     int x = ax + xy[start][0];
6489     int y = ay + xy[start][1];
6490
6491     if (!IN_LEV_FIELD(x, y))
6492       return;
6493
6494 #if 1
6495     if (IS_FREE(x, y) ||
6496         CAN_GROW_INTO(Feld[x][y]) ||
6497         Feld[x][y] == EL_QUICKSAND_EMPTY)
6498     {
6499       newax = x;
6500       neway = y;
6501     }
6502 #else
6503     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6504     if (IS_FREE(x, y) ||
6505         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6506     {
6507       newax = x;
6508       neway = y;
6509     }
6510 #endif
6511
6512     if (newax == ax && neway == ay)
6513       return;
6514   }
6515   else                          /* normal or "filled" (BD style) amoeba */
6516   {
6517     int start = RND(4);
6518     boolean waiting_for_player = FALSE;
6519
6520     for (i = 0; i < NUM_DIRECTIONS; i++)
6521     {
6522       int j = (start + i) % 4;
6523       int x = ax + xy[j][0];
6524       int y = ay + xy[j][1];
6525
6526       if (!IN_LEV_FIELD(x, y))
6527         continue;
6528
6529 #if 1
6530       if (IS_FREE(x, y) ||
6531           CAN_GROW_INTO(Feld[x][y]) ||
6532           Feld[x][y] == EL_QUICKSAND_EMPTY)
6533       {
6534         newax = x;
6535         neway = y;
6536         break;
6537       }
6538 #else
6539       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6540       if (IS_FREE(x, y) ||
6541           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6542       {
6543         newax = x;
6544         neway = y;
6545         break;
6546       }
6547 #endif
6548       else if (IS_PLAYER(x, y))
6549         waiting_for_player = TRUE;
6550     }
6551
6552     if (newax == ax && neway == ay)             /* amoeba cannot grow */
6553     {
6554 #if 1
6555       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6556 #else
6557       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6558 #endif
6559       {
6560         Feld[ax][ay] = EL_AMOEBA_DEAD;
6561         DrawLevelField(ax, ay);
6562         AmoebaCnt[AmoebaNr[ax][ay]]--;
6563
6564         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
6565         {
6566           if (element == EL_AMOEBA_FULL)
6567             AmoebeUmwandeln(ax, ay);
6568           else if (element == EL_BD_AMOEBA)
6569             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6570         }
6571       }
6572       return;
6573     }
6574     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6575     {
6576       /* amoeba gets larger by growing in some direction */
6577
6578       int new_group_nr = AmoebaNr[ax][ay];
6579
6580 #ifdef DEBUG
6581   if (new_group_nr == 0)
6582   {
6583     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6584     printf("AmoebeAbleger(): This should never happen!\n");
6585     return;
6586   }
6587 #endif
6588
6589       AmoebaNr[newax][neway] = new_group_nr;
6590       AmoebaCnt[new_group_nr]++;
6591       AmoebaCnt2[new_group_nr]++;
6592
6593       /* if amoeba touches other amoeba(s) after growing, unify them */
6594       AmoebenVereinigen(newax, neway);
6595
6596       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6597       {
6598         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6599         return;
6600       }
6601     }
6602   }
6603
6604   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6605       (neway == lev_fieldy - 1 && newax != ax))
6606   {
6607     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6608     Store[newax][neway] = element;
6609   }
6610   else if (neway == ay)
6611   {
6612     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6613 #if 1
6614     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6615 #else
6616     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6617 #endif
6618   }
6619   else
6620   {
6621     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6622     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6623     Store[ax][ay] = EL_AMOEBA_DROP;
6624     ContinueMoving(ax, ay);
6625     return;
6626   }
6627
6628   DrawLevelField(newax, neway);
6629 }
6630
6631 void Life(int ax, int ay)
6632 {
6633   int x1, y1, x2, y2;
6634   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6635   int life_time = 40;
6636   int element = Feld[ax][ay];
6637   int graphic = el2img(element);
6638   boolean changed = FALSE;
6639
6640   if (IS_ANIMATED(graphic))
6641     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6642
6643   if (Stop[ax][ay])
6644     return;
6645
6646   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6647     MovDelay[ax][ay] = life_time;
6648
6649   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6650   {
6651     MovDelay[ax][ay]--;
6652     if (MovDelay[ax][ay])
6653       return;
6654   }
6655
6656   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6657   {
6658     int xx = ax+x1, yy = ay+y1;
6659     int nachbarn = 0;
6660
6661     if (!IN_LEV_FIELD(xx, yy))
6662       continue;
6663
6664     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6665     {
6666       int x = xx+x2, y = yy+y2;
6667
6668       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6669         continue;
6670
6671       if (((Feld[x][y] == element ||
6672             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6673            !Stop[x][y]) ||
6674           (IS_FREE(x, y) && Stop[x][y]))
6675         nachbarn++;
6676     }
6677
6678     if (xx == ax && yy == ay)           /* field in the middle */
6679     {
6680       if (nachbarn < life[0] || nachbarn > life[1])
6681       {
6682         Feld[xx][yy] = EL_EMPTY;
6683         if (!Stop[xx][yy])
6684           DrawLevelField(xx, yy);
6685         Stop[xx][yy] = TRUE;
6686         changed = TRUE;
6687       }
6688     }
6689 #if 1
6690     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6691     {                                   /* free border field */
6692       if (nachbarn >= life[2] && nachbarn <= life[3])
6693       {
6694         Feld[xx][yy] = element;
6695         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6696         if (!Stop[xx][yy])
6697           DrawLevelField(xx, yy);
6698         Stop[xx][yy] = TRUE;
6699         changed = TRUE;
6700       }
6701     }
6702 #else
6703     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6704     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6705     {                                   /* free border field */
6706       if (nachbarn >= life[2] && nachbarn <= life[3])
6707       {
6708         Feld[xx][yy] = element;
6709         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6710         if (!Stop[xx][yy])
6711           DrawLevelField(xx, yy);
6712         Stop[xx][yy] = TRUE;
6713         changed = TRUE;
6714       }
6715     }
6716 #endif
6717   }
6718
6719   if (changed)
6720     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6721                    SND_GAME_OF_LIFE_GROWING);
6722 }
6723
6724 static void InitRobotWheel(int x, int y)
6725 {
6726   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6727 }
6728
6729 static void RunRobotWheel(int x, int y)
6730 {
6731   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6732 }
6733
6734 static void StopRobotWheel(int x, int y)
6735 {
6736   if (ZX == x && ZY == y)
6737     ZX = ZY = -1;
6738 }
6739
6740 static void InitTimegateWheel(int x, int y)
6741 {
6742 #if 1
6743   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6744 #else
6745   /* another brainless, "type style" bug ... :-( */
6746   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6747 #endif
6748 }
6749
6750 static void RunTimegateWheel(int x, int y)
6751 {
6752   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6753 }
6754
6755 void CheckExit(int x, int y)
6756 {
6757   if (local_player->gems_still_needed > 0 ||
6758       local_player->sokobanfields_still_needed > 0 ||
6759       local_player->lights_still_needed > 0)
6760   {
6761     int element = Feld[x][y];
6762     int graphic = el2img(element);
6763
6764     if (IS_ANIMATED(graphic))
6765       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6766
6767     return;
6768   }
6769
6770   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6771     return;
6772
6773   Feld[x][y] = EL_EXIT_OPENING;
6774
6775   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6776 }
6777
6778 void CheckExitSP(int x, int y)
6779 {
6780   if (local_player->gems_still_needed > 0)
6781   {
6782     int element = Feld[x][y];
6783     int graphic = el2img(element);
6784
6785     if (IS_ANIMATED(graphic))
6786       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6787
6788     return;
6789   }
6790
6791   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6792     return;
6793
6794   Feld[x][y] = EL_SP_EXIT_OPENING;
6795
6796   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6797 }
6798
6799 static void CloseAllOpenTimegates()
6800 {
6801   int x, y;
6802
6803   for (y = 0; y < lev_fieldy; y++)
6804   {
6805     for (x = 0; x < lev_fieldx; x++)
6806     {
6807       int element = Feld[x][y];
6808
6809       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6810       {
6811         Feld[x][y] = EL_TIMEGATE_CLOSING;
6812 #if 1
6813         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6814 #else
6815         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6816 #endif
6817       }
6818     }
6819   }
6820 }
6821
6822 void EdelsteinFunkeln(int x, int y)
6823 {
6824   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6825     return;
6826
6827   if (Feld[x][y] == EL_BD_DIAMOND)
6828     return;
6829
6830   if (MovDelay[x][y] == 0)      /* next animation frame */
6831     MovDelay[x][y] = 11 * !SimpleRND(500);
6832
6833   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6834   {
6835     MovDelay[x][y]--;
6836
6837     if (setup.direct_draw && MovDelay[x][y])
6838       SetDrawtoField(DRAW_BUFFERED);
6839
6840     DrawLevelElementAnimation(x, y, Feld[x][y]);
6841
6842     if (MovDelay[x][y] != 0)
6843     {
6844       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6845                                            10 - MovDelay[x][y]);
6846
6847       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6848
6849       if (setup.direct_draw)
6850       {
6851         int dest_x, dest_y;
6852
6853         dest_x = FX + SCREENX(x) * TILEX;
6854         dest_y = FY + SCREENY(y) * TILEY;
6855
6856         BlitBitmap(drawto_field, window,
6857                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6858         SetDrawtoField(DRAW_DIRECT);
6859       }
6860     }
6861   }
6862 }
6863
6864 void MauerWaechst(int x, int y)
6865 {
6866   int delay = 6;
6867
6868   if (!MovDelay[x][y])          /* next animation frame */
6869     MovDelay[x][y] = 3 * delay;
6870
6871   if (MovDelay[x][y])           /* wait some time before next frame */
6872   {
6873     MovDelay[x][y]--;
6874
6875     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6876     {
6877       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6878       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6879
6880       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6881     }
6882
6883     if (!MovDelay[x][y])
6884     {
6885       if (MovDir[x][y] == MV_LEFT)
6886       {
6887         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6888           DrawLevelField(x - 1, y);
6889       }
6890       else if (MovDir[x][y] == MV_RIGHT)
6891       {
6892         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6893           DrawLevelField(x + 1, y);
6894       }
6895       else if (MovDir[x][y] == MV_UP)
6896       {
6897         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6898           DrawLevelField(x, y - 1);
6899       }
6900       else
6901       {
6902         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6903           DrawLevelField(x, y + 1);
6904       }
6905
6906       Feld[x][y] = Store[x][y];
6907       Store[x][y] = 0;
6908       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6909       DrawLevelField(x, y);
6910     }
6911   }
6912 }
6913
6914 void MauerAbleger(int ax, int ay)
6915 {
6916   int element = Feld[ax][ay];
6917   int graphic = el2img(element);
6918   boolean oben_frei = FALSE, unten_frei = FALSE;
6919   boolean links_frei = FALSE, rechts_frei = FALSE;
6920   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6921   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6922   boolean new_wall = FALSE;
6923
6924   if (IS_ANIMATED(graphic))
6925     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6926
6927   if (!MovDelay[ax][ay])        /* start building new wall */
6928     MovDelay[ax][ay] = 6;
6929
6930   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6931   {
6932     MovDelay[ax][ay]--;
6933     if (MovDelay[ax][ay])
6934       return;
6935   }
6936
6937   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6938     oben_frei = TRUE;
6939   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6940     unten_frei = TRUE;
6941   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6942     links_frei = TRUE;
6943   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6944     rechts_frei = TRUE;
6945
6946   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6947       element == EL_EXPANDABLE_WALL_ANY)
6948   {
6949     if (oben_frei)
6950     {
6951       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6952       Store[ax][ay-1] = element;
6953       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6954       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6955         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6956                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6957       new_wall = TRUE;
6958     }
6959     if (unten_frei)
6960     {
6961       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6962       Store[ax][ay+1] = element;
6963       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6964       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6965         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6966                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6967       new_wall = TRUE;
6968     }
6969   }
6970
6971   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6972       element == EL_EXPANDABLE_WALL_ANY ||
6973       element == EL_EXPANDABLE_WALL)
6974   {
6975     if (links_frei)
6976     {
6977       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6978       Store[ax-1][ay] = element;
6979       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6980       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6981         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6982                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6983       new_wall = TRUE;
6984     }
6985
6986     if (rechts_frei)
6987     {
6988       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6989       Store[ax+1][ay] = element;
6990       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6991       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6992         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6993                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6994       new_wall = TRUE;
6995     }
6996   }
6997
6998   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6999     DrawLevelField(ax, ay);
7000
7001   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7002     oben_massiv = TRUE;
7003   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7004     unten_massiv = TRUE;
7005   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7006     links_massiv = TRUE;
7007   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7008     rechts_massiv = TRUE;
7009
7010   if (((oben_massiv && unten_massiv) ||
7011        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7012        element == EL_EXPANDABLE_WALL) &&
7013       ((links_massiv && rechts_massiv) ||
7014        element == EL_EXPANDABLE_WALL_VERTICAL))
7015     Feld[ax][ay] = EL_WALL;
7016
7017   if (new_wall)
7018 #if 1
7019     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7020 #else
7021     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7022 #endif
7023 }
7024
7025 void CheckForDragon(int x, int y)
7026 {
7027   int i, j;
7028   boolean dragon_found = FALSE;
7029   static int xy[4][2] =
7030   {
7031     { 0, -1 },
7032     { -1, 0 },
7033     { +1, 0 },
7034     { 0, +1 }
7035   };
7036
7037   for (i = 0; i < NUM_DIRECTIONS; i++)
7038   {
7039     for (j = 0; j < 4; j++)
7040     {
7041       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7042
7043       if (IN_LEV_FIELD(xx, yy) &&
7044           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7045       {
7046         if (Feld[xx][yy] == EL_DRAGON)
7047           dragon_found = TRUE;
7048       }
7049       else
7050         break;
7051     }
7052   }
7053
7054   if (!dragon_found)
7055   {
7056     for (i = 0; i < NUM_DIRECTIONS; i++)
7057     {
7058       for (j = 0; j < 3; j++)
7059       {
7060         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7061   
7062         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7063         {
7064           Feld[xx][yy] = EL_EMPTY;
7065           DrawLevelField(xx, yy);
7066         }
7067         else
7068           break;
7069       }
7070     }
7071   }
7072 }
7073
7074 static void InitBuggyBase(int x, int y)
7075 {
7076   int element = Feld[x][y];
7077   int activating_delay = FRAMES_PER_SECOND / 4;
7078
7079   ChangeDelay[x][y] =
7080     (element == EL_SP_BUGGY_BASE ?
7081      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7082      element == EL_SP_BUGGY_BASE_ACTIVATING ?
7083      activating_delay :
7084      element == EL_SP_BUGGY_BASE_ACTIVE ?
7085      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7086 }
7087
7088 static void WarnBuggyBase(int x, int y)
7089 {
7090   int i;
7091   static int xy[4][2] =
7092   {
7093     { 0, -1 },
7094     { -1, 0 },
7095     { +1, 0 },
7096     { 0, +1 }
7097   };
7098
7099   for (i = 0; i < NUM_DIRECTIONS; i++)
7100   {
7101     int xx = x + xy[i][0], yy = y + xy[i][1];
7102
7103     if (IS_PLAYER(xx, yy))
7104     {
7105       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7106
7107       break;
7108     }
7109   }
7110 }
7111
7112 static void InitTrap(int x, int y)
7113 {
7114   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7115 }
7116
7117 static void ActivateTrap(int x, int y)
7118 {
7119   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7120 }
7121
7122 static void ChangeActiveTrap(int x, int y)
7123 {
7124   int graphic = IMG_TRAP_ACTIVE;
7125
7126   /* if new animation frame was drawn, correct crumbled sand border */
7127   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7128     DrawLevelFieldCrumbledSand(x, y);
7129 }
7130
7131 static void ChangeElementNowExt(int x, int y, int target_element)
7132 {
7133   int previous_move_direction = MovDir[x][y];
7134
7135   /* check if element under player changes from accessible to unaccessible
7136      (needed for special case of dropping element which then changes) */
7137   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7138       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7139   {
7140     Bang(x, y);
7141     return;
7142   }
7143
7144   RemoveField(x, y);
7145   Feld[x][y] = target_element;
7146
7147   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7148
7149   ResetGfxAnimation(x, y);
7150   ResetRandomAnimationValue(x, y);
7151
7152   if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7153     MovDir[x][y] = previous_move_direction;
7154
7155 #if 1
7156   InitField_WithBug1(x, y, FALSE);
7157 #else
7158   InitField(x, y, FALSE);
7159   if (CAN_MOVE(Feld[x][y]))
7160     InitMovDir(x, y);
7161 #endif
7162
7163   DrawLevelField(x, y);
7164
7165   if (GFX_CRUMBLED(Feld[x][y]))
7166     DrawLevelFieldCrumbledSandNeighbours(x, y);
7167
7168   TestIfBadThingTouchesHero(x, y);
7169   TestIfPlayerTouchesCustomElement(x, y);
7170   TestIfElementTouchesCustomElement(x, y);
7171
7172   if (ELEM_IS_PLAYER(target_element))
7173     RelocatePlayer(x, y, target_element);
7174 }
7175
7176 static boolean ChangeElementNow(int x, int y, int element, int page)
7177 {
7178   struct ElementChangeInfo *change = &element_info[element].change_page[page];
7179   int target_element;
7180
7181   /* always use default change event to prevent running into a loop */
7182   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7183     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7184
7185   if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7186   {
7187     /* reset actual trigger element and player */
7188     change->actual_trigger_element = EL_EMPTY;
7189     change->actual_trigger_player = EL_PLAYER_1;
7190   }
7191
7192   /* do not change already changed elements with same change event */
7193 #if 0
7194   if (Changed[x][y] & ChangeEvent[x][y])
7195     return FALSE;
7196 #else
7197   if (Changed[x][y])
7198     return FALSE;
7199 #endif
7200
7201   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7202
7203   CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7204
7205   if (change->explode)
7206   {
7207     Bang(x, y);
7208
7209     return TRUE;
7210   }
7211
7212   if (change->use_target_content)
7213   {
7214     boolean complete_replace = TRUE;
7215     boolean can_replace[3][3];
7216     int xx, yy;
7217
7218     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7219     {
7220       boolean is_empty;
7221       boolean is_diggable;
7222       boolean is_destructible;
7223       int ex = x + xx - 1;
7224       int ey = y + yy - 1;
7225       int content_element = change->target_content[xx][yy];
7226       int e;
7227
7228       can_replace[xx][yy] = TRUE;
7229
7230       if (ex == x && ey == y)   /* do not check changing element itself */
7231         continue;
7232
7233       if (content_element == EL_EMPTY_SPACE)
7234       {
7235         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
7236
7237         continue;
7238       }
7239
7240       if (!IN_LEV_FIELD(ex, ey))
7241       {
7242         can_replace[xx][yy] = FALSE;
7243         complete_replace = FALSE;
7244
7245         continue;
7246       }
7247
7248       e = Feld[ex][ey];
7249
7250       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7251         e = MovingOrBlocked2Element(ex, ey);
7252
7253 #if 1
7254
7255 #if 1
7256       is_empty = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7257                                       IS_WALKABLE(content_element)));
7258 #else
7259       is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7260                                       IS_WALKABLE(content_element)));
7261 #endif
7262       is_diggable = (is_empty || IS_DIGGABLE(e));
7263       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7264
7265       can_replace[xx][yy] =
7266         ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7267          (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7268          (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7269
7270       if (!can_replace[xx][yy])
7271         complete_replace = FALSE;
7272 #else
7273       empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7274                                                IS_WALKABLE(content_element)));
7275 #if 1
7276       half_destructible = (empty_for_element || IS_DIGGABLE(e));
7277 #else
7278       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7279 #endif
7280
7281       if ((change->replace_when <= CP_WHEN_EMPTY  && !empty_for_element) ||
7282           (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7283           (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7284       {
7285         can_replace[xx][yy] = FALSE;
7286         complete_replace = FALSE;
7287       }
7288 #endif
7289     }
7290
7291     if (!change->only_if_complete || complete_replace)
7292     {
7293       boolean something_has_changed = FALSE;
7294
7295       if (change->only_if_complete && change->use_random_replace &&
7296           RND(100) < change->random_percentage)
7297         return FALSE;
7298
7299       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7300       {
7301         int ex = x + xx - 1;
7302         int ey = y + yy - 1;
7303         int content_element;
7304
7305         if (can_replace[xx][yy] && (!change->use_random_replace ||
7306                                     RND(100) < change->random_percentage))
7307         {
7308           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7309             RemoveMovingField(ex, ey);
7310
7311           ChangeEvent[ex][ey] = ChangeEvent[x][y];
7312
7313           content_element = change->target_content[xx][yy];
7314           target_element = GET_TARGET_ELEMENT(content_element, change);
7315
7316           ChangeElementNowExt(ex, ey, target_element);
7317
7318           something_has_changed = TRUE;
7319
7320           /* for symmetry reasons, freeze newly created border elements */
7321           if (ex != x || ey != y)
7322             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
7323         }
7324       }
7325
7326       if (something_has_changed)
7327         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7328     }
7329   }
7330   else
7331   {
7332     target_element = GET_TARGET_ELEMENT(change->target_element, change);
7333
7334     ChangeElementNowExt(x, y, target_element);
7335
7336     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7337   }
7338
7339   return TRUE;
7340 }
7341
7342 static void ChangeElement(int x, int y, int page)
7343 {
7344   int element = MovingOrBlocked2Element(x, y);
7345   struct ElementInfo *ei = &element_info[element];
7346   struct ElementChangeInfo *change = &ei->change_page[page];
7347
7348 #ifdef DEBUG
7349   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7350   {
7351     printf("\n\n");
7352     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7353            x, y, element, element_info[element].token_name);
7354     printf("ChangeElement(): This should never happen!\n");
7355     printf("\n\n");
7356   }
7357 #endif
7358
7359   /* this can happen with classic bombs on walkable, changing elements */
7360   if (!CAN_CHANGE(element))
7361   {
7362 #if 0
7363     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7364       ChangeDelay[x][y] = 0;
7365 #endif
7366
7367     return;
7368   }
7369
7370   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7371   {
7372     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
7373                          RND(change->delay_random * change->delay_frames)) + 1;
7374
7375     ResetGfxAnimation(x, y);
7376     ResetRandomAnimationValue(x, y);
7377
7378     if (change->pre_change_function)
7379       change->pre_change_function(x, y);
7380   }
7381
7382   ChangeDelay[x][y]--;
7383
7384   if (ChangeDelay[x][y] != 0)           /* continue element change */
7385   {
7386     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7387
7388     if (IS_ANIMATED(graphic))
7389       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7390
7391     if (change->change_function)
7392       change->change_function(x, y);
7393   }
7394   else                                  /* finish element change */
7395   {
7396     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7397     {
7398       page = ChangePage[x][y];
7399       ChangePage[x][y] = -1;
7400
7401       change = &ei->change_page[page];
7402     }
7403
7404 #if 0
7405     if (IS_MOVING(x, y) && !change->explode)
7406 #else
7407     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7408 #endif
7409     {
7410       ChangeDelay[x][y] = 1;            /* try change after next move step */
7411       ChangePage[x][y] = page;          /* remember page to use for change */
7412
7413       return;
7414     }
7415
7416     if (ChangeElementNow(x, y, element, page))
7417     {
7418       if (change->post_change_function)
7419         change->post_change_function(x, y);
7420     }
7421   }
7422 }
7423
7424 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7425                                               int trigger_element,
7426                                               int trigger_event,
7427                                               int trigger_player,
7428                                               int trigger_side,
7429                                               int trigger_page)
7430 {
7431   int i, j, x, y;
7432   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7433
7434   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7435     return FALSE;
7436
7437   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7438   {
7439     int element = EL_CUSTOM_START + i;
7440
7441     boolean change_element = FALSE;
7442     int page = 0;
7443
7444     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7445       continue;
7446
7447     for (j = 0; j < element_info[element].num_change_pages; j++)
7448     {
7449       struct ElementChangeInfo *change = &element_info[element].change_page[j];
7450
7451       if (change->can_change &&
7452           change->events & CH_EVENT_BIT(trigger_event) &&
7453           change->trigger_side & trigger_side &&
7454           change->trigger_player & trigger_player &&
7455           change->trigger_page & trigger_page_bits &&
7456           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7457       {
7458 #if 0
7459         if (!(change->events & CH_EVENT_BIT(trigger_event)))
7460           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7461                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7462 #endif
7463
7464         change_element = TRUE;
7465         page = j;
7466
7467         change->actual_trigger_element = trigger_element;
7468         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7469
7470         break;
7471       }
7472     }
7473
7474     if (!change_element)
7475       continue;
7476
7477     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7478     {
7479 #if 0
7480       if (x == lx && y == ly)   /* do not change trigger element itself */
7481         continue;
7482 #endif
7483
7484       if (Feld[x][y] == element)
7485       {
7486         ChangeDelay[x][y] = 1;
7487         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7488         ChangeElement(x, y, page);
7489       }
7490     }
7491   }
7492
7493   return TRUE;
7494 }
7495
7496 static boolean CheckElementChangeExt(int x, int y,
7497                                      int element,
7498                                      int trigger_element,
7499                                      int trigger_event,
7500                                      int trigger_player,
7501                                      int trigger_side,
7502                                      int trigger_page)
7503 {
7504   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7505     return FALSE;
7506
7507   if (Feld[x][y] == EL_BLOCKED)
7508   {
7509     Blocked2Moving(x, y, &x, &y);
7510     element = Feld[x][y];
7511   }
7512
7513 #if 1
7514   if (Feld[x][y] != element)    /* check if element has already changed */
7515   {
7516 #if 0
7517     printf("::: %d ('%s') != %d ('%s') [%d]\n",
7518            Feld[x][y], element_info[Feld[x][y]].token_name,
7519            element, element_info[element].token_name,
7520            trigger_event);
7521 #endif
7522
7523     return FALSE;
7524   }
7525 #endif
7526
7527 #if 1
7528   if (trigger_page < 0)
7529   {
7530     boolean change_element = FALSE;
7531     int i;
7532
7533     for (i = 0; i < element_info[element].num_change_pages; i++)
7534     {
7535       struct ElementChangeInfo *change = &element_info[element].change_page[i];
7536
7537       if (change->can_change &&
7538           change->events & CH_EVENT_BIT(trigger_event) &&
7539           change->trigger_side & trigger_side &&
7540           change->trigger_player & trigger_player)
7541       {
7542         change_element = TRUE;
7543         trigger_page = i;
7544
7545         change->actual_trigger_element = trigger_element;
7546         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7547
7548         break;
7549       }
7550     }
7551
7552     if (!change_element)
7553       return FALSE;
7554   }
7555   else
7556   {
7557     struct ElementInfo *ei = &element_info[element];
7558     struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7559
7560     change->actual_trigger_element = trigger_element;
7561     change->actual_trigger_player = EL_PLAYER_1;        /* unused */
7562   }
7563
7564 #else
7565
7566   /* !!! this check misses pages with same event, but different side !!! */
7567
7568   if (trigger_page < 0)
7569     trigger_page = element_info[element].event_page_nr[trigger_event];
7570
7571   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7572     return FALSE;
7573 #endif
7574
7575   ChangeDelay[x][y] = 1;
7576   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7577   ChangeElement(x, y, trigger_page);
7578
7579   return TRUE;
7580 }
7581
7582 static void PlayPlayerSound(struct PlayerInfo *player)
7583 {
7584   int jx = player->jx, jy = player->jy;
7585   int element = player->element_nr;
7586   int last_action = player->last_action_waiting;
7587   int action = player->action_waiting;
7588
7589   if (player->is_waiting)
7590   {
7591     if (action != last_action)
7592       PlayLevelSoundElementAction(jx, jy, element, action);
7593     else
7594       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7595   }
7596   else
7597   {
7598     if (action != last_action)
7599       StopSound(element_info[element].sound[last_action]);
7600
7601     if (last_action == ACTION_SLEEPING)
7602       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7603   }
7604 }
7605
7606 static void PlayAllPlayersSound()
7607 {
7608   int i;
7609
7610   for (i = 0; i < MAX_PLAYERS; i++)
7611     if (stored_player[i].active)
7612       PlayPlayerSound(&stored_player[i]);
7613 }
7614
7615 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7616 {
7617   boolean last_waiting = player->is_waiting;
7618   int move_dir = player->MovDir;
7619
7620   player->last_action_waiting = player->action_waiting;
7621
7622   if (is_waiting)
7623   {
7624     if (!last_waiting)          /* not waiting -> waiting */
7625     {
7626       player->is_waiting = TRUE;
7627
7628       player->frame_counter_bored =
7629         FrameCounter +
7630         game.player_boring_delay_fixed +
7631         SimpleRND(game.player_boring_delay_random);
7632       player->frame_counter_sleeping =
7633         FrameCounter +
7634         game.player_sleeping_delay_fixed +
7635         SimpleRND(game.player_sleeping_delay_random);
7636
7637       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7638     }
7639
7640     if (game.player_sleeping_delay_fixed +
7641         game.player_sleeping_delay_random > 0 &&
7642         player->anim_delay_counter == 0 &&
7643         player->post_delay_counter == 0 &&
7644         FrameCounter >= player->frame_counter_sleeping)
7645       player->is_sleeping = TRUE;
7646     else if (game.player_boring_delay_fixed +
7647              game.player_boring_delay_random > 0 &&
7648              FrameCounter >= player->frame_counter_bored)
7649       player->is_bored = TRUE;
7650
7651     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7652                               player->is_bored ? ACTION_BORING :
7653                               ACTION_WAITING);
7654
7655     if (player->is_sleeping)
7656     {
7657       if (player->num_special_action_sleeping > 0)
7658       {
7659         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7660         {
7661           int last_special_action = player->special_action_sleeping;
7662           int num_special_action = player->num_special_action_sleeping;
7663           int special_action =
7664             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7665              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7666              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7667              last_special_action + 1 : ACTION_SLEEPING);
7668           int special_graphic =
7669             el_act_dir2img(player->element_nr, special_action, move_dir);
7670
7671           player->anim_delay_counter =
7672             graphic_info[special_graphic].anim_delay_fixed +
7673             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7674           player->post_delay_counter =
7675             graphic_info[special_graphic].post_delay_fixed +
7676             SimpleRND(graphic_info[special_graphic].post_delay_random);
7677
7678           player->special_action_sleeping = special_action;
7679         }
7680
7681         if (player->anim_delay_counter > 0)
7682         {
7683           player->action_waiting = player->special_action_sleeping;
7684           player->anim_delay_counter--;
7685         }
7686         else if (player->post_delay_counter > 0)
7687         {
7688           player->post_delay_counter--;
7689         }
7690       }
7691     }
7692     else if (player->is_bored)
7693     {
7694       if (player->num_special_action_bored > 0)
7695       {
7696         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7697         {
7698           int special_action =
7699             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7700           int special_graphic =
7701             el_act_dir2img(player->element_nr, special_action, move_dir);
7702
7703           player->anim_delay_counter =
7704             graphic_info[special_graphic].anim_delay_fixed +
7705             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7706           player->post_delay_counter =
7707             graphic_info[special_graphic].post_delay_fixed +
7708             SimpleRND(graphic_info[special_graphic].post_delay_random);
7709
7710           player->special_action_bored = special_action;
7711         }
7712
7713         if (player->anim_delay_counter > 0)
7714         {
7715           player->action_waiting = player->special_action_bored;
7716           player->anim_delay_counter--;
7717         }
7718         else if (player->post_delay_counter > 0)
7719         {
7720           player->post_delay_counter--;
7721         }
7722       }
7723     }
7724   }
7725   else if (last_waiting)        /* waiting -> not waiting */
7726   {
7727     player->is_waiting = FALSE;
7728     player->is_bored = FALSE;
7729     player->is_sleeping = FALSE;
7730
7731     player->frame_counter_bored = -1;
7732     player->frame_counter_sleeping = -1;
7733
7734     player->anim_delay_counter = 0;
7735     player->post_delay_counter = 0;
7736
7737     player->action_waiting = ACTION_DEFAULT;
7738
7739     player->special_action_bored = ACTION_DEFAULT;
7740     player->special_action_sleeping = ACTION_DEFAULT;
7741   }
7742 }
7743
7744 #if 1
7745 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7746 {
7747 #if 0
7748   static byte stored_player_action[MAX_PLAYERS];
7749   static int num_stored_actions = 0;
7750 #endif
7751   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7752   int left      = player_action & JOY_LEFT;
7753   int right     = player_action & JOY_RIGHT;
7754   int up        = player_action & JOY_UP;
7755   int down      = player_action & JOY_DOWN;
7756   int button1   = player_action & JOY_BUTTON_1;
7757   int button2   = player_action & JOY_BUTTON_2;
7758   int dx        = (left ? -1    : right ? 1     : 0);
7759   int dy        = (up   ? -1    : down  ? 1     : 0);
7760
7761 #if 0
7762   stored_player_action[player->index_nr] = 0;
7763   num_stored_actions++;
7764 #endif
7765
7766 #if 0
7767   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7768 #endif
7769
7770   if (!player->active || tape.pausing)
7771     return 0;
7772
7773 #if 0
7774   printf("::: [%d %d %d %d] [%d %d]\n",
7775          left, right, up, down, button1, button2);
7776 #endif
7777
7778   if (player_action)
7779   {
7780 #if 0
7781     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7782 #endif
7783
7784 #if 0
7785     /* !!! TEST !!! */
7786     if (player->MovPos == 0)
7787       CheckGravityMovement(player);
7788 #endif
7789     if (button1)
7790       snapped = SnapField(player, dx, dy);
7791     else
7792     {
7793       if (button2)
7794         dropped = DropElement(player);
7795
7796       moved = MovePlayer(player, dx, dy);
7797     }
7798
7799     if (tape.single_step && tape.recording && !tape.pausing)
7800     {
7801       if (button1 || (dropped && !moved))
7802       {
7803         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7804         SnapField(player, 0, 0);                /* stop snapping */
7805       }
7806     }
7807
7808     SetPlayerWaiting(player, FALSE);
7809
7810 #if 1
7811     return player_action;
7812 #else
7813     stored_player_action[player->index_nr] = player_action;
7814 #endif
7815   }
7816   else
7817   {
7818 #if 0
7819     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7820 #endif
7821
7822     /* no actions for this player (no input at player's configured device) */
7823
7824     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7825     SnapField(player, 0, 0);
7826     CheckGravityMovementWhenNotMoving(player);
7827
7828     if (player->MovPos == 0)
7829       SetPlayerWaiting(player, TRUE);
7830
7831     if (player->MovPos == 0)    /* needed for tape.playing */
7832       player->is_moving = FALSE;
7833
7834     player->is_dropping = FALSE;
7835
7836     return 0;
7837   }
7838
7839 #if 0
7840   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7841   {
7842     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7843
7844     TapeRecordAction(stored_player_action);
7845     num_stored_actions = 0;
7846   }
7847 #endif
7848 }
7849
7850 #else
7851
7852 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7853 {
7854   static byte stored_player_action[MAX_PLAYERS];
7855   static int num_stored_actions = 0;
7856   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7857   int left      = player_action & JOY_LEFT;
7858   int right     = player_action & JOY_RIGHT;
7859   int up        = player_action & JOY_UP;
7860   int down      = player_action & JOY_DOWN;
7861   int button1   = player_action & JOY_BUTTON_1;
7862   int button2   = player_action & JOY_BUTTON_2;
7863   int dx        = (left ? -1    : right ? 1     : 0);
7864   int dy        = (up   ? -1    : down  ? 1     : 0);
7865
7866   stored_player_action[player->index_nr] = 0;
7867   num_stored_actions++;
7868
7869   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7870
7871   if (!player->active || tape.pausing)
7872     return;
7873
7874   if (player_action)
7875   {
7876     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7877
7878     if (button1)
7879       snapped = SnapField(player, dx, dy);
7880     else
7881     {
7882       if (button2)
7883         dropped = DropElement(player);
7884
7885       moved = MovePlayer(player, dx, dy);
7886     }
7887
7888     if (tape.single_step && tape.recording && !tape.pausing)
7889     {
7890       if (button1 || (dropped && !moved))
7891       {
7892         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7893         SnapField(player, 0, 0);                /* stop snapping */
7894       }
7895     }
7896
7897     stored_player_action[player->index_nr] = player_action;
7898   }
7899   else
7900   {
7901     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7902
7903     /* no actions for this player (no input at player's configured device) */
7904
7905     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7906     SnapField(player, 0, 0);
7907     CheckGravityMovementWhenNotMoving(player);
7908
7909     if (player->MovPos == 0)
7910       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7911
7912     if (player->MovPos == 0)    /* needed for tape.playing */
7913       player->is_moving = FALSE;
7914   }
7915
7916   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7917   {
7918     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7919
7920     TapeRecordAction(stored_player_action);
7921     num_stored_actions = 0;
7922   }
7923 }
7924 #endif
7925
7926 void GameActions()
7927 {
7928   static unsigned long action_delay = 0;
7929   unsigned long action_delay_value;
7930   int magic_wall_x = 0, magic_wall_y = 0;
7931   int i, x, y, element, graphic;
7932   byte *recorded_player_action;
7933   byte summarized_player_action = 0;
7934 #if 1
7935   byte tape_action[MAX_PLAYERS];
7936 #endif
7937
7938   if (game_status != GAME_MODE_PLAYING)
7939     return;
7940
7941   action_delay_value =
7942     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7943
7944   if (tape.playing && tape.warp_forward && !tape.pausing)
7945     action_delay_value = 0;
7946
7947   /* ---------- main game synchronization point ---------- */
7948
7949   WaitUntilDelayReached(&action_delay, action_delay_value);
7950
7951   if (network_playing && !network_player_action_received)
7952   {
7953     /*
7954 #ifdef DEBUG
7955     printf("DEBUG: try to get network player actions in time\n");
7956 #endif
7957     */
7958
7959 #if defined(PLATFORM_UNIX)
7960     /* last chance to get network player actions without main loop delay */
7961     HandleNetworking();
7962 #endif
7963
7964     if (game_status != GAME_MODE_PLAYING)
7965       return;
7966
7967     if (!network_player_action_received)
7968     {
7969       /*
7970 #ifdef DEBUG
7971       printf("DEBUG: failed to get network player actions in time\n");
7972 #endif
7973       */
7974       return;
7975     }
7976   }
7977
7978   if (tape.pausing)
7979     return;
7980
7981 #if 0
7982   printf("::: getting new tape action [%d]\n", FrameCounter);
7983 #endif
7984
7985   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7986
7987 #if 1
7988   if (recorded_player_action == NULL && tape.pausing)
7989     return;
7990 #endif
7991
7992 #if 0
7993   printf("::: %d\n", stored_player[0].action);
7994 #endif
7995
7996 #if 0
7997   if (recorded_player_action != NULL)
7998     for (i = 0; i < MAX_PLAYERS; i++)
7999       stored_player[i].action = recorded_player_action[i];
8000 #endif
8001
8002   for (i = 0; i < MAX_PLAYERS; i++)
8003   {
8004     summarized_player_action |= stored_player[i].action;
8005
8006     if (!network_playing)
8007       stored_player[i].effective_action = stored_player[i].action;
8008   }
8009
8010 #if defined(PLATFORM_UNIX)
8011   if (network_playing)
8012     SendToServer_MovePlayer(summarized_player_action);
8013 #endif
8014
8015   if (!options.network && !setup.team_mode)
8016     local_player->effective_action = summarized_player_action;
8017
8018 #if 1
8019   if (recorded_player_action != NULL)
8020     for (i = 0; i < MAX_PLAYERS; i++)
8021       stored_player[i].effective_action = recorded_player_action[i];
8022 #endif
8023
8024 #if 1
8025   for (i = 0; i < MAX_PLAYERS; i++)
8026   {
8027     tape_action[i] = stored_player[i].effective_action;
8028
8029     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8030       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8031   }
8032
8033   /* only save actions from input devices, but not programmed actions */
8034   if (tape.recording)
8035     TapeRecordAction(tape_action);
8036 #endif
8037
8038   for (i = 0; i < MAX_PLAYERS; i++)
8039   {
8040     int actual_player_action = stored_player[i].effective_action;
8041
8042 #if 1
8043     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8044        - rnd_equinox_tetrachloride 048
8045        - rnd_equinox_tetrachloride_ii 096
8046        - rnd_emanuel_schmieg 002
8047        - doctor_sloan_ww 001, 020
8048     */
8049     if (stored_player[i].MovPos == 0)
8050       CheckGravityMovement(&stored_player[i]);
8051 #endif
8052
8053 #if 1
8054     /* overwrite programmed action with tape action */
8055     if (stored_player[i].programmed_action)
8056       actual_player_action = stored_player[i].programmed_action;
8057 #endif
8058
8059 #if 0
8060     if (stored_player[i].programmed_action)
8061       printf("::: %d\n", stored_player[i].programmed_action);
8062 #endif
8063
8064     if (recorded_player_action)
8065     {
8066 #if 0
8067       if (stored_player[i].programmed_action &&
8068           stored_player[i].programmed_action != recorded_player_action[i])
8069         printf("::: %d: %d <-> %d\n", i,
8070                stored_player[i].programmed_action, recorded_player_action[i]);
8071 #endif
8072
8073 #if 0
8074       actual_player_action = recorded_player_action[i];
8075 #endif
8076     }
8077
8078 #if 0
8079     /* overwrite tape action with programmed action */
8080     if (stored_player[i].programmed_action)
8081       actual_player_action = stored_player[i].programmed_action;
8082 #endif
8083
8084 #if 0
8085     if (i == 0)
8086       printf("::: action: %d: %x [%d]\n",
8087              stored_player[i].MovPos, actual_player_action, FrameCounter);
8088 #endif
8089
8090 #if 1
8091     PlayerActions(&stored_player[i], actual_player_action);
8092 #else
8093     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8094
8095     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8096       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8097 #endif
8098
8099     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8100   }
8101
8102 #if 0
8103   if (tape.recording)
8104     TapeRecordAction(tape_action);
8105 #endif
8106
8107   network_player_action_received = FALSE;
8108
8109   ScrollScreen(NULL, SCROLL_GO_ON);
8110
8111 #if 0
8112   FrameCounter++;
8113   TimeFrames++;
8114
8115   for (i = 0; i < MAX_PLAYERS; i++)
8116     stored_player[i].Frame++;
8117 #endif
8118
8119 #if 1
8120   /* for downwards compatibility, the following code emulates a fixed bug that
8121      occured when pushing elements (causing elements that just made their last
8122      pushing step to already (if possible) make their first falling step in the
8123      same game frame, which is bad); this code is also needed to use the famous
8124      "spring push bug" which is used in older levels and might be wanted to be
8125      used also in newer levels, but in this case the buggy pushing code is only
8126      affecting the "spring" element and no other elements */
8127
8128 #if 1
8129   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8130 #else
8131   if (game.engine_version < VERSION_IDENT(2,2,0,7))
8132 #endif
8133   {
8134     for (i = 0; i < MAX_PLAYERS; i++)
8135     {
8136       struct PlayerInfo *player = &stored_player[i];
8137       int x = player->jx;
8138       int y = player->jy;
8139
8140 #if 1
8141       if (player->active && player->is_pushing && player->is_moving &&
8142           IS_MOVING(x, y) &&
8143           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8144            Feld[x][y] == EL_SPRING))
8145 #else
8146       if (player->active && player->is_pushing && player->is_moving &&
8147           IS_MOVING(x, y))
8148 #endif
8149       {
8150         ContinueMoving(x, y);
8151
8152         /* continue moving after pushing (this is actually a bug) */
8153         if (!IS_MOVING(x, y))
8154         {
8155           Stop[x][y] = FALSE;
8156         }
8157       }
8158     }
8159   }
8160 #endif
8161
8162   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8163   {
8164     Changed[x][y] = CE_BITMASK_DEFAULT;
8165     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8166
8167 #if DEBUG
8168     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8169     {
8170       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8171       printf("GameActions(): This should never happen!\n");
8172
8173       ChangePage[x][y] = -1;
8174     }
8175 #endif
8176
8177     Stop[x][y] = FALSE;
8178     if (WasJustMoving[x][y] > 0)
8179       WasJustMoving[x][y]--;
8180     if (WasJustFalling[x][y] > 0)
8181       WasJustFalling[x][y]--;
8182
8183     GfxFrame[x][y]++;
8184
8185 #if 1
8186     /* reset finished pushing action (not done in ContinueMoving() to allow
8187        continous pushing animation for elements with zero push delay) */
8188     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8189     {
8190       ResetGfxAnimation(x, y);
8191       DrawLevelField(x, y);
8192     }
8193 #endif
8194
8195 #if DEBUG
8196     if (IS_BLOCKED(x, y))
8197     {
8198       int oldx, oldy;
8199
8200       Blocked2Moving(x, y, &oldx, &oldy);
8201       if (!IS_MOVING(oldx, oldy))
8202       {
8203         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8204         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8205         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8206         printf("GameActions(): This should never happen!\n");
8207       }
8208     }
8209 #endif
8210   }
8211
8212   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8213   {
8214     element = Feld[x][y];
8215 #if 1
8216     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8217 #else
8218     graphic = el2img(element);
8219 #endif
8220
8221 #if 0
8222     if (element == -1)
8223     {
8224       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8225
8226       element = graphic = 0;
8227     }
8228 #endif
8229
8230     if (graphic_info[graphic].anim_global_sync)
8231       GfxFrame[x][y] = FrameCounter;
8232
8233     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8234         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8235       ResetRandomAnimationValue(x, y);
8236
8237     SetRandomAnimationValue(x, y);
8238
8239 #if 1
8240     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8241 #endif
8242
8243     if (IS_INACTIVE(element))
8244     {
8245       if (IS_ANIMATED(graphic))
8246         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8247
8248       continue;
8249     }
8250
8251 #if 1
8252     /* this may take place after moving, so 'element' may have changed */
8253 #if 0
8254     if (IS_CHANGING(x, y))
8255 #else
8256     if (IS_CHANGING(x, y) &&
8257         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8258 #endif
8259     {
8260 #if 0
8261       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8262                     element_info[element].event_page_nr[CE_DELAY]);
8263 #else
8264       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8265 #endif
8266
8267       element = Feld[x][y];
8268       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8269     }
8270 #endif
8271
8272     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8273     {
8274       StartMoving(x, y);
8275
8276 #if 1
8277       element = Feld[x][y];
8278       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8279 #if 0
8280       if (element == EL_MOLE)
8281         printf("::: %d, %d, %d [%d]\n",
8282                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8283                GfxAction[x][y]);
8284 #endif
8285 #if 0
8286       if (element == EL_YAMYAM)
8287         printf("::: %d, %d, %d\n",
8288                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8289 #endif
8290 #endif
8291
8292       if (IS_ANIMATED(graphic) &&
8293           !IS_MOVING(x, y) &&
8294           !Stop[x][y])
8295       {
8296         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8297
8298 #if 0
8299         if (element == EL_BUG)
8300           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8301 #endif
8302
8303 #if 0
8304         if (element == EL_MOLE)
8305           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8306 #endif
8307       }
8308
8309       if (IS_GEM(element) || element == EL_SP_INFOTRON)
8310         EdelsteinFunkeln(x, y);
8311     }
8312     else if ((element == EL_ACID ||
8313               element == EL_EXIT_OPEN ||
8314               element == EL_SP_EXIT_OPEN ||
8315               element == EL_SP_TERMINAL ||
8316               element == EL_SP_TERMINAL_ACTIVE ||
8317               element == EL_EXTRA_TIME ||
8318               element == EL_SHIELD_NORMAL ||
8319               element == EL_SHIELD_DEADLY) &&
8320              IS_ANIMATED(graphic))
8321       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8322     else if (IS_MOVING(x, y))
8323       ContinueMoving(x, y);
8324     else if (IS_ACTIVE_BOMB(element))
8325       CheckDynamite(x, y);
8326 #if 0
8327     else if (element == EL_EXPLOSION && !game.explosions_delayed)
8328       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8329 #endif
8330     else if (element == EL_AMOEBA_GROWING)
8331       AmoebeWaechst(x, y);
8332     else if (element == EL_AMOEBA_SHRINKING)
8333       AmoebaDisappearing(x, y);
8334
8335 #if !USE_NEW_AMOEBA_CODE
8336     else if (IS_AMOEBALIVE(element))
8337       AmoebeAbleger(x, y);
8338 #endif
8339
8340     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8341       Life(x, y);
8342     else if (element == EL_EXIT_CLOSED)
8343       CheckExit(x, y);
8344     else if (element == EL_SP_EXIT_CLOSED)
8345       CheckExitSP(x, y);
8346     else if (element == EL_EXPANDABLE_WALL_GROWING)
8347       MauerWaechst(x, y);
8348     else if (element == EL_EXPANDABLE_WALL ||
8349              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8350              element == EL_EXPANDABLE_WALL_VERTICAL ||
8351              element == EL_EXPANDABLE_WALL_ANY)
8352       MauerAbleger(x, y);
8353     else if (element == EL_FLAMES)
8354       CheckForDragon(x, y);
8355 #if 0
8356     else if (IS_AUTO_CHANGING(element))
8357       ChangeElement(x, y);
8358 #endif
8359     else if (element == EL_EXPLOSION)
8360       ; /* drawing of correct explosion animation is handled separately */
8361     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8362       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8363
8364 #if 0
8365     /* this may take place after moving, so 'element' may have changed */
8366     if (IS_AUTO_CHANGING(Feld[x][y]))
8367       ChangeElement(x, y);
8368 #endif
8369
8370     if (IS_BELT_ACTIVE(element))
8371       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8372
8373     if (game.magic_wall_active)
8374     {
8375       int jx = local_player->jx, jy = local_player->jy;
8376
8377       /* play the element sound at the position nearest to the player */
8378       if ((element == EL_MAGIC_WALL_FULL ||
8379            element == EL_MAGIC_WALL_ACTIVE ||
8380            element == EL_MAGIC_WALL_EMPTYING ||
8381            element == EL_BD_MAGIC_WALL_FULL ||
8382            element == EL_BD_MAGIC_WALL_ACTIVE ||
8383            element == EL_BD_MAGIC_WALL_EMPTYING) &&
8384           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8385       {
8386         magic_wall_x = x;
8387         magic_wall_y = y;
8388       }
8389     }
8390   }
8391
8392 #if USE_NEW_AMOEBA_CODE
8393   /* new experimental amoeba growth stuff */
8394 #if 1
8395   if (!(FrameCounter % 8))
8396 #endif
8397   {
8398     static unsigned long random = 1684108901;
8399
8400     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8401     {
8402 #if 0
8403       x = (random >> 10) % lev_fieldx;
8404       y = (random >> 20) % lev_fieldy;
8405 #else
8406       x = RND(lev_fieldx);
8407       y = RND(lev_fieldy);
8408 #endif
8409       element = Feld[x][y];
8410
8411 #if 1
8412       if (!IS_PLAYER(x,y) &&
8413           (element == EL_EMPTY ||
8414            CAN_GROW_INTO(element) ||
8415            element == EL_QUICKSAND_EMPTY ||
8416            element == EL_ACID_SPLASH_LEFT ||
8417            element == EL_ACID_SPLASH_RIGHT))
8418       {
8419         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8420             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8421             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8422             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8423           Feld[x][y] = EL_AMOEBA_DROP;
8424       }
8425 #else
8426       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8427       if (!IS_PLAYER(x,y) &&
8428           (element == EL_EMPTY ||
8429            element == EL_SAND ||
8430            element == EL_QUICKSAND_EMPTY ||
8431            element == EL_ACID_SPLASH_LEFT ||
8432            element == EL_ACID_SPLASH_RIGHT))
8433       {
8434         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8435             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8436             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8437             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8438           Feld[x][y] = EL_AMOEBA_DROP;
8439       }
8440 #endif
8441
8442       random = random * 129 + 1;
8443     }
8444   }
8445 #endif
8446
8447 #if 0
8448   if (game.explosions_delayed)
8449 #endif
8450   {
8451     game.explosions_delayed = FALSE;
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 (ExplodeField[x][y])
8458         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8459       else if (element == EL_EXPLOSION)
8460         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8461
8462       ExplodeField[x][y] = EX_TYPE_NONE;
8463     }
8464
8465     game.explosions_delayed = TRUE;
8466   }
8467
8468   if (game.magic_wall_active)
8469   {
8470     if (!(game.magic_wall_time_left % 4))
8471     {
8472       int element = Feld[magic_wall_x][magic_wall_y];
8473
8474       if (element == EL_BD_MAGIC_WALL_FULL ||
8475           element == EL_BD_MAGIC_WALL_ACTIVE ||
8476           element == EL_BD_MAGIC_WALL_EMPTYING)
8477         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8478       else
8479         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8480     }
8481
8482     if (game.magic_wall_time_left > 0)
8483     {
8484       game.magic_wall_time_left--;
8485       if (!game.magic_wall_time_left)
8486       {
8487         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8488         {
8489           element = Feld[x][y];
8490
8491           if (element == EL_MAGIC_WALL_ACTIVE ||
8492               element == EL_MAGIC_WALL_FULL)
8493           {
8494             Feld[x][y] = EL_MAGIC_WALL_DEAD;
8495             DrawLevelField(x, y);
8496           }
8497           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8498                    element == EL_BD_MAGIC_WALL_FULL)
8499           {
8500             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8501             DrawLevelField(x, y);
8502           }
8503         }
8504
8505         game.magic_wall_active = FALSE;
8506       }
8507     }
8508   }
8509
8510   if (game.light_time_left > 0)
8511   {
8512     game.light_time_left--;
8513
8514     if (game.light_time_left == 0)
8515       RedrawAllLightSwitchesAndInvisibleElements();
8516   }
8517
8518   if (game.timegate_time_left > 0)
8519   {
8520     game.timegate_time_left--;
8521
8522     if (game.timegate_time_left == 0)
8523       CloseAllOpenTimegates();
8524   }
8525
8526   for (i = 0; i < MAX_PLAYERS; i++)
8527   {
8528     struct PlayerInfo *player = &stored_player[i];
8529
8530     if (SHIELD_ON(player))
8531     {
8532       if (player->shield_deadly_time_left)
8533         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8534       else if (player->shield_normal_time_left)
8535         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8536     }
8537   }
8538
8539   if (TimeFrames >= FRAMES_PER_SECOND)
8540   {
8541     TimeFrames = 0;
8542     TapeTime++;
8543
8544     if (!level.use_step_counter)
8545     {
8546       TimePlayed++;
8547
8548       for (i = 0; i < MAX_PLAYERS; i++)
8549       {
8550         struct PlayerInfo *player = &stored_player[i];
8551
8552         if (SHIELD_ON(player))
8553         {
8554           player->shield_normal_time_left--;
8555
8556           if (player->shield_deadly_time_left > 0)
8557             player->shield_deadly_time_left--;
8558         }
8559       }
8560
8561       if (TimeLeft > 0)
8562       {
8563         TimeLeft--;
8564
8565         if (TimeLeft <= 10 && setup.time_limit)
8566           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8567
8568         DrawGameValue_Time(TimeLeft);
8569
8570         if (!TimeLeft && setup.time_limit)
8571           for (i = 0; i < MAX_PLAYERS; i++)
8572             KillHero(&stored_player[i]);
8573       }
8574       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8575         DrawGameValue_Time(TimePlayed);
8576     }
8577
8578     if (tape.recording || tape.playing)
8579       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8580   }
8581
8582   DrawAllPlayers();
8583   PlayAllPlayersSound();
8584
8585   if (options.debug)                    /* calculate frames per second */
8586   {
8587     static unsigned long fps_counter = 0;
8588     static int fps_frames = 0;
8589     unsigned long fps_delay_ms = Counter() - fps_counter;
8590
8591     fps_frames++;
8592
8593     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
8594     {
8595       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8596
8597       fps_frames = 0;
8598       fps_counter = Counter();
8599     }
8600
8601     redraw_mask |= REDRAW_FPS;
8602   }
8603
8604 #if 0
8605   if (stored_player[0].jx != stored_player[0].last_jx ||
8606       stored_player[0].jy != stored_player[0].last_jy)
8607     printf("::: %d, %d, %d, %d, %d\n",
8608            stored_player[0].MovDir,
8609            stored_player[0].MovPos,
8610            stored_player[0].GfxPos,
8611            stored_player[0].Frame,
8612            stored_player[0].StepFrame);
8613 #endif
8614
8615 #if 1
8616   FrameCounter++;
8617   TimeFrames++;
8618
8619   for (i = 0; i < MAX_PLAYERS; i++)
8620   {
8621     int move_frames =
8622       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
8623
8624     stored_player[i].Frame += move_frames;
8625
8626     if (stored_player[i].MovPos != 0)
8627       stored_player[i].StepFrame += move_frames;
8628
8629     if (stored_player[i].drop_delay > 0)
8630       stored_player[i].drop_delay--;
8631   }
8632 #endif
8633
8634 #if 1
8635   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8636   {
8637     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8638
8639     local_player->show_envelope = 0;
8640   }
8641 #endif
8642 }
8643
8644 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8645 {
8646   int min_x = x, min_y = y, max_x = x, max_y = y;
8647   int i;
8648
8649   for (i = 0; i < MAX_PLAYERS; i++)
8650   {
8651     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8652
8653     if (!stored_player[i].active || &stored_player[i] == player)
8654       continue;
8655
8656     min_x = MIN(min_x, jx);
8657     min_y = MIN(min_y, jy);
8658     max_x = MAX(max_x, jx);
8659     max_y = MAX(max_y, jy);
8660   }
8661
8662   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8663 }
8664
8665 static boolean AllPlayersInVisibleScreen()
8666 {
8667   int i;
8668
8669   for (i = 0; i < MAX_PLAYERS; i++)
8670   {
8671     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8672
8673     if (!stored_player[i].active)
8674       continue;
8675
8676     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8677       return FALSE;
8678   }
8679
8680   return TRUE;
8681 }
8682
8683 void ScrollLevel(int dx, int dy)
8684 {
8685   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8686   int x, y;
8687
8688   BlitBitmap(drawto_field, drawto_field,
8689              FX + TILEX * (dx == -1) - softscroll_offset,
8690              FY + TILEY * (dy == -1) - softscroll_offset,
8691              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8692              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8693              FX + TILEX * (dx == 1) - softscroll_offset,
8694              FY + TILEY * (dy == 1) - softscroll_offset);
8695
8696   if (dx)
8697   {
8698     x = (dx == 1 ? BX1 : BX2);
8699     for (y = BY1; y <= BY2; y++)
8700       DrawScreenField(x, y);
8701   }
8702
8703   if (dy)
8704   {
8705     y = (dy == 1 ? BY1 : BY2);
8706     for (x = BX1; x <= BX2; x++)
8707       DrawScreenField(x, y);
8708   }
8709
8710   redraw_mask |= REDRAW_FIELD;
8711 }
8712
8713 #if 0
8714 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8715 {
8716   int nextx = x + dx, nexty = y + dy;
8717   int element = Feld[x][y];
8718
8719   if ((dx == -1 &&
8720        element != EL_SP_PORT_LEFT &&
8721        element != EL_SP_GRAVITY_PORT_LEFT &&
8722        element != EL_SP_PORT_HORIZONTAL &&
8723        element != EL_SP_PORT_ANY) ||
8724       (dx == +1 &&
8725        element != EL_SP_PORT_RIGHT &&
8726        element != EL_SP_GRAVITY_PORT_RIGHT &&
8727        element != EL_SP_PORT_HORIZONTAL &&
8728        element != EL_SP_PORT_ANY) ||
8729       (dy == -1 &&
8730        element != EL_SP_PORT_UP &&
8731        element != EL_SP_GRAVITY_PORT_UP &&
8732        element != EL_SP_PORT_VERTICAL &&
8733        element != EL_SP_PORT_ANY) ||
8734       (dy == +1 &&
8735        element != EL_SP_PORT_DOWN &&
8736        element != EL_SP_GRAVITY_PORT_DOWN &&
8737        element != EL_SP_PORT_VERTICAL &&
8738        element != EL_SP_PORT_ANY) ||
8739       !IN_LEV_FIELD(nextx, nexty) ||
8740       !IS_FREE(nextx, nexty))
8741     return FALSE;
8742
8743   return TRUE;
8744 }
8745 #endif
8746
8747 static boolean canFallDown(struct PlayerInfo *player)
8748 {
8749   int jx = player->jx, jy = player->jy;
8750
8751   return (IN_LEV_FIELD(jx, jy + 1) &&
8752           (IS_FREE(jx, jy + 1) ||
8753            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8754           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8755           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8756 }
8757
8758 static boolean canPassField(int x, int y, int move_dir)
8759 {
8760   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8761   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8762   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
8763   int nextx = x + dx;
8764   int nexty = y + dy;
8765   int element = Feld[x][y];
8766
8767   return (IS_PASSABLE_FROM(element, opposite_dir) &&
8768           !CAN_MOVE(element) &&
8769           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8770           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8771           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8772 }
8773
8774 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8775 {
8776   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8777   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8778   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
8779   int newx = x + dx;
8780   int newy = y + dy;
8781 #if 0
8782   int nextx = newx + dx;
8783   int nexty = newy + dy;
8784 #endif
8785
8786 #if 1
8787   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8788           (IS_DIGGABLE(Feld[newx][newy]) ||
8789            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8790            canPassField(newx, newy, move_dir)));
8791 #else
8792   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8793           (IS_DIGGABLE(Feld[newx][newy]) ||
8794            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8795            (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
8796             !CAN_MOVE(Feld[newx][newy]) &&
8797             IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8798             IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8799             (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
8800 #endif
8801 }
8802
8803 static void CheckGravityMovement(struct PlayerInfo *player)
8804 {
8805   if (game.gravity && !player->programmed_action)
8806   {
8807 #if 1
8808     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8809     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
8810 #else
8811     int move_dir_horizontal = player->action & MV_HORIZONTAL;
8812     int move_dir_vertical   = player->action & MV_VERTICAL;
8813 #endif
8814
8815 #if 1
8816     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8817 #else
8818     boolean player_is_snapping = player->action & JOY_BUTTON_1;
8819 #endif
8820
8821     int jx = player->jx, jy = player->jy;
8822
8823     boolean player_is_moving_to_valid_field =
8824       (!player_is_snapping &&
8825        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8826         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8827
8828 #if 0
8829     int move_dir =
8830       (player->last_move_dir & MV_HORIZONTAL ?
8831        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8832        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8833 #endif
8834
8835 #if 0
8836     int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8837     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8838     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8839     int new_jx = jx + dx, new_jy = jy + dy;
8840     int nextx = new_jx + dx, nexty = new_jy + dy;
8841 #endif
8842
8843 #if 1
8844
8845 #if 1
8846     boolean player_can_fall_down = canFallDown(player);
8847 #else
8848     boolean player_can_fall_down =
8849       (IN_LEV_FIELD(jx, jy + 1) &&
8850        (IS_FREE(jx, jy + 1) ||
8851         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8852 #endif
8853
8854 #else
8855     boolean player_can_fall_down =
8856       (IN_LEV_FIELD(jx, jy + 1) &&
8857        (IS_FREE(jx, jy + 1)));
8858 #endif
8859
8860 #if 0
8861     boolean player_is_moving_to_valid_field =
8862       (
8863 #if 1
8864        !player_is_snapping &&
8865 #endif
8866
8867 #if 1
8868        IN_LEV_FIELD(new_jx, new_jy) &&
8869        (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
8870         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8871          element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
8872          IN_LEV_FIELD(nextx, nexty) &&
8873          element_info[Feld[nextx][nexty]].access_direction & move_dir))
8874 #else
8875        IN_LEV_FIELD(new_jx, new_jy) &&
8876        (Feld[new_jx][new_jy] == EL_SP_BASE ||
8877         Feld[new_jx][new_jy] == EL_SAND ||
8878         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8879          canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
8880     /* !!! extend EL_SAND to anything diggable !!! */
8881 #endif
8882        );
8883 #endif
8884
8885 #if 0
8886     boolean player_is_standing_on_valid_field =
8887       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8888        (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
8889 #endif
8890
8891 #if 0
8892     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8893            player_can_fall_down,
8894            player_is_standing_on_valid_field,
8895            player_is_moving_to_valid_field,
8896            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8897            player->effective_action,
8898            player->can_fall_into_acid);
8899 #endif
8900
8901     if (player_can_fall_down &&
8902 #if 0
8903         !player_is_standing_on_valid_field &&
8904 #endif
8905         !player_is_moving_to_valid_field)
8906     {
8907 #if 0
8908       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8909              jx, jy, FrameCounter);
8910 #endif
8911
8912       player->programmed_action = MV_DOWN;
8913     }
8914   }
8915 }
8916
8917 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8918 {
8919 #if 1
8920   return CheckGravityMovement(player);
8921 #endif
8922
8923   if (game.gravity && !player->programmed_action)
8924   {
8925     int jx = player->jx, jy = player->jy;
8926     boolean field_under_player_is_free =
8927       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8928     boolean player_is_standing_on_valid_field =
8929       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8930        (IS_WALKABLE(Feld[jx][jy]) &&
8931         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8932
8933     if (field_under_player_is_free && !player_is_standing_on_valid_field)
8934       player->programmed_action = MV_DOWN;
8935   }
8936 }
8937
8938 /*
8939   MovePlayerOneStep()
8940   -----------------------------------------------------------------------------
8941   dx, dy:               direction (non-diagonal) to try to move the player to
8942   real_dx, real_dy:     direction as read from input device (can be diagonal)
8943 */
8944
8945 boolean MovePlayerOneStep(struct PlayerInfo *player,
8946                           int dx, int dy, int real_dx, int real_dy)
8947 {
8948 #if 0
8949   static int trigger_sides[4][2] =
8950   {
8951     /* enter side        leave side */
8952     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
8953     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
8954     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
8955     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
8956   };
8957   int move_direction = (dx == -1 ? MV_LEFT :
8958                         dx == +1 ? MV_RIGHT :
8959                         dy == -1 ? MV_UP :
8960                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8961   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8962   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8963 #endif
8964   int jx = player->jx, jy = player->jy;
8965   int new_jx = jx + dx, new_jy = jy + dy;
8966   int element;
8967   int can_move;
8968
8969   if (!player->active || (!dx && !dy))
8970     return MF_NO_ACTION;
8971
8972   player->MovDir = (dx < 0 ? MV_LEFT :
8973                     dx > 0 ? MV_RIGHT :
8974                     dy < 0 ? MV_UP :
8975                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
8976
8977   if (!IN_LEV_FIELD(new_jx, new_jy))
8978     return MF_NO_ACTION;
8979
8980   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8981     return MF_NO_ACTION;
8982
8983 #if 0
8984   element = MovingOrBlocked2Element(new_jx, new_jy);
8985 #else
8986   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8987 #endif
8988
8989   if (DONT_RUN_INTO(element))
8990   {
8991     if (element == EL_ACID && dx == 0 && dy == 1)
8992     {
8993       SplashAcid(new_jx, new_jy);
8994       Feld[jx][jy] = EL_PLAYER_1;
8995       InitMovingField(jx, jy, MV_DOWN);
8996       Store[jx][jy] = EL_ACID;
8997       ContinueMoving(jx, jy);
8998       BuryHero(player);
8999     }
9000     else
9001       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9002
9003     return MF_MOVING;
9004   }
9005
9006   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9007   if (can_move != MF_MOVING)
9008     return can_move;
9009
9010   /* check if DigField() has caused relocation of the player */
9011   if (player->jx != jx || player->jy != jy)
9012     return MF_NO_ACTION;
9013
9014   StorePlayer[jx][jy] = 0;
9015   player->last_jx = jx;
9016   player->last_jy = jy;
9017   player->jx = new_jx;
9018   player->jy = new_jy;
9019   StorePlayer[new_jx][new_jy] = player->element_nr;
9020
9021   player->MovPos =
9022     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9023
9024   player->step_counter++;
9025
9026 #if 0
9027   player->drop_delay = 0;
9028 #endif
9029
9030   PlayerVisit[jx][jy] = FrameCounter;
9031
9032   ScrollPlayer(player, SCROLL_INIT);
9033
9034 #if 0
9035   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9036   {
9037     CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9038                                       leave_side);
9039     CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9040   }
9041
9042   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9043   {
9044     CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9045                                       CE_OTHER_GETS_ENTERED, enter_side);
9046     CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9047                              CE_ENTERED_BY_PLAYER, enter_side);
9048   }
9049 #endif
9050
9051   return MF_MOVING;
9052 }
9053
9054 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9055 {
9056   int jx = player->jx, jy = player->jy;
9057   int old_jx = jx, old_jy = jy;
9058   int moved = MF_NO_ACTION;
9059
9060 #if 1
9061   if (!player->active)
9062     return FALSE;
9063
9064   if (!dx && !dy)
9065   {
9066     if (player->MovPos == 0)
9067     {
9068       player->is_moving = FALSE;
9069       player->is_digging = FALSE;
9070       player->is_collecting = FALSE;
9071       player->is_snapping = FALSE;
9072       player->is_pushing = FALSE;
9073     }
9074
9075     return FALSE;
9076   }
9077 #else
9078   if (!player->active || (!dx && !dy))
9079     return FALSE;
9080 #endif
9081
9082 #if 0
9083   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9084       !tape.playing)
9085     return FALSE;
9086 #else
9087
9088 #if 1
9089   if (!FrameReached(&player->move_delay, player->move_delay_value))
9090     return FALSE;
9091 #else
9092   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9093       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9094     return FALSE;
9095 #endif
9096
9097 #endif
9098
9099   /* store if player is automatically moved to next field */
9100   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9101
9102   /* remove the last programmed player action */
9103   player->programmed_action = 0;
9104
9105   if (player->MovPos)
9106   {
9107     /* should only happen if pre-1.2 tape recordings are played */
9108     /* this is only for backward compatibility */
9109
9110     int original_move_delay_value = player->move_delay_value;
9111
9112 #if DEBUG
9113     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9114            tape.counter);
9115 #endif
9116
9117     /* scroll remaining steps with finest movement resolution */
9118     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9119
9120     while (player->MovPos)
9121     {
9122       ScrollPlayer(player, SCROLL_GO_ON);
9123       ScrollScreen(NULL, SCROLL_GO_ON);
9124       FrameCounter++;
9125       DrawAllPlayers();
9126       BackToFront();
9127     }
9128
9129     player->move_delay_value = original_move_delay_value;
9130   }
9131
9132   if (player->last_move_dir & MV_HORIZONTAL)
9133   {
9134     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9135       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9136   }
9137   else
9138   {
9139     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9140       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9141   }
9142
9143   jx = player->jx;
9144   jy = player->jy;
9145
9146   if (moved & MF_MOVING && !ScreenMovPos &&
9147       (player == local_player || !options.network))
9148   {
9149     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9150     int offset = (setup.scroll_delay ? 3 : 0);
9151
9152     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9153     {
9154       /* actual player has left the screen -- scroll in that direction */
9155       if (jx != old_jx)         /* player has moved horizontally */
9156         scroll_x += (jx - old_jx);
9157       else                      /* player has moved vertically */
9158         scroll_y += (jy - old_jy);
9159     }
9160     else
9161     {
9162       if (jx != old_jx)         /* player has moved horizontally */
9163       {
9164         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
9165             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9166           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9167
9168         /* don't scroll over playfield boundaries */
9169         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9170           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9171
9172         /* don't scroll more than one field at a time */
9173         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9174
9175         /* don't scroll against the player's moving direction */
9176         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
9177             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9178           scroll_x = old_scroll_x;
9179       }
9180       else                      /* player has moved vertically */
9181       {
9182         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
9183             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9184           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9185
9186         /* don't scroll over playfield boundaries */
9187         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9188           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9189
9190         /* don't scroll more than one field at a time */
9191         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9192
9193         /* don't scroll against the player's moving direction */
9194         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
9195             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9196           scroll_y = old_scroll_y;
9197       }
9198     }
9199
9200     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9201     {
9202       if (!options.network && !AllPlayersInVisibleScreen())
9203       {
9204         scroll_x = old_scroll_x;
9205         scroll_y = old_scroll_y;
9206       }
9207       else
9208       {
9209         ScrollScreen(player, SCROLL_INIT);
9210         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9211       }
9212     }
9213   }
9214
9215 #if 0
9216 #if 1
9217   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9218 #else
9219   if (!(moved & MF_MOVING) && !player->is_pushing)
9220     player->Frame = 0;
9221 #endif
9222 #endif
9223
9224   player->StepFrame = 0;
9225
9226   if (moved & MF_MOVING)
9227   {
9228     if (old_jx != jx && old_jy == jy)
9229       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9230     else if (old_jx == jx && old_jy != jy)
9231       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9232
9233     DrawLevelField(jx, jy);     /* for "crumbled sand" */
9234
9235     player->last_move_dir = player->MovDir;
9236     player->is_moving = TRUE;
9237 #if 1
9238     player->is_snapping = FALSE;
9239 #endif
9240
9241 #if 1
9242     player->is_switching = FALSE;
9243 #endif
9244
9245     player->is_dropping = FALSE;
9246
9247
9248 #if 0
9249     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9250
9251 #if 1
9252     if (game.engine_version < VERSION_IDENT(3,1,0,0))
9253 #endif
9254     {
9255       static int trigger_sides[4][2] =
9256       {
9257         /* enter side           leave side */
9258         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9259         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9260         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9261         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9262       };
9263       int move_direction = player->MovDir;
9264       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9265       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9266
9267 #if 1
9268       /* !!! TEST ONLY !!! */
9269       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9270         CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9271                                    CE_LEFT_BY_PLAYER,
9272                                    player->index_bit, leave_side);
9273
9274       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9275                                           CE_OTHER_GETS_LEFT,
9276                                           player->index_bit, leave_side);
9277
9278       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9279         CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9280                                    player->index_bit, enter_side);
9281
9282       CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9283                                           CE_OTHER_GETS_ENTERED,
9284                                           player->index_bit, enter_side);
9285 #endif
9286
9287     }
9288 #endif
9289
9290
9291   }
9292   else
9293   {
9294     CheckGravityMovementWhenNotMoving(player);
9295
9296     /*
9297     player->last_move_dir = MV_NO_MOVING;
9298     */
9299     player->is_moving = FALSE;
9300   }
9301
9302   if (game.engine_version < VERSION_IDENT(3,0,7,0))
9303   {
9304     TestIfHeroTouchesBadThing(jx, jy);
9305     TestIfPlayerTouchesCustomElement(jx, jy);
9306   }
9307
9308   if (!player->active)
9309     RemoveHero(player);
9310
9311   return moved;
9312 }
9313
9314 void ScrollPlayer(struct PlayerInfo *player, int mode)
9315 {
9316   int jx = player->jx, jy = player->jy;
9317   int last_jx = player->last_jx, last_jy = player->last_jy;
9318   int move_stepsize = TILEX / player->move_delay_value;
9319
9320   if (!player->active || !player->MovPos)
9321     return;
9322
9323   if (mode == SCROLL_INIT)
9324   {
9325     player->actual_frame_counter = FrameCounter;
9326     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9327
9328     if (Feld[last_jx][last_jy] == EL_EMPTY)
9329       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9330
9331 #if 0
9332     DrawPlayer(player);
9333 #endif
9334
9335     return;
9336   }
9337   else if (!FrameReached(&player->actual_frame_counter, 1))
9338     return;
9339
9340   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9341   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9342
9343   if (!player->block_last_field &&
9344       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9345     Feld[last_jx][last_jy] = EL_EMPTY;
9346
9347   /* before DrawPlayer() to draw correct player graphic for this case */
9348   if (player->MovPos == 0)
9349     CheckGravityMovement(player);
9350
9351 #if 0
9352   DrawPlayer(player);   /* needed here only to cleanup last field */
9353 #endif
9354
9355   if (player->MovPos == 0)      /* player reached destination field */
9356   {
9357 #if 1
9358     if (player->move_delay_reset_counter > 0)
9359     {
9360       player->move_delay_reset_counter--;
9361
9362       if (player->move_delay_reset_counter == 0)
9363       {
9364         /* continue with normal speed after quickly moving through gate */
9365         HALVE_PLAYER_SPEED(player);
9366
9367         /* be able to make the next move without delay */
9368         player->move_delay = 0;
9369       }
9370     }
9371 #else
9372     if (IS_PASSABLE(Feld[last_jx][last_jy]))
9373     {
9374       /* continue with normal speed after quickly moving through gate */
9375       HALVE_PLAYER_SPEED(player);
9376
9377       /* be able to make the next move without delay */
9378       player->move_delay = 0;
9379     }
9380 #endif
9381
9382     if (player->block_last_field &&
9383         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9384       Feld[last_jx][last_jy] = EL_EMPTY;
9385
9386     player->last_jx = jx;
9387     player->last_jy = jy;
9388
9389     if (Feld[jx][jy] == EL_EXIT_OPEN ||
9390         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9391         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
9392     {
9393       DrawPlayer(player);       /* needed here only to cleanup last field */
9394       RemoveHero(player);
9395
9396       if (local_player->friends_still_needed == 0 ||
9397           IS_SP_ELEMENT(Feld[jx][jy]))
9398         player->LevelSolved = player->GameOver = TRUE;
9399     }
9400
9401 #if 1
9402     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9403     /* this breaks one level: "machine", level 000 */
9404 #if 0
9405     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9406 #endif
9407     {
9408       static int trigger_sides[4][2] =
9409       {
9410         /* enter side           leave side */
9411         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9412         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9413         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9414         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9415       };
9416       int move_direction = player->MovDir;
9417       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9418       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9419       int old_jx = last_jx;
9420       int old_jy = last_jy;
9421
9422 #if 1
9423       /* !!! TEST ONLY !!! */
9424       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9425         CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9426                                    CE_LEFT_BY_PLAYER,
9427                                    player->index_bit, leave_side);
9428
9429       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9430                                           CE_OTHER_GETS_LEFT,
9431                                           player->index_bit, leave_side);
9432
9433       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9434         CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9435                                    player->index_bit, enter_side);
9436
9437       CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
9438                                           CE_OTHER_GETS_ENTERED,
9439                                           player->index_bit, enter_side);
9440 #endif
9441
9442     }
9443 #endif
9444
9445     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9446     {
9447       TestIfHeroTouchesBadThing(jx, jy);
9448       TestIfPlayerTouchesCustomElement(jx, jy);
9449 #if 1
9450       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
9451 #endif
9452
9453       if (!player->active)
9454         RemoveHero(player);
9455     }
9456
9457     if (level.use_step_counter)
9458     {
9459       int i;
9460
9461       TimePlayed++;
9462
9463       for (i = 0; i < MAX_PLAYERS; i++)
9464       {
9465         struct PlayerInfo *player = &stored_player[i];
9466
9467         if (SHIELD_ON(player))
9468         {
9469           player->shield_normal_time_left--;
9470
9471           if (player->shield_deadly_time_left > 0)
9472             player->shield_deadly_time_left--;
9473         }
9474       }
9475
9476       if (TimeLeft > 0)
9477       {
9478         TimeLeft--;
9479
9480         if (TimeLeft <= 10 && setup.time_limit)
9481           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9482
9483         DrawGameValue_Time(TimeLeft);
9484
9485         if (!TimeLeft && setup.time_limit)
9486           for (i = 0; i < MAX_PLAYERS; i++)
9487             KillHero(&stored_player[i]);
9488       }
9489       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9490         DrawGameValue_Time(TimePlayed);
9491     }
9492
9493     if (tape.single_step && tape.recording && !tape.pausing &&
9494         !player->programmed_action)
9495       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9496   }
9497 }
9498
9499 void ScrollScreen(struct PlayerInfo *player, int mode)
9500 {
9501   static unsigned long screen_frame_counter = 0;
9502
9503   if (mode == SCROLL_INIT)
9504   {
9505     /* set scrolling step size according to actual player's moving speed */
9506     ScrollStepSize = TILEX / player->move_delay_value;
9507
9508     screen_frame_counter = FrameCounter;
9509     ScreenMovDir = player->MovDir;
9510     ScreenMovPos = player->MovPos;
9511     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9512     return;
9513   }
9514   else if (!FrameReached(&screen_frame_counter, 1))
9515     return;
9516
9517   if (ScreenMovPos)
9518   {
9519     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9520     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9521     redraw_mask |= REDRAW_FIELD;
9522   }
9523   else
9524     ScreenMovDir = MV_NO_MOVING;
9525 }
9526
9527 void TestIfPlayerTouchesCustomElement(int x, int y)
9528 {
9529   static int xy[4][2] =
9530   {
9531     { 0, -1 },
9532     { -1, 0 },
9533     { +1, 0 },
9534     { 0, +1 }
9535   };
9536   static int trigger_sides[4][2] =
9537   {
9538     /* center side       border side */
9539     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9540     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9541     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9542     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9543   };
9544   static int touch_dir[4] =
9545   {
9546     MV_LEFT | MV_RIGHT,
9547     MV_UP   | MV_DOWN,
9548     MV_UP   | MV_DOWN,
9549     MV_LEFT | MV_RIGHT
9550   };
9551   int center_element = Feld[x][y];      /* should always be non-moving! */
9552   int i;
9553
9554   for (i = 0; i < NUM_DIRECTIONS; i++)
9555   {
9556     int xx = x + xy[i][0];
9557     int yy = y + xy[i][1];
9558     int center_side = trigger_sides[i][0];
9559     int border_side = trigger_sides[i][1];
9560     int border_element;
9561
9562     if (!IN_LEV_FIELD(xx, yy))
9563       continue;
9564
9565     if (IS_PLAYER(x, y))
9566     {
9567       struct PlayerInfo *player = PLAYERINFO(x, y);
9568
9569       if (game.engine_version < VERSION_IDENT(3,0,7,0))
9570         border_element = Feld[xx][yy];          /* may be moving! */
9571       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9572         border_element = Feld[xx][yy];
9573       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
9574         border_element = MovingOrBlocked2Element(xx, yy);
9575       else
9576         continue;               /* center and border element do not touch */
9577
9578 #if 1
9579       /* !!! TEST ONLY !!! */
9580       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9581                                  player->index_bit, border_side);
9582       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9583                                           CE_OTHER_GETS_TOUCHED,
9584                                           player->index_bit, border_side);
9585 #else
9586       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9587                                           CE_OTHER_GETS_TOUCHED,
9588                                           player->index_bit, border_side);
9589       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9590                                  player->index_bit, border_side);
9591 #endif
9592     }
9593     else if (IS_PLAYER(xx, yy))
9594     {
9595       struct PlayerInfo *player = PLAYERINFO(xx, yy);
9596
9597       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9598       {
9599         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9600           continue;             /* center and border element do not touch */
9601       }
9602
9603 #if 1
9604       /* !!! TEST ONLY !!! */
9605       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9606                                  player->index_bit, center_side);
9607       CheckTriggeredElementChangeByPlayer(x, y, center_element,
9608                                           CE_OTHER_GETS_TOUCHED,
9609                                           player->index_bit, center_side);
9610 #else
9611       CheckTriggeredElementChangeByPlayer(x, y, center_element,
9612                                           CE_OTHER_GETS_TOUCHED,
9613                                           player->index_bit, center_side);
9614       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9615                                  player->index_bit, center_side);
9616 #endif
9617
9618       break;
9619     }
9620   }
9621 }
9622
9623 void TestIfElementTouchesCustomElement(int x, int y)
9624 {
9625   static int xy[4][2] =
9626   {
9627     { 0, -1 },
9628     { -1, 0 },
9629     { +1, 0 },
9630     { 0, +1 }
9631   };
9632   static int trigger_sides[4][2] =
9633   {
9634     /* center side      border side */
9635     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9636     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9637     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9638     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9639   };
9640   static int touch_dir[4] =
9641   {
9642     MV_LEFT | MV_RIGHT,
9643     MV_UP   | MV_DOWN,
9644     MV_UP   | MV_DOWN,
9645     MV_LEFT | MV_RIGHT
9646   };
9647   boolean change_center_element = FALSE;
9648   int center_element_change_page = 0;
9649   int center_element = Feld[x][y];      /* should always be non-moving! */
9650   int border_trigger_element;
9651   int i, j;
9652
9653   for (i = 0; i < NUM_DIRECTIONS; i++)
9654   {
9655     int xx = x + xy[i][0];
9656     int yy = y + xy[i][1];
9657     int center_side = trigger_sides[i][0];
9658     int border_side = trigger_sides[i][1];
9659     int border_element;
9660
9661     if (!IN_LEV_FIELD(xx, yy))
9662       continue;
9663
9664     if (game.engine_version < VERSION_IDENT(3,0,7,0))
9665       border_element = Feld[xx][yy];    /* may be moving! */
9666     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9667       border_element = Feld[xx][yy];
9668     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
9669       border_element = MovingOrBlocked2Element(xx, yy);
9670     else
9671       continue;                 /* center and border element do not touch */
9672
9673     /* check for change of center element (but change it only once) */
9674     if (IS_CUSTOM_ELEMENT(center_element) &&
9675         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9676         !change_center_element)
9677     {
9678       for (j = 0; j < element_info[center_element].num_change_pages; j++)
9679       {
9680         struct ElementChangeInfo *change =
9681           &element_info[center_element].change_page[j];
9682
9683         if (change->can_change &&
9684             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9685             change->trigger_side & border_side &&
9686 #if 1
9687             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9688 #else
9689             change->trigger_element == border_element
9690 #endif
9691             )
9692         {
9693           change_center_element = TRUE;
9694           center_element_change_page = j;
9695           border_trigger_element = border_element;
9696
9697           break;
9698         }
9699       }
9700     }
9701
9702     /* check for change of border element */
9703     if (IS_CUSTOM_ELEMENT(border_element) &&
9704         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9705     {
9706       for (j = 0; j < element_info[border_element].num_change_pages; j++)
9707       {
9708         struct ElementChangeInfo *change =
9709           &element_info[border_element].change_page[j];
9710
9711         if (change->can_change &&
9712             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9713             change->trigger_side & center_side &&
9714 #if 1
9715             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9716 #else
9717             change->trigger_element == center_element
9718 #endif
9719             )
9720         {
9721 #if 0
9722           printf("::: border_element %d, %d\n", x, y);
9723 #endif
9724
9725           CheckElementChangeByPage(xx, yy, border_element, center_element,
9726                                    CE_OTHER_IS_TOUCHING, j);
9727           break;
9728         }
9729       }
9730     }
9731   }
9732
9733   if (change_center_element)
9734   {
9735 #if 0
9736     printf("::: center_element %d, %d\n", x, y);
9737 #endif
9738
9739     CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9740                              CE_OTHER_IS_TOUCHING, center_element_change_page);
9741   }
9742 }
9743
9744 void TestIfElementHitsCustomElement(int x, int y, int direction)
9745 {
9746   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9747   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9748   int hitx = x + dx, hity = y + dy;
9749   int hitting_element = Feld[x][y];
9750   int touched_element;
9751 #if 0
9752   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9753                         !IS_FREE(hitx, hity) &&
9754                         (!IS_MOVING(hitx, hity) ||
9755                          MovDir[hitx][hity] != direction ||
9756                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9757 #endif
9758
9759   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9760     return;
9761
9762 #if 0
9763   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9764     return;
9765 #endif
9766
9767   touched_element = (IN_LEV_FIELD(hitx, hity) ?
9768                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9769
9770   CheckElementChangeBySide(x, y, hitting_element, touched_element,
9771                            CE_HITTING_SOMETHING, direction);
9772
9773   if (IN_LEV_FIELD(hitx, hity))
9774   {
9775     int opposite_direction = MV_DIR_OPPOSITE(direction);
9776     int hitting_side = direction;
9777     int touched_side = opposite_direction;
9778 #if 0
9779     int touched_element = MovingOrBlocked2Element(hitx, hity);
9780 #endif
9781 #if 1
9782     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9783                           MovDir[hitx][hity] != direction ||
9784                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9785
9786     object_hit = TRUE;
9787 #endif
9788
9789     if (object_hit)
9790     {
9791       int i;
9792
9793       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9794                                CE_HIT_BY_SOMETHING, opposite_direction);
9795
9796       if (IS_CUSTOM_ELEMENT(hitting_element) &&
9797           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9798       {
9799         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9800         {
9801           struct ElementChangeInfo *change =
9802             &element_info[hitting_element].change_page[i];
9803
9804           if (change->can_change &&
9805               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9806               change->trigger_side & touched_side &&
9807           
9808 #if 1
9809               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9810 #else
9811               change->trigger_element == touched_element
9812 #endif
9813               )
9814           {
9815             CheckElementChangeByPage(x, y, hitting_element, touched_element,
9816                                      CE_OTHER_IS_HITTING, i);
9817             break;
9818           }
9819         }
9820       }
9821
9822       if (IS_CUSTOM_ELEMENT(touched_element) &&
9823           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9824       {
9825         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9826         {
9827           struct ElementChangeInfo *change =
9828             &element_info[touched_element].change_page[i];
9829
9830           if (change->can_change &&
9831               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9832               change->trigger_side & hitting_side &&
9833 #if 1
9834               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9835 #else
9836               change->trigger_element == hitting_element
9837 #endif
9838               )
9839           {
9840             CheckElementChangeByPage(hitx, hity, touched_element,
9841                                      hitting_element, CE_OTHER_GETS_HIT, i);
9842             break;
9843           }
9844         }
9845       }
9846     }
9847   }
9848 }
9849
9850 #if 0
9851 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9852 {
9853   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9854   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9855   int hitx = x + dx, hity = y + dy;
9856   int hitting_element = Feld[x][y];
9857   int touched_element;
9858 #if 0
9859   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9860                         !IS_FREE(hitx, hity) &&
9861                         (!IS_MOVING(hitx, hity) ||
9862                          MovDir[hitx][hity] != direction ||
9863                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9864 #endif
9865
9866   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9867     return;
9868
9869 #if 0
9870   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9871     return;
9872 #endif
9873
9874   touched_element = (IN_LEV_FIELD(hitx, hity) ?
9875                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9876
9877   CheckElementChangeBySide(x, y, hitting_element, touched_element,
9878                            EP_CAN_SMASH_EVERYTHING, direction);
9879
9880   if (IN_LEV_FIELD(hitx, hity))
9881   {
9882     int opposite_direction = MV_DIR_OPPOSITE(direction);
9883     int hitting_side = direction;
9884     int touched_side = opposite_direction;
9885 #if 0
9886     int touched_element = MovingOrBlocked2Element(hitx, hity);
9887 #endif
9888 #if 1
9889     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9890                           MovDir[hitx][hity] != direction ||
9891                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9892
9893     object_hit = TRUE;
9894 #endif
9895
9896     if (object_hit)
9897     {
9898       int i;
9899
9900       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9901                                CE_SMASHED_BY_SOMETHING, opposite_direction);
9902
9903       if (IS_CUSTOM_ELEMENT(hitting_element) &&
9904           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9905       {
9906         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9907         {
9908           struct ElementChangeInfo *change =
9909             &element_info[hitting_element].change_page[i];
9910
9911           if (change->can_change &&
9912               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9913               change->trigger_side & touched_side &&
9914           
9915 #if 1
9916               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9917 #else
9918               change->trigger_element == touched_element
9919 #endif
9920               )
9921           {
9922             CheckElementChangeByPage(x, y, hitting_element, touched_element,
9923                                      CE_OTHER_IS_SMASHING, i);
9924             break;
9925           }
9926         }
9927       }
9928
9929       if (IS_CUSTOM_ELEMENT(touched_element) &&
9930           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9931       {
9932         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9933         {
9934           struct ElementChangeInfo *change =
9935             &element_info[touched_element].change_page[i];
9936
9937           if (change->can_change &&
9938               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9939               change->trigger_side & hitting_side &&
9940 #if 1
9941               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9942 #else
9943               change->trigger_element == hitting_element
9944 #endif
9945               )
9946           {
9947             CheckElementChangeByPage(hitx, hity, touched_element,
9948                                      hitting_element, CE_OTHER_GETS_SMASHED,i);
9949             break;
9950           }
9951         }
9952       }
9953     }
9954   }
9955 }
9956 #endif
9957
9958 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9959 {
9960   int i, kill_x = -1, kill_y = -1;
9961   int bad_element = -1;
9962   static int test_xy[4][2] =
9963   {
9964     { 0, -1 },
9965     { -1, 0 },
9966     { +1, 0 },
9967     { 0, +1 }
9968   };
9969   static int test_dir[4] =
9970   {
9971     MV_UP,
9972     MV_LEFT,
9973     MV_RIGHT,
9974     MV_DOWN
9975   };
9976
9977   for (i = 0; i < NUM_DIRECTIONS; i++)
9978   {
9979     int test_x, test_y, test_move_dir, test_element;
9980
9981     test_x = good_x + test_xy[i][0];
9982     test_y = good_y + test_xy[i][1];
9983
9984     if (!IN_LEV_FIELD(test_x, test_y))
9985       continue;
9986
9987     test_move_dir =
9988       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9989
9990 #if 0
9991     test_element = Feld[test_x][test_y];
9992 #else
9993     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9994 #endif
9995
9996     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9997        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9998     */
9999     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10000         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
10001     {
10002       kill_x = test_x;
10003       kill_y = test_y;
10004       bad_element = test_element;
10005
10006       break;
10007     }
10008   }
10009
10010   if (kill_x != -1 || kill_y != -1)
10011   {
10012     if (IS_PLAYER(good_x, good_y))
10013     {
10014       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10015
10016 #if 1
10017       if (player->shield_deadly_time_left > 0 &&
10018           !IS_INDESTRUCTIBLE(bad_element))
10019         Bang(kill_x, kill_y);
10020       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10021         KillHero(player);
10022 #else
10023       if (player->shield_deadly_time_left > 0)
10024         Bang(kill_x, kill_y);
10025       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10026         KillHero(player);
10027 #endif
10028     }
10029     else
10030       Bang(good_x, good_y);
10031   }
10032 }
10033
10034 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10035 {
10036   int i, kill_x = -1, kill_y = -1;
10037   int bad_element = Feld[bad_x][bad_y];
10038   static int test_xy[4][2] =
10039   {
10040     { 0, -1 },
10041     { -1, 0 },
10042     { +1, 0 },
10043     { 0, +1 }
10044   };
10045   static int touch_dir[4] =
10046   {
10047     MV_LEFT | MV_RIGHT,
10048     MV_UP   | MV_DOWN,
10049     MV_UP   | MV_DOWN,
10050     MV_LEFT | MV_RIGHT
10051   };
10052   static int test_dir[4] =
10053   {
10054     MV_UP,
10055     MV_LEFT,
10056     MV_RIGHT,
10057     MV_DOWN
10058   };
10059
10060   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
10061     return;
10062
10063   for (i = 0; i < NUM_DIRECTIONS; i++)
10064   {
10065     int test_x, test_y, test_move_dir, test_element;
10066
10067     test_x = bad_x + test_xy[i][0];
10068     test_y = bad_y + test_xy[i][1];
10069     if (!IN_LEV_FIELD(test_x, test_y))
10070       continue;
10071
10072     test_move_dir =
10073       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10074
10075     test_element = Feld[test_x][test_y];
10076
10077     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10078        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10079     */
10080     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
10081         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
10082     {
10083       /* good thing is player or penguin that does not move away */
10084       if (IS_PLAYER(test_x, test_y))
10085       {
10086         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10087
10088         if (bad_element == EL_ROBOT && player->is_moving)
10089           continue;     /* robot does not kill player if he is moving */
10090
10091         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10092         {
10093           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10094             continue;           /* center and border element do not touch */
10095         }
10096
10097         kill_x = test_x;
10098         kill_y = test_y;
10099         break;
10100       }
10101       else if (test_element == EL_PENGUIN)
10102       {
10103         kill_x = test_x;
10104         kill_y = test_y;
10105         break;
10106       }
10107     }
10108   }
10109
10110   if (kill_x != -1 || kill_y != -1)
10111   {
10112     if (IS_PLAYER(kill_x, kill_y))
10113     {
10114       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10115
10116 #if 1
10117       if (player->shield_deadly_time_left > 0 &&
10118           !IS_INDESTRUCTIBLE(bad_element))
10119         Bang(bad_x, bad_y);
10120       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10121         KillHero(player);
10122 #else
10123       if (player->shield_deadly_time_left > 0)
10124         Bang(bad_x, bad_y);
10125       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10126         KillHero(player);
10127 #endif
10128     }
10129     else
10130       Bang(kill_x, kill_y);
10131   }
10132 }
10133
10134 void TestIfHeroTouchesBadThing(int x, int y)
10135 {
10136   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10137 }
10138
10139 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10140 {
10141   TestIfGoodThingHitsBadThing(x, y, move_dir);
10142 }
10143
10144 void TestIfBadThingTouchesHero(int x, int y)
10145 {
10146   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10147 }
10148
10149 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10150 {
10151   TestIfBadThingHitsGoodThing(x, y, move_dir);
10152 }
10153
10154 void TestIfFriendTouchesBadThing(int x, int y)
10155 {
10156   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10157 }
10158
10159 void TestIfBadThingTouchesFriend(int x, int y)
10160 {
10161   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10162 }
10163
10164 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10165 {
10166   int i, kill_x = bad_x, kill_y = bad_y;
10167   static int xy[4][2] =
10168   {
10169     { 0, -1 },
10170     { -1, 0 },
10171     { +1, 0 },
10172     { 0, +1 }
10173   };
10174
10175   for (i = 0; i < NUM_DIRECTIONS; i++)
10176   {
10177     int x, y, element;
10178
10179     x = bad_x + xy[i][0];
10180     y = bad_y + xy[i][1];
10181     if (!IN_LEV_FIELD(x, y))
10182       continue;
10183
10184     element = Feld[x][y];
10185     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10186         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10187     {
10188       kill_x = x;
10189       kill_y = y;
10190       break;
10191     }
10192   }
10193
10194   if (kill_x != bad_x || kill_y != bad_y)
10195     Bang(bad_x, bad_y);
10196 }
10197
10198 void KillHero(struct PlayerInfo *player)
10199 {
10200   int jx = player->jx, jy = player->jy;
10201
10202   if (!player->active)
10203     return;
10204
10205   /* remove accessible field at the player's position */
10206   Feld[jx][jy] = EL_EMPTY;
10207
10208   /* deactivate shield (else Bang()/Explode() would not work right) */
10209   player->shield_normal_time_left = 0;
10210   player->shield_deadly_time_left = 0;
10211
10212   Bang(jx, jy);
10213   BuryHero(player);
10214 }
10215
10216 static void KillHeroUnlessEnemyProtected(int x, int y)
10217 {
10218   if (!PLAYER_ENEMY_PROTECTED(x, y))
10219     KillHero(PLAYERINFO(x, y));
10220 }
10221
10222 static void KillHeroUnlessExplosionProtected(int x, int y)
10223 {
10224   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10225     KillHero(PLAYERINFO(x, y));
10226 }
10227
10228 void BuryHero(struct PlayerInfo *player)
10229 {
10230   int jx = player->jx, jy = player->jy;
10231
10232   if (!player->active)
10233     return;
10234
10235 #if 1
10236   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10237 #else
10238   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10239 #endif
10240   PlayLevelSound(jx, jy, SND_GAME_LOSING);
10241
10242   player->GameOver = TRUE;
10243   RemoveHero(player);
10244 }
10245
10246 void RemoveHero(struct PlayerInfo *player)
10247 {
10248   int jx = player->jx, jy = player->jy;
10249   int i, found = FALSE;
10250
10251   player->present = FALSE;
10252   player->active = FALSE;
10253
10254   if (!ExplodeField[jx][jy])
10255     StorePlayer[jx][jy] = 0;
10256
10257   for (i = 0; i < MAX_PLAYERS; i++)
10258     if (stored_player[i].active)
10259       found = TRUE;
10260
10261   if (!found)
10262     AllPlayersGone = TRUE;
10263
10264   ExitX = ZX = jx;
10265   ExitY = ZY = jy;
10266 }
10267
10268 /*
10269   =============================================================================
10270   checkDiagonalPushing()
10271   -----------------------------------------------------------------------------
10272   check if diagonal input device direction results in pushing of object
10273   (by checking if the alternative direction is walkable, diggable, ...)
10274   =============================================================================
10275 */
10276
10277 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10278                                     int x, int y, int real_dx, int real_dy)
10279 {
10280   int jx, jy, dx, dy, xx, yy;
10281
10282   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
10283     return TRUE;
10284
10285   /* diagonal direction: check alternative direction */
10286   jx = player->jx;
10287   jy = player->jy;
10288   dx = x - jx;
10289   dy = y - jy;
10290   xx = jx + (dx == 0 ? real_dx : 0);
10291   yy = jy + (dy == 0 ? real_dy : 0);
10292
10293   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10294 }
10295
10296 /*
10297   =============================================================================
10298   DigField()
10299   -----------------------------------------------------------------------------
10300   x, y:                 field next to player (non-diagonal) to try to dig to
10301   real_dx, real_dy:     direction as read from input device (can be diagonal)
10302   =============================================================================
10303 */
10304
10305 int DigField(struct PlayerInfo *player,
10306              int oldx, int oldy, int x, int y,
10307              int real_dx, int real_dy, int mode)
10308 {
10309   static int trigger_sides[4] =
10310   {
10311     CH_SIDE_RIGHT,      /* moving left  */
10312     CH_SIDE_LEFT,       /* moving right */
10313     CH_SIDE_BOTTOM,     /* moving up    */
10314     CH_SIDE_TOP,        /* moving down  */
10315   };
10316 #if 0
10317   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10318 #endif
10319   int jx = oldx, jy = oldy;
10320   int dx = x - jx, dy = y - jy;
10321   int nextx = x + dx, nexty = y + dy;
10322   int move_direction = (dx == -1 ? MV_LEFT :
10323                         dx == +1 ? MV_RIGHT :
10324                         dy == -1 ? MV_UP :
10325                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10326   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10327   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10328   int old_element = Feld[jx][jy];
10329   int element;
10330
10331   if (player->MovPos == 0)
10332   {
10333     player->is_digging = FALSE;
10334     player->is_collecting = FALSE;
10335   }
10336
10337   if (player->MovPos == 0)      /* last pushing move finished */
10338     player->is_pushing = FALSE;
10339
10340   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
10341   {
10342     player->is_switching = FALSE;
10343     player->push_delay = 0;
10344
10345     return MF_NO_ACTION;
10346   }
10347
10348   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10349     return MF_NO_ACTION;
10350
10351 #if 0
10352
10353 #if 0
10354   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10355 #else
10356   if (IS_TUBE(Feld[jx][jy]) ||
10357       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10358 #endif
10359   {
10360     int i = 0;
10361     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10362     int tube_leave_directions[][2] =
10363     {
10364       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10365       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
10366       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
10367       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
10368       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
10369       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
10370       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
10371       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
10372       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
10373       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
10374       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
10375       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10376     };
10377
10378     while (tube_leave_directions[i][0] != tube_element)
10379     {
10380       i++;
10381       if (tube_leave_directions[i][0] == -1)    /* should not happen */
10382         break;
10383     }
10384
10385     if (!(tube_leave_directions[i][1] & move_direction))
10386       return MF_NO_ACTION;      /* tube has no opening in this direction */
10387   }
10388
10389 #else
10390
10391   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10392     old_element = Back[jx][jy];
10393
10394 #endif
10395
10396   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10397     return MF_NO_ACTION;        /* field has no opening in this direction */
10398
10399   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10400     return MF_NO_ACTION;        /* field has no opening in this direction */
10401
10402   element = Feld[x][y];
10403
10404   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10405       game.engine_version >= VERSION_IDENT(2,2,0,0))
10406     return MF_NO_ACTION;
10407
10408   switch (element)
10409   {
10410 #if 0
10411     case EL_SP_PORT_LEFT:
10412     case EL_SP_PORT_RIGHT:
10413     case EL_SP_PORT_UP:
10414     case EL_SP_PORT_DOWN:
10415     case EL_SP_PORT_HORIZONTAL:
10416     case EL_SP_PORT_VERTICAL:
10417     case EL_SP_PORT_ANY:
10418     case EL_SP_GRAVITY_PORT_LEFT:
10419     case EL_SP_GRAVITY_PORT_RIGHT:
10420     case EL_SP_GRAVITY_PORT_UP:
10421     case EL_SP_GRAVITY_PORT_DOWN:
10422 #if 1
10423       if (!canEnterSupaplexPort(x, y, dx, dy))
10424         return MF_NO_ACTION;
10425 #else
10426       if ((dx == -1 &&
10427            element != EL_SP_PORT_LEFT &&
10428            element != EL_SP_GRAVITY_PORT_LEFT &&
10429            element != EL_SP_PORT_HORIZONTAL &&
10430            element != EL_SP_PORT_ANY) ||
10431           (dx == +1 &&
10432            element != EL_SP_PORT_RIGHT &&
10433            element != EL_SP_GRAVITY_PORT_RIGHT &&
10434            element != EL_SP_PORT_HORIZONTAL &&
10435            element != EL_SP_PORT_ANY) ||
10436           (dy == -1 &&
10437            element != EL_SP_PORT_UP &&
10438            element != EL_SP_GRAVITY_PORT_UP &&
10439            element != EL_SP_PORT_VERTICAL &&
10440            element != EL_SP_PORT_ANY) ||
10441           (dy == +1 &&
10442            element != EL_SP_PORT_DOWN &&
10443            element != EL_SP_GRAVITY_PORT_DOWN &&
10444            element != EL_SP_PORT_VERTICAL &&
10445            element != EL_SP_PORT_ANY) ||
10446           !IN_LEV_FIELD(nextx, nexty) ||
10447           !IS_FREE(nextx, nexty))
10448         return MF_NO_ACTION;
10449 #endif
10450
10451       if (element == EL_SP_GRAVITY_PORT_LEFT ||
10452           element == EL_SP_GRAVITY_PORT_RIGHT ||
10453           element == EL_SP_GRAVITY_PORT_UP ||
10454           element == EL_SP_GRAVITY_PORT_DOWN)
10455         game.gravity = !game.gravity;
10456
10457       /* automatically move to the next field with double speed */
10458       player->programmed_action = move_direction;
10459 #if 1
10460       if (player->move_delay_reset_counter == 0)
10461       {
10462         player->move_delay_reset_counter = 2;   /* two double speed steps */
10463
10464         DOUBLE_PLAYER_SPEED(player);
10465       }
10466 #else
10467       player->move_delay_reset_counter = 2;
10468
10469       DOUBLE_PLAYER_SPEED(player);
10470 #endif
10471
10472 #if 0
10473       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10474 #endif
10475
10476       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10477       break;
10478 #endif
10479
10480 #if 0
10481     case EL_TUBE_ANY:
10482     case EL_TUBE_VERTICAL:
10483     case EL_TUBE_HORIZONTAL:
10484     case EL_TUBE_VERTICAL_LEFT:
10485     case EL_TUBE_VERTICAL_RIGHT:
10486     case EL_TUBE_HORIZONTAL_UP:
10487     case EL_TUBE_HORIZONTAL_DOWN:
10488     case EL_TUBE_LEFT_UP:
10489     case EL_TUBE_LEFT_DOWN:
10490     case EL_TUBE_RIGHT_UP:
10491     case EL_TUBE_RIGHT_DOWN:
10492       {
10493         int i = 0;
10494         int tube_enter_directions[][2] =
10495         {
10496           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10497           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
10498           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
10499           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
10500           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
10501           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
10502           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
10503           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
10504           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
10505           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
10506           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
10507           { -1,                         MV_NO_MOVING                         }
10508         };
10509
10510         while (tube_enter_directions[i][0] != element)
10511         {
10512           i++;
10513           if (tube_enter_directions[i][0] == -1)        /* should not happen */
10514             break;
10515         }
10516
10517         if (!(tube_enter_directions[i][1] & move_direction))
10518           return MF_NO_ACTION;  /* tube has no opening in this direction */
10519
10520         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10521       }
10522       break;
10523 #endif
10524
10525     default:
10526
10527 #if 1
10528       if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10529 #else
10530       if (IS_WALKABLE(element))
10531 #endif
10532       {
10533         int sound_action = ACTION_WALKING;
10534
10535 #if 0
10536         if (!ACCESS_FROM(element, opposite_direction))
10537           return MF_NO_ACTION;  /* field not accessible from this direction */
10538 #endif
10539
10540 #if 1
10541         if (element == EL_EMPTY_SPACE &&
10542             game.gravity && !player->is_auto_moving &&
10543             canFallDown(player) && move_direction != MV_DOWN)
10544           return MF_NO_ACTION;  /* player cannot walk here due to gravity */
10545 #endif
10546
10547         if (IS_GATE(element))
10548         {
10549           if (!player->key[element - EL_GATE_1])
10550             return MF_NO_ACTION;
10551         }
10552         else if (IS_GATE_GRAY(element))
10553         {
10554           if (!player->key[element - EL_GATE_1_GRAY])
10555             return MF_NO_ACTION;
10556         }
10557         else if (element == EL_EXIT_OPEN ||
10558                  element == EL_SP_EXIT_OPEN ||
10559                  element == EL_SP_EXIT_OPENING)
10560         {
10561           sound_action = ACTION_PASSING;        /* player is passing exit */
10562         }
10563         else if (element == EL_EMPTY)
10564         {
10565           sound_action = ACTION_MOVING;         /* nothing to walk on */
10566         }
10567
10568         /* play sound from background or player, whatever is available */
10569         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10570           PlayLevelSoundElementAction(x, y, element, sound_action);
10571         else
10572           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10573
10574         break;
10575       }
10576 #if 1
10577       else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10578 #else
10579       else if (IS_PASSABLE(element))
10580 #endif
10581       {
10582 #if 0
10583         if (!canPassField(x, y, move_direction))
10584           return MF_NO_ACTION;
10585 #else
10586
10587 #if 0
10588 #if 1
10589         if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10590             !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10591             (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10592           return MF_NO_ACTION;
10593 #else
10594         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10595           return MF_NO_ACTION;
10596 #endif
10597 #endif
10598
10599 #if 1
10600         if (!ACCESS_FROM(element, opposite_direction))
10601           return MF_NO_ACTION;  /* field not accessible from this direction */
10602 #else
10603         if (IS_CUSTOM_ELEMENT(element) &&
10604             !ACCESS_FROM(element, opposite_direction))
10605           return MF_NO_ACTION;  /* field not accessible from this direction */
10606 #endif
10607
10608 #if 1
10609         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
10610           return MF_NO_ACTION;
10611 #endif
10612
10613 #endif
10614
10615         if (IS_EM_GATE(element))
10616         {
10617           if (!player->key[element - EL_EM_GATE_1])
10618             return MF_NO_ACTION;
10619         }
10620         else if (IS_EM_GATE_GRAY(element))
10621         {
10622           if (!player->key[element - EL_EM_GATE_1_GRAY])
10623             return MF_NO_ACTION;
10624         }
10625         else if (IS_SP_PORT(element))
10626         {
10627           if (element == EL_SP_GRAVITY_PORT_LEFT ||
10628               element == EL_SP_GRAVITY_PORT_RIGHT ||
10629               element == EL_SP_GRAVITY_PORT_UP ||
10630               element == EL_SP_GRAVITY_PORT_DOWN)
10631             game.gravity = !game.gravity;
10632         }
10633
10634         /* automatically move to the next field with double speed */
10635         player->programmed_action = move_direction;
10636 #if 1
10637         if (player->move_delay_reset_counter == 0)
10638         {
10639           player->move_delay_reset_counter = 2; /* two double speed steps */
10640
10641           DOUBLE_PLAYER_SPEED(player);
10642         }
10643 #else
10644         player->move_delay_reset_counter = 2;
10645
10646         DOUBLE_PLAYER_SPEED(player);
10647 #endif
10648
10649         PlayLevelSoundAction(x, y, ACTION_PASSING);
10650
10651         break;
10652       }
10653       else if (IS_DIGGABLE(element))
10654       {
10655         RemoveField(x, y);
10656
10657         if (mode != DF_SNAP)
10658         {
10659 #if 1
10660           GfxElement[x][y] = GFX_ELEMENT(element);
10661 #else
10662           GfxElement[x][y] =
10663             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10664 #endif
10665           player->is_digging = TRUE;
10666         }
10667
10668         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10669
10670         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10671                                             player->index_bit, dig_side);
10672
10673 #if 1
10674         if (mode == DF_SNAP)
10675           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
10676 #endif
10677
10678         break;
10679       }
10680       else if (IS_COLLECTIBLE(element))
10681       {
10682         RemoveField(x, y);
10683
10684         if (mode != DF_SNAP)
10685         {
10686           GfxElement[x][y] = element;
10687           player->is_collecting = TRUE;
10688         }
10689
10690         if (element == EL_SPEED_PILL)
10691           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10692         else if (element == EL_EXTRA_TIME && level.time > 0)
10693         {
10694           TimeLeft += 10;
10695           DrawGameValue_Time(TimeLeft);
10696         }
10697         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10698         {
10699           player->shield_normal_time_left += 10;
10700           if (element == EL_SHIELD_DEADLY)
10701             player->shield_deadly_time_left += 10;
10702         }
10703         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10704         {
10705           if (player->inventory_size < MAX_INVENTORY_SIZE)
10706             player->inventory_element[player->inventory_size++] = element;
10707
10708           DrawGameValue_Dynamite(local_player->inventory_size);
10709         }
10710         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10711         {
10712           player->dynabomb_count++;
10713           player->dynabombs_left++;
10714         }
10715         else if (element == EL_DYNABOMB_INCREASE_SIZE)
10716         {
10717           player->dynabomb_size++;
10718         }
10719         else if (element == EL_DYNABOMB_INCREASE_POWER)
10720         {
10721           player->dynabomb_xl = TRUE;
10722         }
10723         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10724                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10725         {
10726           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10727                         element - EL_KEY_1 : element - EL_EM_KEY_1);
10728
10729           player->key[key_nr] = TRUE;
10730
10731           DrawGameValue_Keys(player);
10732
10733           redraw_mask |= REDRAW_DOOR_1;
10734         }
10735         else if (IS_ENVELOPE(element))
10736         {
10737 #if 1
10738           player->show_envelope = element;
10739 #else
10740           ShowEnvelope(element - EL_ENVELOPE_1);
10741 #endif
10742         }
10743         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
10744         {
10745           int i;
10746
10747           if (element_info[element].collect_count == 0)
10748             player->inventory_infinite_element = element;
10749           else
10750             for (i = 0; i < element_info[element].collect_count; i++)
10751               if (player->inventory_size < MAX_INVENTORY_SIZE)
10752                 player->inventory_element[player->inventory_size++] = element;
10753
10754           DrawGameValue_Dynamite(local_player->inventory_size);
10755         }
10756         else if (element_info[element].collect_count > 0)
10757         {
10758           local_player->gems_still_needed -=
10759             element_info[element].collect_count;
10760           if (local_player->gems_still_needed < 0)
10761             local_player->gems_still_needed = 0;
10762
10763           DrawGameValue_Emeralds(local_player->gems_still_needed);
10764         }
10765
10766         RaiseScoreElement(element);
10767         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10768
10769         CheckTriggeredElementChangeByPlayer(x, y, element,
10770                                             CE_OTHER_GETS_COLLECTED,
10771                                             player->index_bit, dig_side);
10772
10773 #if 1
10774         if (mode == DF_SNAP)
10775           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
10776 #endif
10777
10778         break;
10779       }
10780       else if (IS_PUSHABLE(element))
10781       {
10782         if (mode == DF_SNAP && element != EL_BD_ROCK)
10783           return MF_NO_ACTION;
10784
10785         if (CAN_FALL(element) && dy)
10786           return MF_NO_ACTION;
10787
10788         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10789             !(element == EL_SPRING && level.use_spring_bug))
10790           return MF_NO_ACTION;
10791
10792 #if 1
10793         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10794             ((move_direction & MV_VERTICAL &&
10795               ((element_info[element].move_pattern & MV_LEFT &&
10796                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10797                (element_info[element].move_pattern & MV_RIGHT &&
10798                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10799              (move_direction & MV_HORIZONTAL &&
10800               ((element_info[element].move_pattern & MV_UP &&
10801                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10802                (element_info[element].move_pattern & MV_DOWN &&
10803                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10804           return MF_NO_ACTION;
10805 #endif
10806
10807 #if 1
10808         /* do not push elements already moving away faster than player */
10809         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10810             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10811           return MF_NO_ACTION;
10812 #else
10813         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10814           return MF_NO_ACTION;
10815 #endif
10816
10817 #if 1
10818         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10819         {
10820           if (player->push_delay_value == -1)
10821             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10822         }
10823         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10824         {
10825           if (!player->is_pushing)
10826             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10827         }
10828
10829         /*
10830         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10831             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10832              !player_is_pushing))
10833           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10834         */
10835 #else
10836         if (!player->is_pushing &&
10837             game.engine_version >= VERSION_IDENT(2,2,0,7))
10838           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10839 #endif
10840
10841 #if 0
10842         printf("::: push delay: %ld [%d, %d] [%d]\n",
10843                player->push_delay_value, FrameCounter, game.engine_version,
10844                player->is_pushing);
10845 #endif
10846
10847         player->is_pushing = TRUE;
10848
10849         if (!(IN_LEV_FIELD(nextx, nexty) &&
10850               (IS_FREE(nextx, nexty) ||
10851                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10852                 IS_SB_ELEMENT(element)))))
10853           return MF_NO_ACTION;
10854
10855         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10856           return MF_NO_ACTION;
10857
10858         if (player->push_delay == 0)    /* new pushing; restart delay */
10859           player->push_delay = FrameCounter;
10860
10861         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10862             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10863             element != EL_SPRING && element != EL_BALLOON)
10864         {
10865           /* make sure that there is no move delay before next try to push */
10866           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10867             player->move_delay = INITIAL_MOVE_DELAY_OFF;
10868
10869           return MF_NO_ACTION;
10870         }
10871
10872 #if 0
10873         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10874 #endif
10875
10876         if (IS_SB_ELEMENT(element))
10877         {
10878           if (element == EL_SOKOBAN_FIELD_FULL)
10879           {
10880             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10881             local_player->sokobanfields_still_needed++;
10882           }
10883
10884           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10885           {
10886             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10887             local_player->sokobanfields_still_needed--;
10888           }
10889
10890           Feld[x][y] = EL_SOKOBAN_OBJECT;
10891
10892           if (Back[x][y] == Back[nextx][nexty])
10893             PlayLevelSoundAction(x, y, ACTION_PUSHING);
10894           else if (Back[x][y] != 0)
10895             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10896                                         ACTION_EMPTYING);
10897           else
10898             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10899                                         ACTION_FILLING);
10900
10901           if (local_player->sokobanfields_still_needed == 0 &&
10902               game.emulation == EMU_SOKOBAN)
10903           {
10904             player->LevelSolved = player->GameOver = TRUE;
10905             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10906           }
10907         }
10908         else
10909           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10910
10911         InitMovingField(x, y, move_direction);
10912         GfxAction[x][y] = ACTION_PUSHING;
10913
10914         if (mode == DF_SNAP)
10915           ContinueMoving(x, y);
10916         else
10917           MovPos[x][y] = (dx != 0 ? dx : dy);
10918
10919         Pushed[x][y] = TRUE;
10920         Pushed[nextx][nexty] = TRUE;
10921
10922         if (game.engine_version < VERSION_IDENT(2,2,0,7))
10923           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10924         else
10925           player->push_delay_value = -1;        /* get new value later */
10926
10927 #if 1
10928         /* check for element change _after_ element has been pushed! */
10929 #else
10930
10931 #if 1
10932       /* !!! TEST ONLY !!! */
10933         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10934                                    player->index_bit, dig_side);
10935         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
10936                                             player->index_bit, dig_side);
10937 #else
10938         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
10939                                             player->index_bit, dig_side);
10940         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10941                                    player->index_bit, dig_side);
10942 #endif
10943 #endif
10944
10945         break;
10946       }
10947       else if (IS_SWITCHABLE(element))
10948       {
10949         if (PLAYER_SWITCHING(player, x, y))
10950           return MF_ACTION;
10951
10952         player->is_switching = TRUE;
10953         player->switch_x = x;
10954         player->switch_y = y;
10955
10956         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10957
10958         if (element == EL_ROBOT_WHEEL)
10959         {
10960           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10961           ZX = x;
10962           ZY = y;
10963
10964           DrawLevelField(x, y);
10965         }
10966         else if (element == EL_SP_TERMINAL)
10967         {
10968           int xx, yy;
10969
10970           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10971           {
10972             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10973               Bang(xx, yy);
10974             else if (Feld[xx][yy] == EL_SP_TERMINAL)
10975               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10976           }
10977         }
10978         else if (IS_BELT_SWITCH(element))
10979         {
10980           ToggleBeltSwitch(x, y);
10981         }
10982         else if (element == EL_SWITCHGATE_SWITCH_UP ||
10983                  element == EL_SWITCHGATE_SWITCH_DOWN)
10984         {
10985           ToggleSwitchgateSwitch(x, y);
10986         }
10987         else if (element == EL_LIGHT_SWITCH ||
10988                  element == EL_LIGHT_SWITCH_ACTIVE)
10989         {
10990           ToggleLightSwitch(x, y);
10991
10992 #if 0
10993           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10994                          SND_LIGHT_SWITCH_ACTIVATING :
10995                          SND_LIGHT_SWITCH_DEACTIVATING);
10996 #endif
10997         }
10998         else if (element == EL_TIMEGATE_SWITCH)
10999         {
11000           ActivateTimegateSwitch(x, y);
11001         }
11002         else if (element == EL_BALLOON_SWITCH_LEFT ||
11003                  element == EL_BALLOON_SWITCH_RIGHT ||
11004                  element == EL_BALLOON_SWITCH_UP ||
11005                  element == EL_BALLOON_SWITCH_DOWN ||
11006                  element == EL_BALLOON_SWITCH_ANY)
11007         {
11008           if (element == EL_BALLOON_SWITCH_ANY)
11009             game.balloon_dir = move_direction;
11010           else
11011             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
11012                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11013                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
11014                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
11015                                 MV_NO_MOVING);
11016         }
11017         else if (element == EL_LAMP)
11018         {
11019           Feld[x][y] = EL_LAMP_ACTIVE;
11020           local_player->lights_still_needed--;
11021
11022           DrawLevelField(x, y);
11023         }
11024         else if (element == EL_TIME_ORB_FULL)
11025         {
11026           Feld[x][y] = EL_TIME_ORB_EMPTY;
11027           TimeLeft += 10;
11028           DrawGameValue_Time(TimeLeft);
11029
11030           DrawLevelField(x, y);
11031
11032 #if 0
11033           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11034 #endif
11035         }
11036
11037         return MF_ACTION;
11038       }
11039       else
11040       {
11041         if (!PLAYER_SWITCHING(player, x, y))
11042         {
11043           player->is_switching = TRUE;
11044           player->switch_x = x;
11045           player->switch_y = y;
11046
11047 #if 1
11048           /* !!! TEST ONLY !!! */
11049           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11050                                      player->index_bit, dig_side);
11051           CheckTriggeredElementChangeByPlayer(x, y, element,
11052                                               CE_OTHER_IS_SWITCHING,
11053                                               player->index_bit, dig_side);
11054 #else
11055           CheckTriggeredElementChangeByPlayer(x, y, element,
11056                                               CE_OTHER_IS_SWITCHING,
11057                                               player->index_bit, dig_side);
11058           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11059                                      player->index_bit, dig_side);
11060 #endif
11061         }
11062
11063 #if 1
11064         /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11065         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11066                                    player->index_bit, dig_side);
11067         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11068                                             player->index_bit, dig_side);
11069 #else
11070         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11071                                             player->index_bit, dig_side);
11072         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11073                                    player->index_bit, dig_side);
11074 #endif
11075       }
11076
11077       return MF_NO_ACTION;
11078   }
11079
11080   player->push_delay = 0;
11081
11082   if (Feld[x][y] != element)            /* really digged/collected something */
11083     player->is_collecting = !player->is_digging;
11084
11085   return MF_MOVING;
11086 }
11087
11088 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11089 {
11090   int jx = player->jx, jy = player->jy;
11091   int x = jx + dx, y = jy + dy;
11092   int snap_direction = (dx == -1 ? MV_LEFT :
11093                         dx == +1 ? MV_RIGHT :
11094                         dy == -1 ? MV_UP :
11095                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11096
11097 #if 0
11098   if (player->MovPos)
11099     return FALSE;
11100 #else
11101   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
11102     return FALSE;
11103 #endif
11104
11105   if (!player->active || !IN_LEV_FIELD(x, y))
11106     return FALSE;
11107
11108   if (dx && dy)
11109     return FALSE;
11110
11111   if (!dx && !dy)
11112   {
11113     if (player->MovPos == 0)
11114       player->is_pushing = FALSE;
11115
11116     player->is_snapping = FALSE;
11117
11118     if (player->MovPos == 0)
11119     {
11120       player->is_moving = FALSE;
11121       player->is_digging = FALSE;
11122       player->is_collecting = FALSE;
11123     }
11124
11125     return FALSE;
11126   }
11127
11128   if (player->is_snapping)
11129     return FALSE;
11130
11131   player->MovDir = snap_direction;
11132
11133 #if 1
11134   if (player->MovPos == 0)
11135 #endif
11136   {
11137     player->is_moving = FALSE;
11138     player->is_digging = FALSE;
11139     player->is_collecting = FALSE;
11140   }
11141
11142   player->is_dropping = FALSE;
11143
11144   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11145     return FALSE;
11146
11147   player->is_snapping = TRUE;
11148
11149 #if 1
11150   if (player->MovPos == 0)
11151 #endif
11152   {
11153     player->is_moving = FALSE;
11154     player->is_digging = FALSE;
11155     player->is_collecting = FALSE;
11156   }
11157
11158   DrawLevelField(x, y);
11159   BackToFront();
11160
11161   return TRUE;
11162 }
11163
11164 boolean DropElement(struct PlayerInfo *player)
11165 {
11166   static int trigger_sides[4] =
11167   {
11168     CH_SIDE_LEFT,       /* dropping left  */
11169     CH_SIDE_RIGHT,      /* dropping right */
11170     CH_SIDE_TOP,        /* dropping up    */
11171     CH_SIDE_BOTTOM,     /* dropping down  */
11172   };
11173   int jx = player->jx, jy = player->jy;
11174   int drop_direction = player->MovDir;
11175   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11176   int old_element = Feld[jx][jy];
11177   int drop_element = (player->inventory_size > 0 ?
11178                       player->inventory_element[player->inventory_size - 1] :
11179                       player->inventory_infinite_element != EL_UNDEFINED ?
11180                       player->inventory_infinite_element :
11181                       player->dynabombs_left > 0 ?
11182                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11183                       EL_UNDEFINED);
11184   int new_element = drop_element;       /* default: element does not change */
11185
11186   /* check if player is active, not moving and ready to drop */
11187   if (!player->active || player->MovPos || player->drop_delay > 0)
11188     return FALSE;
11189
11190   /* check if player has anything that can be dropped */
11191 #if 1
11192   if (new_element == EL_UNDEFINED)
11193     return FALSE;
11194 #else
11195   if (player->inventory_size == 0 &&
11196       player->inventory_infinite_element == EL_UNDEFINED &&
11197       player->dynabombs_left == 0)
11198     return FALSE;
11199 #endif
11200
11201   /* check if anything can be dropped at the current position */
11202   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11203     return FALSE;
11204
11205   /* collected custom elements can only be dropped on empty fields */
11206 #if 1
11207   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11208     return FALSE;
11209 #else
11210   if (player->inventory_size > 0 &&
11211       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11212       && old_element != EL_EMPTY)
11213     return FALSE;
11214 #endif
11215
11216   if (old_element != EL_EMPTY)
11217     Back[jx][jy] = old_element;         /* store old element on this field */
11218
11219   ResetGfxAnimation(jx, jy);
11220   ResetRandomAnimationValue(jx, jy);
11221
11222   if (player->inventory_size > 0 ||
11223       player->inventory_infinite_element != EL_UNDEFINED)
11224   {
11225     if (player->inventory_size > 0)
11226     {
11227       player->inventory_size--;
11228
11229 #if 0
11230       new_element = player->inventory_element[player->inventory_size];
11231 #endif
11232
11233       DrawGameValue_Dynamite(local_player->inventory_size);
11234
11235       if (new_element == EL_DYNAMITE)
11236         new_element = EL_DYNAMITE_ACTIVE;
11237       else if (new_element == EL_SP_DISK_RED)
11238         new_element = EL_SP_DISK_RED_ACTIVE;
11239     }
11240
11241     Feld[jx][jy] = new_element;
11242
11243     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
11244       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
11245
11246     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11247
11248 #if 1
11249     /* needed if previous element just changed to "empty" in the last frame */
11250     Changed[jx][jy] = 0;                /* allow another change */
11251 #endif
11252
11253 #if 1
11254     /* !!! TEST ONLY !!! */
11255     CheckElementChangeByPlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
11256                                player->index_bit, drop_side);
11257     CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11258                                         CE_OTHER_GETS_DROPPED,
11259                                         player->index_bit, drop_side);
11260 #else
11261     CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11262                                         CE_OTHER_GETS_DROPPED,
11263                                         player->index_bit, drop_side);
11264     CheckElementChangeByPlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
11265                                player->index_bit, drop_side);
11266 #endif
11267
11268     TestIfElementTouchesCustomElement(jx, jy);
11269   }
11270   else          /* player is dropping a dyna bomb */
11271   {
11272     player->dynabombs_left--;
11273
11274 #if 0
11275     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11276 #endif
11277
11278     Feld[jx][jy] = new_element;
11279
11280     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
11281       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
11282
11283     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
11284   }
11285
11286
11287
11288 #if 1
11289
11290   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
11291   {
11292 #if 1
11293     InitField_WithBug1(jx, jy, FALSE);
11294 #else
11295     InitField(jx, jy, FALSE);
11296     if (CAN_MOVE(Feld[jx][jy]))
11297       InitMovDir(jx, jy);
11298 #endif
11299   }
11300
11301   new_element = Feld[jx][jy];           /* element might have changed */
11302
11303   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11304       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11305   {
11306 #if 0
11307     int move_stepsize = element_info[new_element].move_stepsize;
11308 #endif
11309     int direction, dx, dy, nextx, nexty;
11310
11311     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11312       MovDir[jx][jy] = player->MovDir;
11313
11314     direction = MovDir[jx][jy];
11315     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11316     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11317     nextx = jx + dx;
11318     nexty = jy + dy;
11319
11320     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11321     {
11322 #if 0
11323       WasJustMoving[jx][jy] = 3;
11324 #else
11325       InitMovingField(jx, jy, direction);
11326       ContinueMoving(jx, jy);
11327 #endif
11328     }
11329     else
11330     {
11331       Changed[jx][jy] = 0;              /* allow another change */
11332
11333 #if 1
11334       TestIfElementHitsCustomElement(jx, jy, direction);
11335 #else
11336       CheckElementChangeBySide(jx, jy, new_element, touched_element,
11337                                CE_HITTING_SOMETHING, direction);
11338 #endif
11339     }
11340
11341 #if 0
11342     player->drop_delay = 2 * TILEX / move_stepsize + 1;
11343 #endif
11344   }
11345
11346 #if 0
11347   player->drop_delay = 8 + 8 + 8;
11348 #endif
11349
11350 #if 1
11351   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11352 #endif
11353
11354 #endif
11355
11356   player->is_dropping = TRUE;
11357
11358
11359   return TRUE;
11360 }
11361
11362 /* ------------------------------------------------------------------------- */
11363 /* game sound playing functions                                              */
11364 /* ------------------------------------------------------------------------- */
11365
11366 static int *loop_sound_frame = NULL;
11367 static int *loop_sound_volume = NULL;
11368
11369 void InitPlayLevelSound()
11370 {
11371   int num_sounds = getSoundListSize();
11372
11373   checked_free(loop_sound_frame);
11374   checked_free(loop_sound_volume);
11375
11376   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
11377   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11378 }
11379
11380 static void PlayLevelSound(int x, int y, int nr)
11381 {
11382   int sx = SCREENX(x), sy = SCREENY(y);
11383   int volume, stereo_position;
11384   int max_distance = 8;
11385   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11386
11387   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11388       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11389     return;
11390
11391   if (!IN_LEV_FIELD(x, y) ||
11392       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11393       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11394     return;
11395
11396   volume = SOUND_MAX_VOLUME;
11397
11398   if (!IN_SCR_FIELD(sx, sy))
11399   {
11400     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11401     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11402
11403     volume -= volume * (dx > dy ? dx : dy) / max_distance;
11404   }
11405
11406   stereo_position = (SOUND_MAX_LEFT +
11407                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11408                      (SCR_FIELDX + 2 * max_distance));
11409
11410   if (IS_LOOP_SOUND(nr))
11411   {
11412     /* This assures that quieter loop sounds do not overwrite louder ones,
11413        while restarting sound volume comparison with each new game frame. */
11414
11415     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11416       return;
11417
11418     loop_sound_volume[nr] = volume;
11419     loop_sound_frame[nr] = FrameCounter;
11420   }
11421
11422   PlaySoundExt(nr, volume, stereo_position, type);
11423 }
11424
11425 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11426 {
11427   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11428                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
11429                  y < LEVELY(BY1) ? LEVELY(BY1) :
11430                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
11431                  sound_action);
11432 }
11433
11434 static void PlayLevelSoundAction(int x, int y, int action)
11435 {
11436   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11437 }
11438
11439 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11440 {
11441   int sound_effect = element_info[element].sound[action];
11442
11443   if (sound_effect != SND_UNDEFINED)
11444     PlayLevelSound(x, y, sound_effect);
11445 }
11446
11447 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11448                                               int action)
11449 {
11450   int sound_effect = element_info[element].sound[action];
11451
11452   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11453     PlayLevelSound(x, y, sound_effect);
11454 }
11455
11456 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11457 {
11458   int sound_effect = element_info[Feld[x][y]].sound[action];
11459
11460   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11461     PlayLevelSound(x, y, sound_effect);
11462 }
11463
11464 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11465 {
11466   int sound_effect = element_info[Feld[x][y]].sound[action];
11467
11468   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11469     StopSound(sound_effect);
11470 }
11471
11472 static void PlayLevelMusic()
11473 {
11474   if (levelset.music[level_nr] != MUS_UNDEFINED)
11475     PlayMusic(levelset.music[level_nr]);        /* from config file */
11476   else
11477     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
11478 }
11479
11480 void RaiseScore(int value)
11481 {
11482   local_player->score += value;
11483
11484   DrawGameValue_Score(local_player->score);
11485 }
11486
11487 void RaiseScoreElement(int element)
11488 {
11489   switch(element)
11490   {
11491     case EL_EMERALD:
11492     case EL_BD_DIAMOND:
11493     case EL_EMERALD_YELLOW:
11494     case EL_EMERALD_RED:
11495     case EL_EMERALD_PURPLE:
11496     case EL_SP_INFOTRON:
11497       RaiseScore(level.score[SC_EMERALD]);
11498       break;
11499     case EL_DIAMOND:
11500       RaiseScore(level.score[SC_DIAMOND]);
11501       break;
11502     case EL_CRYSTAL:
11503       RaiseScore(level.score[SC_CRYSTAL]);
11504       break;
11505     case EL_PEARL:
11506       RaiseScore(level.score[SC_PEARL]);
11507       break;
11508     case EL_BUG:
11509     case EL_BD_BUTTERFLY:
11510     case EL_SP_ELECTRON:
11511       RaiseScore(level.score[SC_BUG]);
11512       break;
11513     case EL_SPACESHIP:
11514     case EL_BD_FIREFLY:
11515     case EL_SP_SNIKSNAK:
11516       RaiseScore(level.score[SC_SPACESHIP]);
11517       break;
11518     case EL_YAMYAM:
11519     case EL_DARK_YAMYAM:
11520       RaiseScore(level.score[SC_YAMYAM]);
11521       break;
11522     case EL_ROBOT:
11523       RaiseScore(level.score[SC_ROBOT]);
11524       break;
11525     case EL_PACMAN:
11526       RaiseScore(level.score[SC_PACMAN]);
11527       break;
11528     case EL_NUT:
11529       RaiseScore(level.score[SC_NUT]);
11530       break;
11531     case EL_DYNAMITE:
11532     case EL_SP_DISK_RED:
11533     case EL_DYNABOMB_INCREASE_NUMBER:
11534     case EL_DYNABOMB_INCREASE_SIZE:
11535     case EL_DYNABOMB_INCREASE_POWER:
11536       RaiseScore(level.score[SC_DYNAMITE]);
11537       break;
11538     case EL_SHIELD_NORMAL:
11539     case EL_SHIELD_DEADLY:
11540       RaiseScore(level.score[SC_SHIELD]);
11541       break;
11542     case EL_EXTRA_TIME:
11543       RaiseScore(level.score[SC_TIME_BONUS]);
11544       break;
11545     case EL_KEY_1:
11546     case EL_KEY_2:
11547     case EL_KEY_3:
11548     case EL_KEY_4:
11549       RaiseScore(level.score[SC_KEY]);
11550       break;
11551     default:
11552       RaiseScore(element_info[element].collect_score);
11553       break;
11554   }
11555 }
11556
11557 void RequestQuitGame(boolean ask_if_really_quit)
11558 {
11559   if (AllPlayersGone ||
11560       !ask_if_really_quit ||
11561       level_editor_test_game ||
11562       Request("Do you really want to quit the game ?",
11563               REQ_ASK | REQ_STAY_CLOSED))
11564   {
11565 #if defined(PLATFORM_UNIX)
11566     if (options.network)
11567       SendToServer_StopPlaying();
11568     else
11569 #endif
11570     {
11571       game_status = GAME_MODE_MAIN;
11572       DrawMainMenu();
11573     }
11574   }
11575   else
11576   {
11577
11578 #if 1
11579     if (tape.playing && tape.deactivate_display)
11580       TapeDeactivateDisplayOff(TRUE);
11581 #endif
11582
11583     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11584
11585 #if 1
11586     if (tape.playing && tape.deactivate_display)
11587       TapeDeactivateDisplayOn();
11588 #endif
11589
11590   }
11591 }
11592
11593
11594 /* ---------- new game button stuff ---------------------------------------- */
11595
11596 /* graphic position values for game buttons */
11597 #define GAME_BUTTON_XSIZE       30
11598 #define GAME_BUTTON_YSIZE       30
11599 #define GAME_BUTTON_XPOS        5
11600 #define GAME_BUTTON_YPOS        215
11601 #define SOUND_BUTTON_XPOS       5
11602 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11603
11604 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11605 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11606 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11607 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11608 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11609 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11610
11611 static struct
11612 {
11613   int x, y;
11614   int gadget_id;
11615   char *infotext;
11616 } gamebutton_info[NUM_GAME_BUTTONS] =
11617 {
11618   {
11619     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
11620     GAME_CTRL_ID_STOP,
11621     "stop game"
11622   },
11623   {
11624     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
11625     GAME_CTRL_ID_PAUSE,
11626     "pause game"
11627   },
11628   {
11629     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
11630     GAME_CTRL_ID_PLAY,
11631     "play game"
11632   },
11633   {
11634     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
11635     SOUND_CTRL_ID_MUSIC,
11636     "background music on/off"
11637   },
11638   {
11639     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
11640     SOUND_CTRL_ID_LOOPS,
11641     "sound loops on/off"
11642   },
11643   {
11644     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
11645     SOUND_CTRL_ID_SIMPLE,
11646     "normal sounds on/off"
11647   }
11648 };
11649
11650 void CreateGameButtons()
11651 {
11652   int i;
11653
11654   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11655   {
11656     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11657     struct GadgetInfo *gi;
11658     int button_type;
11659     boolean checked;
11660     unsigned long event_mask;
11661     int gd_xoffset, gd_yoffset;
11662     int gd_x1, gd_x2, gd_y1, gd_y2;
11663     int id = i;
11664
11665     gd_xoffset = gamebutton_info[i].x;
11666     gd_yoffset = gamebutton_info[i].y;
11667     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11668     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11669
11670     if (id == GAME_CTRL_ID_STOP ||
11671         id == GAME_CTRL_ID_PAUSE ||
11672         id == GAME_CTRL_ID_PLAY)
11673     {
11674       button_type = GD_TYPE_NORMAL_BUTTON;
11675       checked = FALSE;
11676       event_mask = GD_EVENT_RELEASED;
11677       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11678       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11679     }
11680     else
11681     {
11682       button_type = GD_TYPE_CHECK_BUTTON;
11683       checked =
11684         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11685          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11686          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11687       event_mask = GD_EVENT_PRESSED;
11688       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
11689       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11690     }
11691
11692     gi = CreateGadget(GDI_CUSTOM_ID, id,
11693                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
11694                       GDI_X, DX + gd_xoffset,
11695                       GDI_Y, DY + gd_yoffset,
11696                       GDI_WIDTH, GAME_BUTTON_XSIZE,
11697                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
11698                       GDI_TYPE, button_type,
11699                       GDI_STATE, GD_BUTTON_UNPRESSED,
11700                       GDI_CHECKED, checked,
11701                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11702                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11703                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11704                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11705                       GDI_EVENT_MASK, event_mask,
11706                       GDI_CALLBACK_ACTION, HandleGameButtons,
11707                       GDI_END);
11708
11709     if (gi == NULL)
11710       Error(ERR_EXIT, "cannot create gadget");
11711
11712     game_gadget[id] = gi;
11713   }
11714 }
11715
11716 void FreeGameButtons()
11717 {
11718   int i;
11719
11720   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11721     FreeGadget(game_gadget[i]);
11722 }
11723
11724 static void MapGameButtons()
11725 {
11726   int i;
11727
11728   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11729     MapGadget(game_gadget[i]);
11730 }
11731
11732 void UnmapGameButtons()
11733 {
11734   int i;
11735
11736   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11737     UnmapGadget(game_gadget[i]);
11738 }
11739
11740 static void HandleGameButtons(struct GadgetInfo *gi)
11741 {
11742   int id = gi->custom_id;
11743
11744   if (game_status != GAME_MODE_PLAYING)
11745     return;
11746
11747   switch (id)
11748   {
11749     case GAME_CTRL_ID_STOP:
11750       RequestQuitGame(TRUE);
11751       break;
11752
11753     case GAME_CTRL_ID_PAUSE:
11754       if (options.network)
11755       {
11756 #if defined(PLATFORM_UNIX)
11757         if (tape.pausing)
11758           SendToServer_ContinuePlaying();
11759         else
11760           SendToServer_PausePlaying();
11761 #endif
11762       }
11763       else
11764         TapeTogglePause(TAPE_TOGGLE_MANUAL);
11765       break;
11766
11767     case GAME_CTRL_ID_PLAY:
11768       if (tape.pausing)
11769       {
11770 #if defined(PLATFORM_UNIX)
11771         if (options.network)
11772           SendToServer_ContinuePlaying();
11773         else
11774 #endif
11775         {
11776           tape.pausing = FALSE;
11777           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11778         }
11779       }
11780       break;
11781
11782     case SOUND_CTRL_ID_MUSIC:
11783       if (setup.sound_music)
11784       { 
11785         setup.sound_music = FALSE;
11786         FadeMusic();
11787       }
11788       else if (audio.music_available)
11789       { 
11790         setup.sound = setup.sound_music = TRUE;
11791
11792         SetAudioMode(setup.sound);
11793
11794         PlayLevelMusic();
11795       }
11796       break;
11797
11798     case SOUND_CTRL_ID_LOOPS:
11799       if (setup.sound_loops)
11800         setup.sound_loops = FALSE;
11801       else if (audio.loops_available)
11802       {
11803         setup.sound = setup.sound_loops = TRUE;
11804         SetAudioMode(setup.sound);
11805       }
11806       break;
11807
11808     case SOUND_CTRL_ID_SIMPLE:
11809       if (setup.sound_simple)
11810         setup.sound_simple = FALSE;
11811       else if (audio.sound_available)
11812       {
11813         setup.sound = setup.sound_simple = TRUE;
11814         SetAudioMode(setup.sound);
11815       }
11816       break;
11817
11818     default:
11819       break;
11820   }
11821 }