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