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