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