rnd-20040322-B-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 1
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 (!CAN_MOVE(element) ||
5969       (CAN_FALL(element) && direction == MV_DOWN))
5970     GfxDir[x][y] = MovDir[newx][newy] = 0;
5971
5972 #endif
5973 #endif
5974
5975   DrawLevelField(x, y);
5976   DrawLevelField(newx, newy);
5977
5978   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
5979
5980   /* prevent pushed element from moving on in pushed direction */
5981   if (pushed_by_player && CAN_MOVE(element) &&
5982       element_info[element].move_pattern & MV_ANY_DIRECTION &&
5983       !(element_info[element].move_pattern & direction))
5984     TurnRound(newx, newy);
5985
5986 #if 1
5987   /* prevent elements on conveyor belt from moving on in last direction */
5988   if (pushed_by_conveyor && CAN_FALL(element) &&
5989       direction & MV_HORIZONTAL)
5990     MovDir[newx][newy] = 0;
5991 #endif
5992
5993   if (!pushed_by_player)
5994   {
5995     WasJustMoving[newx][newy] = 3;
5996
5997     if (CAN_FALL(element) && direction == MV_DOWN)
5998       WasJustFalling[newx][newy] = 3;
5999   }
6000
6001   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
6002   {
6003     TestIfBadThingTouchesHero(newx, newy);
6004     TestIfBadThingTouchesFriend(newx, newy);
6005
6006     if (!IS_CUSTOM_ELEMENT(element))
6007       TestIfBadThingTouchesOtherBadThing(newx, newy);
6008   }
6009   else if (element == EL_PENGUIN)
6010     TestIfFriendTouchesBadThing(newx, newy);
6011
6012   if (CAN_FALL(element) && direction == MV_DOWN &&
6013       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6014     Impact(x, newy);
6015
6016 #if 1
6017   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
6018 #endif
6019
6020 #if 0
6021   if (ChangePage[newx][newy] != -1)                     /* delayed change */
6022     ChangeElement(newx, newy, ChangePage[newx][newy]);
6023 #endif
6024
6025 #if 1
6026
6027   TestIfElementHitsCustomElement(newx, newy, direction);
6028
6029 #else
6030
6031   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6032   {
6033     int hitting_element = Feld[newx][newy];
6034
6035     /* !!! fix side (direction) orientation here and elsewhere !!! */
6036     CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6037                            direction);
6038
6039 #if 0
6040     if (IN_LEV_FIELD(nextx, nexty))
6041     {
6042       int opposite_direction = MV_DIR_OPPOSITE(direction);
6043       int hitting_side = direction;
6044       int touched_side = opposite_direction;
6045       int touched_element = MovingOrBlocked2Element(nextx, nexty);
6046       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6047                             MovDir[nextx][nexty] != direction ||
6048                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6049
6050       if (object_hit)
6051       {
6052         int i;
6053
6054         CheckElementChangeSide(nextx, nexty, touched_element,
6055                                CE_HIT_BY_SOMETHING, opposite_direction);
6056
6057         if (IS_CUSTOM_ELEMENT(hitting_element) &&
6058             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6059         {
6060           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6061           {
6062             struct ElementChangeInfo *change =
6063               &element_info[hitting_element].change_page[i];
6064
6065             if (change->can_change &&
6066                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6067                 change->trigger_side & touched_side &&
6068                 change->trigger_element == touched_element)
6069             {
6070               CheckElementChangePage(newx, newy, hitting_element,
6071                                      touched_element, CE_OTHER_IS_HITTING, i);
6072               break;
6073             }
6074           }
6075         }
6076
6077         if (IS_CUSTOM_ELEMENT(touched_element) &&
6078             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6079         {
6080           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6081           {
6082             struct ElementChangeInfo *change =
6083               &element_info[touched_element].change_page[i];
6084
6085             if (change->can_change &&
6086                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6087                 change->trigger_side & hitting_side &&
6088                 change->trigger_element == hitting_element)
6089             {
6090               CheckElementChangePage(nextx, nexty, touched_element,
6091                                      hitting_element, CE_OTHER_GETS_HIT, i);
6092               break;
6093             }
6094           }
6095         }
6096       }
6097     }
6098 #endif
6099   }
6100 #endif
6101
6102   TestIfPlayerTouchesCustomElement(newx, newy);
6103   TestIfElementTouchesCustomElement(newx, newy);
6104 }
6105
6106 int AmoebeNachbarNr(int ax, int ay)
6107 {
6108   int i;
6109   int element = Feld[ax][ay];
6110   int group_nr = 0;
6111   static int xy[4][2] =
6112   {
6113     { 0, -1 },
6114     { -1, 0 },
6115     { +1, 0 },
6116     { 0, +1 }
6117   };
6118
6119   for (i = 0; i < NUM_DIRECTIONS; i++)
6120   {
6121     int x = ax + xy[i][0];
6122     int y = ay + xy[i][1];
6123
6124     if (!IN_LEV_FIELD(x, y))
6125       continue;
6126
6127     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6128       group_nr = AmoebaNr[x][y];
6129   }
6130
6131   return group_nr;
6132 }
6133
6134 void AmoebenVereinigen(int ax, int ay)
6135 {
6136   int i, x, y, xx, yy;
6137   int new_group_nr = AmoebaNr[ax][ay];
6138   static int xy[4][2] =
6139   {
6140     { 0, -1 },
6141     { -1, 0 },
6142     { +1, 0 },
6143     { 0, +1 }
6144   };
6145
6146   if (new_group_nr == 0)
6147     return;
6148
6149   for (i = 0; i < NUM_DIRECTIONS; i++)
6150   {
6151     x = ax + xy[i][0];
6152     y = ay + xy[i][1];
6153
6154     if (!IN_LEV_FIELD(x, y))
6155       continue;
6156
6157     if ((Feld[x][y] == EL_AMOEBA_FULL ||
6158          Feld[x][y] == EL_BD_AMOEBA ||
6159          Feld[x][y] == EL_AMOEBA_DEAD) &&
6160         AmoebaNr[x][y] != new_group_nr)
6161     {
6162       int old_group_nr = AmoebaNr[x][y];
6163
6164       if (old_group_nr == 0)
6165         return;
6166
6167       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6168       AmoebaCnt[old_group_nr] = 0;
6169       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6170       AmoebaCnt2[old_group_nr] = 0;
6171
6172       for (yy = 0; yy < lev_fieldy; yy++)
6173       {
6174         for (xx = 0; xx < lev_fieldx; xx++)
6175         {
6176           if (AmoebaNr[xx][yy] == old_group_nr)
6177             AmoebaNr[xx][yy] = new_group_nr;
6178         }
6179       }
6180     }
6181   }
6182 }
6183
6184 void AmoebeUmwandeln(int ax, int ay)
6185 {
6186   int i, x, y;
6187
6188   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6189   {
6190     int group_nr = AmoebaNr[ax][ay];
6191
6192 #ifdef DEBUG
6193     if (group_nr == 0)
6194     {
6195       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6196       printf("AmoebeUmwandeln(): This should never happen!\n");
6197       return;
6198     }
6199 #endif
6200
6201     for (y = 0; y < lev_fieldy; y++)
6202     {
6203       for (x = 0; x < lev_fieldx; x++)
6204       {
6205         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6206         {
6207           AmoebaNr[x][y] = 0;
6208           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6209         }
6210       }
6211     }
6212     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6213                             SND_AMOEBA_TURNING_TO_GEM :
6214                             SND_AMOEBA_TURNING_TO_ROCK));
6215     Bang(ax, ay);
6216   }
6217   else
6218   {
6219     static int xy[4][2] =
6220     {
6221       { 0, -1 },
6222       { -1, 0 },
6223       { +1, 0 },
6224       { 0, +1 }
6225     };
6226
6227     for (i = 0; i < NUM_DIRECTIONS; i++)
6228     {
6229       x = ax + xy[i][0];
6230       y = ay + xy[i][1];
6231
6232       if (!IN_LEV_FIELD(x, y))
6233         continue;
6234
6235       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6236       {
6237         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6238                               SND_AMOEBA_TURNING_TO_GEM :
6239                               SND_AMOEBA_TURNING_TO_ROCK));
6240         Bang(x, y);
6241       }
6242     }
6243   }
6244 }
6245
6246 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6247 {
6248   int x, y;
6249   int group_nr = AmoebaNr[ax][ay];
6250   boolean done = FALSE;
6251
6252 #ifdef DEBUG
6253   if (group_nr == 0)
6254   {
6255     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6256     printf("AmoebeUmwandelnBD(): This should never happen!\n");
6257     return;
6258   }
6259 #endif
6260
6261   for (y = 0; y < lev_fieldy; y++)
6262   {
6263     for (x = 0; x < lev_fieldx; x++)
6264     {
6265       if (AmoebaNr[x][y] == group_nr &&
6266           (Feld[x][y] == EL_AMOEBA_DEAD ||
6267            Feld[x][y] == EL_BD_AMOEBA ||
6268            Feld[x][y] == EL_AMOEBA_GROWING))
6269       {
6270         AmoebaNr[x][y] = 0;
6271         Feld[x][y] = new_element;
6272         InitField(x, y, FALSE);
6273         DrawLevelField(x, y);
6274         done = TRUE;
6275       }
6276     }
6277   }
6278
6279   if (done)
6280     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6281                             SND_BD_AMOEBA_TURNING_TO_ROCK :
6282                             SND_BD_AMOEBA_TURNING_TO_GEM));
6283 }
6284
6285 void AmoebeWaechst(int x, int y)
6286 {
6287   static unsigned long sound_delay = 0;
6288   static unsigned long sound_delay_value = 0;
6289
6290   if (!MovDelay[x][y])          /* start new growing cycle */
6291   {
6292     MovDelay[x][y] = 7;
6293
6294     if (DelayReached(&sound_delay, sound_delay_value))
6295     {
6296 #if 1
6297       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6298 #else
6299       if (Store[x][y] == EL_BD_AMOEBA)
6300         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6301       else
6302         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6303 #endif
6304       sound_delay_value = 30;
6305     }
6306   }
6307
6308   if (MovDelay[x][y])           /* wait some time before growing bigger */
6309   {
6310     MovDelay[x][y]--;
6311     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6312     {
6313       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6314                                            6 - MovDelay[x][y]);
6315
6316       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6317     }
6318
6319     if (!MovDelay[x][y])
6320     {
6321       Feld[x][y] = Store[x][y];
6322       Store[x][y] = 0;
6323       DrawLevelField(x, y);
6324     }
6325   }
6326 }
6327
6328 void AmoebaDisappearing(int x, int y)
6329 {
6330   static unsigned long sound_delay = 0;
6331   static unsigned long sound_delay_value = 0;
6332
6333   if (!MovDelay[x][y])          /* start new shrinking cycle */
6334   {
6335     MovDelay[x][y] = 7;
6336
6337     if (DelayReached(&sound_delay, sound_delay_value))
6338       sound_delay_value = 30;
6339   }
6340
6341   if (MovDelay[x][y])           /* wait some time before shrinking */
6342   {
6343     MovDelay[x][y]--;
6344     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6345     {
6346       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6347                                            6 - MovDelay[x][y]);
6348
6349       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6350     }
6351
6352     if (!MovDelay[x][y])
6353     {
6354       Feld[x][y] = EL_EMPTY;
6355       DrawLevelField(x, y);
6356
6357       /* don't let mole enter this field in this cycle;
6358          (give priority to objects falling to this field from above) */
6359       Stop[x][y] = TRUE;
6360     }
6361   }
6362 }
6363
6364 void AmoebeAbleger(int ax, int ay)
6365 {
6366   int i;
6367   int element = Feld[ax][ay];
6368   int graphic = el2img(element);
6369   int newax = ax, neway = ay;
6370   static int xy[4][2] =
6371   {
6372     { 0, -1 },
6373     { -1, 0 },
6374     { +1, 0 },
6375     { 0, +1 }
6376   };
6377
6378   if (!level.amoeba_speed)
6379   {
6380     Feld[ax][ay] = EL_AMOEBA_DEAD;
6381     DrawLevelField(ax, ay);
6382     return;
6383   }
6384
6385   if (IS_ANIMATED(graphic))
6386     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6387
6388   if (!MovDelay[ax][ay])        /* start making new amoeba field */
6389     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6390
6391   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
6392   {
6393     MovDelay[ax][ay]--;
6394     if (MovDelay[ax][ay])
6395       return;
6396   }
6397
6398   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6399   {
6400     int start = RND(4);
6401     int x = ax + xy[start][0];
6402     int y = ay + xy[start][1];
6403
6404     if (!IN_LEV_FIELD(x, y))
6405       return;
6406
6407     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6408     if (IS_FREE(x, y) ||
6409         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6410     {
6411       newax = x;
6412       neway = y;
6413     }
6414
6415     if (newax == ax && neway == ay)
6416       return;
6417   }
6418   else                          /* normal or "filled" (BD style) amoeba */
6419   {
6420     int start = RND(4);
6421     boolean waiting_for_player = FALSE;
6422
6423     for (i = 0; i < NUM_DIRECTIONS; i++)
6424     {
6425       int j = (start + i) % 4;
6426       int x = ax + xy[j][0];
6427       int y = ay + xy[j][1];
6428
6429       if (!IN_LEV_FIELD(x, y))
6430         continue;
6431
6432       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6433       if (IS_FREE(x, y) ||
6434           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6435       {
6436         newax = x;
6437         neway = y;
6438         break;
6439       }
6440       else if (IS_PLAYER(x, y))
6441         waiting_for_player = TRUE;
6442     }
6443
6444     if (newax == ax && neway == ay)             /* amoeba cannot grow */
6445     {
6446       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6447       {
6448         Feld[ax][ay] = EL_AMOEBA_DEAD;
6449         DrawLevelField(ax, ay);
6450         AmoebaCnt[AmoebaNr[ax][ay]]--;
6451
6452         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
6453         {
6454           if (element == EL_AMOEBA_FULL)
6455             AmoebeUmwandeln(ax, ay);
6456           else if (element == EL_BD_AMOEBA)
6457             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6458         }
6459       }
6460       return;
6461     }
6462     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6463     {
6464       /* amoeba gets larger by growing in some direction */
6465
6466       int new_group_nr = AmoebaNr[ax][ay];
6467
6468 #ifdef DEBUG
6469   if (new_group_nr == 0)
6470   {
6471     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6472     printf("AmoebeAbleger(): This should never happen!\n");
6473     return;
6474   }
6475 #endif
6476
6477       AmoebaNr[newax][neway] = new_group_nr;
6478       AmoebaCnt[new_group_nr]++;
6479       AmoebaCnt2[new_group_nr]++;
6480
6481       /* if amoeba touches other amoeba(s) after growing, unify them */
6482       AmoebenVereinigen(newax, neway);
6483
6484       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6485       {
6486         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6487         return;
6488       }
6489     }
6490   }
6491
6492   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6493       (neway == lev_fieldy - 1 && newax != ax))
6494   {
6495     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6496     Store[newax][neway] = element;
6497   }
6498   else if (neway == ay)
6499   {
6500     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6501 #if 1
6502     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6503 #else
6504     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6505 #endif
6506   }
6507   else
6508   {
6509     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6510     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6511     Store[ax][ay] = EL_AMOEBA_DROP;
6512     ContinueMoving(ax, ay);
6513     return;
6514   }
6515
6516   DrawLevelField(newax, neway);
6517 }
6518
6519 void Life(int ax, int ay)
6520 {
6521   int x1, y1, x2, y2;
6522   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6523   int life_time = 40;
6524   int element = Feld[ax][ay];
6525   int graphic = el2img(element);
6526   boolean changed = FALSE;
6527
6528   if (IS_ANIMATED(graphic))
6529     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6530
6531   if (Stop[ax][ay])
6532     return;
6533
6534   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6535     MovDelay[ax][ay] = life_time;
6536
6537   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6538   {
6539     MovDelay[ax][ay]--;
6540     if (MovDelay[ax][ay])
6541       return;
6542   }
6543
6544   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6545   {
6546     int xx = ax+x1, yy = ay+y1;
6547     int nachbarn = 0;
6548
6549     if (!IN_LEV_FIELD(xx, yy))
6550       continue;
6551
6552     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6553     {
6554       int x = xx+x2, y = yy+y2;
6555
6556       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6557         continue;
6558
6559       if (((Feld[x][y] == element ||
6560             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6561            !Stop[x][y]) ||
6562           (IS_FREE(x, y) && Stop[x][y]))
6563         nachbarn++;
6564     }
6565
6566     if (xx == ax && yy == ay)           /* field in the middle */
6567     {
6568       if (nachbarn < life[0] || nachbarn > life[1])
6569       {
6570         Feld[xx][yy] = EL_EMPTY;
6571         if (!Stop[xx][yy])
6572           DrawLevelField(xx, yy);
6573         Stop[xx][yy] = TRUE;
6574         changed = TRUE;
6575       }
6576     }
6577     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6578     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6579     {                                   /* free border field */
6580       if (nachbarn >= life[2] && nachbarn <= life[3])
6581       {
6582         Feld[xx][yy] = element;
6583         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6584         if (!Stop[xx][yy])
6585           DrawLevelField(xx, yy);
6586         Stop[xx][yy] = TRUE;
6587         changed = TRUE;
6588       }
6589     }
6590   }
6591
6592   if (changed)
6593     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6594                    SND_GAME_OF_LIFE_GROWING);
6595 }
6596
6597 static void InitRobotWheel(int x, int y)
6598 {
6599   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6600 }
6601
6602 static void RunRobotWheel(int x, int y)
6603 {
6604   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6605 }
6606
6607 static void StopRobotWheel(int x, int y)
6608 {
6609   if (ZX == x && ZY == y)
6610     ZX = ZY = -1;
6611 }
6612
6613 static void InitTimegateWheel(int x, int y)
6614 {
6615   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6616 }
6617
6618 static void RunTimegateWheel(int x, int y)
6619 {
6620   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6621 }
6622
6623 void CheckExit(int x, int y)
6624 {
6625   if (local_player->gems_still_needed > 0 ||
6626       local_player->sokobanfields_still_needed > 0 ||
6627       local_player->lights_still_needed > 0)
6628   {
6629     int element = Feld[x][y];
6630     int graphic = el2img(element);
6631
6632     if (IS_ANIMATED(graphic))
6633       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6634
6635     return;
6636   }
6637
6638   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6639     return;
6640
6641   Feld[x][y] = EL_EXIT_OPENING;
6642
6643   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6644 }
6645
6646 void CheckExitSP(int x, int y)
6647 {
6648   if (local_player->gems_still_needed > 0)
6649   {
6650     int element = Feld[x][y];
6651     int graphic = el2img(element);
6652
6653     if (IS_ANIMATED(graphic))
6654       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6655
6656     return;
6657   }
6658
6659   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6660     return;
6661
6662   Feld[x][y] = EL_SP_EXIT_OPENING;
6663
6664   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6665 }
6666
6667 static void CloseAllOpenTimegates()
6668 {
6669   int x, y;
6670
6671   for (y = 0; y < lev_fieldy; y++)
6672   {
6673     for (x = 0; x < lev_fieldx; x++)
6674     {
6675       int element = Feld[x][y];
6676
6677       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6678       {
6679         Feld[x][y] = EL_TIMEGATE_CLOSING;
6680 #if 1
6681         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6682 #else
6683         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6684 #endif
6685       }
6686     }
6687   }
6688 }
6689
6690 void EdelsteinFunkeln(int x, int y)
6691 {
6692   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6693     return;
6694
6695   if (Feld[x][y] == EL_BD_DIAMOND)
6696     return;
6697
6698   if (MovDelay[x][y] == 0)      /* next animation frame */
6699     MovDelay[x][y] = 11 * !SimpleRND(500);
6700
6701   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6702   {
6703     MovDelay[x][y]--;
6704
6705     if (setup.direct_draw && MovDelay[x][y])
6706       SetDrawtoField(DRAW_BUFFERED);
6707
6708     DrawLevelElementAnimation(x, y, Feld[x][y]);
6709
6710     if (MovDelay[x][y] != 0)
6711     {
6712       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6713                                            10 - MovDelay[x][y]);
6714
6715       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6716
6717       if (setup.direct_draw)
6718       {
6719         int dest_x, dest_y;
6720
6721         dest_x = FX + SCREENX(x) * TILEX;
6722         dest_y = FY + SCREENY(y) * TILEY;
6723
6724         BlitBitmap(drawto_field, window,
6725                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6726         SetDrawtoField(DRAW_DIRECT);
6727       }
6728     }
6729   }
6730 }
6731
6732 void MauerWaechst(int x, int y)
6733 {
6734   int delay = 6;
6735
6736   if (!MovDelay[x][y])          /* next animation frame */
6737     MovDelay[x][y] = 3 * delay;
6738
6739   if (MovDelay[x][y])           /* wait some time before next frame */
6740   {
6741     MovDelay[x][y]--;
6742
6743     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6744     {
6745       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6746       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6747
6748       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6749     }
6750
6751     if (!MovDelay[x][y])
6752     {
6753       if (MovDir[x][y] == MV_LEFT)
6754       {
6755         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6756           DrawLevelField(x - 1, y);
6757       }
6758       else if (MovDir[x][y] == MV_RIGHT)
6759       {
6760         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6761           DrawLevelField(x + 1, y);
6762       }
6763       else if (MovDir[x][y] == MV_UP)
6764       {
6765         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6766           DrawLevelField(x, y - 1);
6767       }
6768       else
6769       {
6770         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6771           DrawLevelField(x, y + 1);
6772       }
6773
6774       Feld[x][y] = Store[x][y];
6775       Store[x][y] = 0;
6776       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6777       DrawLevelField(x, y);
6778     }
6779   }
6780 }
6781
6782 void MauerAbleger(int ax, int ay)
6783 {
6784   int element = Feld[ax][ay];
6785   int graphic = el2img(element);
6786   boolean oben_frei = FALSE, unten_frei = FALSE;
6787   boolean links_frei = FALSE, rechts_frei = FALSE;
6788   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6789   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6790   boolean new_wall = FALSE;
6791
6792   if (IS_ANIMATED(graphic))
6793     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6794
6795   if (!MovDelay[ax][ay])        /* start building new wall */
6796     MovDelay[ax][ay] = 6;
6797
6798   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6799   {
6800     MovDelay[ax][ay]--;
6801     if (MovDelay[ax][ay])
6802       return;
6803   }
6804
6805   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6806     oben_frei = TRUE;
6807   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6808     unten_frei = TRUE;
6809   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6810     links_frei = TRUE;
6811   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6812     rechts_frei = TRUE;
6813
6814   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6815       element == EL_EXPANDABLE_WALL_ANY)
6816   {
6817     if (oben_frei)
6818     {
6819       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6820       Store[ax][ay-1] = element;
6821       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6822       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6823         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6824                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6825       new_wall = TRUE;
6826     }
6827     if (unten_frei)
6828     {
6829       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6830       Store[ax][ay+1] = element;
6831       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6832       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6833         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6834                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6835       new_wall = TRUE;
6836     }
6837   }
6838
6839   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6840       element == EL_EXPANDABLE_WALL_ANY ||
6841       element == EL_EXPANDABLE_WALL)
6842   {
6843     if (links_frei)
6844     {
6845       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6846       Store[ax-1][ay] = element;
6847       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6848       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6849         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6850                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6851       new_wall = TRUE;
6852     }
6853
6854     if (rechts_frei)
6855     {
6856       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6857       Store[ax+1][ay] = element;
6858       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6859       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6860         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6861                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6862       new_wall = TRUE;
6863     }
6864   }
6865
6866   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6867     DrawLevelField(ax, ay);
6868
6869   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6870     oben_massiv = TRUE;
6871   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6872     unten_massiv = TRUE;
6873   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6874     links_massiv = TRUE;
6875   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6876     rechts_massiv = TRUE;
6877
6878   if (((oben_massiv && unten_massiv) ||
6879        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6880        element == EL_EXPANDABLE_WALL) &&
6881       ((links_massiv && rechts_massiv) ||
6882        element == EL_EXPANDABLE_WALL_VERTICAL))
6883     Feld[ax][ay] = EL_WALL;
6884
6885   if (new_wall)
6886 #if 1
6887     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6888 #else
6889     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6890 #endif
6891 }
6892
6893 void CheckForDragon(int x, int y)
6894 {
6895   int i, j;
6896   boolean dragon_found = FALSE;
6897   static int xy[4][2] =
6898   {
6899     { 0, -1 },
6900     { -1, 0 },
6901     { +1, 0 },
6902     { 0, +1 }
6903   };
6904
6905   for (i = 0; i < NUM_DIRECTIONS; i++)
6906   {
6907     for (j = 0; j < 4; j++)
6908     {
6909       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6910
6911       if (IN_LEV_FIELD(xx, yy) &&
6912           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6913       {
6914         if (Feld[xx][yy] == EL_DRAGON)
6915           dragon_found = TRUE;
6916       }
6917       else
6918         break;
6919     }
6920   }
6921
6922   if (!dragon_found)
6923   {
6924     for (i = 0; i < NUM_DIRECTIONS; i++)
6925     {
6926       for (j = 0; j < 3; j++)
6927       {
6928         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6929   
6930         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6931         {
6932           Feld[xx][yy] = EL_EMPTY;
6933           DrawLevelField(xx, yy);
6934         }
6935         else
6936           break;
6937       }
6938     }
6939   }
6940 }
6941
6942 static void InitBuggyBase(int x, int y)
6943 {
6944   int element = Feld[x][y];
6945   int activating_delay = FRAMES_PER_SECOND / 4;
6946
6947   ChangeDelay[x][y] =
6948     (element == EL_SP_BUGGY_BASE ?
6949      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6950      element == EL_SP_BUGGY_BASE_ACTIVATING ?
6951      activating_delay :
6952      element == EL_SP_BUGGY_BASE_ACTIVE ?
6953      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6954 }
6955
6956 static void WarnBuggyBase(int x, int y)
6957 {
6958   int i;
6959   static int xy[4][2] =
6960   {
6961     { 0, -1 },
6962     { -1, 0 },
6963     { +1, 0 },
6964     { 0, +1 }
6965   };
6966
6967   for (i = 0; i < NUM_DIRECTIONS; i++)
6968   {
6969     int xx = x + xy[i][0], yy = y + xy[i][1];
6970
6971     if (IS_PLAYER(xx, yy))
6972     {
6973       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6974
6975       break;
6976     }
6977   }
6978 }
6979
6980 static void InitTrap(int x, int y)
6981 {
6982   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6983 }
6984
6985 static void ActivateTrap(int x, int y)
6986 {
6987   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6988 }
6989
6990 static void ChangeActiveTrap(int x, int y)
6991 {
6992   int graphic = IMG_TRAP_ACTIVE;
6993
6994   /* if new animation frame was drawn, correct crumbled sand border */
6995   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6996     DrawLevelFieldCrumbledSand(x, y);
6997 }
6998
6999 static void ChangeElementNowExt(int x, int y, int target_element)
7000 {
7001   int previous_move_direction = MovDir[x][y];
7002
7003   /* check if element under player changes from accessible to unaccessible
7004      (needed for special case of dropping element which then changes) */
7005   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7006       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7007   {
7008     Bang(x, y);
7009     return;
7010   }
7011
7012   RemoveField(x, y);
7013   Feld[x][y] = target_element;
7014
7015   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7016
7017   ResetGfxAnimation(x, y);
7018   ResetRandomAnimationValue(x, y);
7019
7020   if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7021     MovDir[x][y] = previous_move_direction;
7022
7023 #if 1
7024   InitField_WithBug1(x, y, FALSE);
7025 #else
7026   InitField(x, y, FALSE);
7027   if (CAN_MOVE(Feld[x][y]))
7028     InitMovDir(x, y);
7029 #endif
7030
7031   DrawLevelField(x, y);
7032
7033   if (GFX_CRUMBLED(Feld[x][y]))
7034     DrawLevelFieldCrumbledSandNeighbours(x, y);
7035
7036   TestIfBadThingTouchesHero(x, y);
7037   TestIfPlayerTouchesCustomElement(x, y);
7038   TestIfElementTouchesCustomElement(x, y);
7039
7040   if (ELEM_IS_PLAYER(target_element))
7041     RelocatePlayer(x, y, target_element);
7042 }
7043
7044 static boolean ChangeElementNow(int x, int y, int element, int page)
7045 {
7046   struct ElementChangeInfo *change = &element_info[element].change_page[page];
7047   int target_element;
7048
7049   /* always use default change event to prevent running into a loop */
7050   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7051     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7052
7053   if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7054   {
7055     /* reset actual trigger element and player */
7056     change->actual_trigger_element = EL_EMPTY;
7057     change->actual_trigger_player = EL_PLAYER_1;
7058   }
7059
7060   /* do not change already changed elements with same change event */
7061 #if 0
7062   if (Changed[x][y] & ChangeEvent[x][y])
7063     return FALSE;
7064 #else
7065   if (Changed[x][y])
7066     return FALSE;
7067 #endif
7068
7069   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7070
7071   CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
7072
7073   if (change->explode)
7074   {
7075     Bang(x, y);
7076
7077     return TRUE;
7078   }
7079
7080   if (change->use_target_content)
7081   {
7082     boolean complete_replace = TRUE;
7083     boolean can_replace[3][3];
7084     int xx, yy;
7085
7086     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7087     {
7088       boolean is_empty;
7089       boolean is_diggable;
7090       boolean is_destructible;
7091       int ex = x + xx - 1;
7092       int ey = y + yy - 1;
7093       int content_element = change->target_content[xx][yy];
7094       int e;
7095
7096       can_replace[xx][yy] = TRUE;
7097
7098       if (ex == x && ey == y)   /* do not check changing element itself */
7099         continue;
7100
7101       if (content_element == EL_EMPTY_SPACE)
7102       {
7103         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
7104
7105         continue;
7106       }
7107
7108       if (!IN_LEV_FIELD(ex, ey))
7109       {
7110         can_replace[xx][yy] = FALSE;
7111         complete_replace = FALSE;
7112
7113         continue;
7114       }
7115
7116       e = Feld[ex][ey];
7117
7118       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7119         e = MovingOrBlocked2Element(ex, ey);
7120
7121 #if 1
7122       is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
7123                                       IS_WALKABLE(content_element)));
7124       is_diggable = (is_empty || IS_DIGGABLE(e));
7125       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7126
7127       can_replace[xx][yy] =
7128         ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7129          (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7130          (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7131
7132       if (!can_replace[xx][yy])
7133         complete_replace = FALSE;
7134 #else
7135       empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7136                                                IS_WALKABLE(content_element)));
7137 #if 1
7138       half_destructible = (empty_for_element || IS_DIGGABLE(e));
7139 #else
7140       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7141 #endif
7142
7143       if ((change->replace_when <= CP_WHEN_EMPTY  && !empty_for_element) ||
7144           (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7145           (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7146       {
7147         can_replace[xx][yy] = FALSE;
7148         complete_replace = FALSE;
7149       }
7150 #endif
7151     }
7152
7153     if (!change->only_if_complete || complete_replace)
7154     {
7155       boolean something_has_changed = FALSE;
7156
7157       if (change->only_if_complete && change->use_random_replace &&
7158           RND(100) < change->random_percentage)
7159         return FALSE;
7160
7161       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7162       {
7163         int ex = x + xx - 1;
7164         int ey = y + yy - 1;
7165         int content_element;
7166
7167         if (can_replace[xx][yy] && (!change->use_random_replace ||
7168                                     RND(100) < change->random_percentage))
7169         {
7170           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7171             RemoveMovingField(ex, ey);
7172
7173           ChangeEvent[ex][ey] = ChangeEvent[x][y];
7174
7175           content_element = change->target_content[xx][yy];
7176           target_element = GET_TARGET_ELEMENT(content_element, change);
7177
7178           ChangeElementNowExt(ex, ey, target_element);
7179
7180           something_has_changed = TRUE;
7181
7182           /* for symmetry reasons, freeze newly created border elements */
7183           if (ex != x || ey != y)
7184             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
7185         }
7186       }
7187
7188       if (something_has_changed)
7189         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7190     }
7191   }
7192   else
7193   {
7194     target_element = GET_TARGET_ELEMENT(change->target_element, change);
7195
7196     ChangeElementNowExt(x, y, target_element);
7197
7198     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7199   }
7200
7201   return TRUE;
7202 }
7203
7204 static void ChangeElement(int x, int y, int page)
7205 {
7206   int element = MovingOrBlocked2Element(x, y);
7207   struct ElementInfo *ei = &element_info[element];
7208   struct ElementChangeInfo *change = &ei->change_page[page];
7209
7210 #ifdef DEBUG
7211   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7212   {
7213     printf("\n\n");
7214     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7215            x, y, element, element_info[element].token_name);
7216     printf("ChangeElement(): This should never happen!\n");
7217     printf("\n\n");
7218   }
7219 #endif
7220
7221   /* this can happen with classic bombs on walkable, changing elements */
7222   if (!CAN_CHANGE(element))
7223   {
7224 #if 0
7225     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7226       ChangeDelay[x][y] = 0;
7227 #endif
7228
7229     return;
7230   }
7231
7232   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7233   {
7234     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
7235                          RND(change->delay_random * change->delay_frames)) + 1;
7236
7237     ResetGfxAnimation(x, y);
7238     ResetRandomAnimationValue(x, y);
7239
7240     if (change->pre_change_function)
7241       change->pre_change_function(x, y);
7242   }
7243
7244   ChangeDelay[x][y]--;
7245
7246   if (ChangeDelay[x][y] != 0)           /* continue element change */
7247   {
7248     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7249
7250     if (IS_ANIMATED(graphic))
7251       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7252
7253     if (change->change_function)
7254       change->change_function(x, y);
7255   }
7256   else                                  /* finish element change */
7257   {
7258     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7259     {
7260       page = ChangePage[x][y];
7261       ChangePage[x][y] = -1;
7262
7263       change = &ei->change_page[page];
7264     }
7265
7266 #if 0
7267     if (IS_MOVING(x, y) && !change->explode)
7268 #else
7269     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7270 #endif
7271     {
7272       ChangeDelay[x][y] = 1;            /* try change after next move step */
7273       ChangePage[x][y] = page;          /* remember page to use for change */
7274
7275       return;
7276     }
7277
7278     if (ChangeElementNow(x, y, element, page))
7279     {
7280       if (change->post_change_function)
7281         change->post_change_function(x, y);
7282     }
7283   }
7284 }
7285
7286 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7287                                               int trigger_element,
7288                                               int trigger_event,
7289                                               int trigger_player,
7290                                               int trigger_side,
7291                                               int trigger_page)
7292 {
7293   int i, j, x, y;
7294   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7295
7296   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7297     return FALSE;
7298
7299   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7300   {
7301     int element = EL_CUSTOM_START + i;
7302
7303     boolean change_element = FALSE;
7304     int page = 0;
7305
7306     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7307       continue;
7308
7309     for (j = 0; j < element_info[element].num_change_pages; j++)
7310     {
7311       struct ElementChangeInfo *change = &element_info[element].change_page[j];
7312
7313       if (change->can_change &&
7314           change->events & CH_EVENT_BIT(trigger_event) &&
7315           change->trigger_side & trigger_side &&
7316           change->trigger_player & trigger_player &&
7317           change->trigger_page & trigger_page_bits &&
7318           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7319       {
7320 #if 0
7321         if (!(change->events & CH_EVENT_BIT(trigger_event)))
7322           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7323                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7324 #endif
7325
7326         change_element = TRUE;
7327         page = j;
7328
7329         change->actual_trigger_element = trigger_element;
7330         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7331
7332         break;
7333       }
7334     }
7335
7336     if (!change_element)
7337       continue;
7338
7339     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7340     {
7341 #if 0
7342       if (x == lx && y == ly)   /* do not change trigger element itself */
7343         continue;
7344 #endif
7345
7346       if (Feld[x][y] == element)
7347       {
7348         ChangeDelay[x][y] = 1;
7349         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7350         ChangeElement(x, y, page);
7351       }
7352     }
7353   }
7354
7355   return TRUE;
7356 }
7357
7358 static boolean CheckElementChangeExt(int x, int y,
7359                                      int element,
7360                                      int trigger_element,
7361                                      int trigger_event,
7362                                      int trigger_player,
7363                                      int trigger_side,
7364                                      int trigger_page)
7365 {
7366   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7367     return FALSE;
7368
7369   if (Feld[x][y] == EL_BLOCKED)
7370   {
7371     Blocked2Moving(x, y, &x, &y);
7372     element = Feld[x][y];
7373   }
7374
7375 #if 1
7376   if (Feld[x][y] != element)    /* check if element has already changed */
7377   {
7378 #if 0
7379     printf("::: %d ('%s') != %d ('%s') [%d]\n",
7380            Feld[x][y], element_info[Feld[x][y]].token_name,
7381            element, element_info[element].token_name,
7382            trigger_event);
7383 #endif
7384
7385     return FALSE;
7386   }
7387 #endif
7388
7389 #if 1
7390   if (trigger_page < 0)
7391   {
7392     boolean change_element = FALSE;
7393     int i;
7394
7395     for (i = 0; i < element_info[element].num_change_pages; i++)
7396     {
7397       struct ElementChangeInfo *change = &element_info[element].change_page[i];
7398
7399       if (change->can_change &&
7400           change->events & CH_EVENT_BIT(trigger_event) &&
7401           change->trigger_side & trigger_side &&
7402           change->trigger_player & trigger_player)
7403       {
7404         change_element = TRUE;
7405         trigger_page = i;
7406
7407         change->actual_trigger_element = trigger_element;
7408         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7409
7410         break;
7411       }
7412     }
7413
7414     if (!change_element)
7415       return FALSE;
7416   }
7417   else
7418   {
7419     struct ElementInfo *ei = &element_info[element];
7420     struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7421
7422     change->actual_trigger_element = trigger_element;
7423     change->actual_trigger_player = EL_PLAYER_1;        /* unused */
7424   }
7425
7426 #else
7427
7428   /* !!! this check misses pages with same event, but different side !!! */
7429
7430   if (trigger_page < 0)
7431     trigger_page = element_info[element].event_page_nr[trigger_event];
7432
7433   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7434     return FALSE;
7435 #endif
7436
7437   ChangeDelay[x][y] = 1;
7438   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7439   ChangeElement(x, y, trigger_page);
7440
7441   return TRUE;
7442 }
7443
7444 static void PlayPlayerSound(struct PlayerInfo *player)
7445 {
7446   int jx = player->jx, jy = player->jy;
7447   int element = player->element_nr;
7448   int last_action = player->last_action_waiting;
7449   int action = player->action_waiting;
7450
7451   if (player->is_waiting)
7452   {
7453     if (action != last_action)
7454       PlayLevelSoundElementAction(jx, jy, element, action);
7455     else
7456       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7457   }
7458   else
7459   {
7460     if (action != last_action)
7461       StopSound(element_info[element].sound[last_action]);
7462
7463     if (last_action == ACTION_SLEEPING)
7464       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7465   }
7466 }
7467
7468 static void PlayAllPlayersSound()
7469 {
7470   int i;
7471
7472   for (i = 0; i < MAX_PLAYERS; i++)
7473     if (stored_player[i].active)
7474       PlayPlayerSound(&stored_player[i]);
7475 }
7476
7477 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7478 {
7479   boolean last_waiting = player->is_waiting;
7480   int move_dir = player->MovDir;
7481
7482   player->last_action_waiting = player->action_waiting;
7483
7484   if (is_waiting)
7485   {
7486     if (!last_waiting)          /* not waiting -> waiting */
7487     {
7488       player->is_waiting = TRUE;
7489
7490       player->frame_counter_bored =
7491         FrameCounter +
7492         game.player_boring_delay_fixed +
7493         SimpleRND(game.player_boring_delay_random);
7494       player->frame_counter_sleeping =
7495         FrameCounter +
7496         game.player_sleeping_delay_fixed +
7497         SimpleRND(game.player_sleeping_delay_random);
7498
7499       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7500     }
7501
7502     if (game.player_sleeping_delay_fixed +
7503         game.player_sleeping_delay_random > 0 &&
7504         player->anim_delay_counter == 0 &&
7505         player->post_delay_counter == 0 &&
7506         FrameCounter >= player->frame_counter_sleeping)
7507       player->is_sleeping = TRUE;
7508     else if (game.player_boring_delay_fixed +
7509              game.player_boring_delay_random > 0 &&
7510              FrameCounter >= player->frame_counter_bored)
7511       player->is_bored = TRUE;
7512
7513     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7514                               player->is_bored ? ACTION_BORING :
7515                               ACTION_WAITING);
7516
7517     if (player->is_sleeping)
7518     {
7519       if (player->num_special_action_sleeping > 0)
7520       {
7521         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7522         {
7523           int last_special_action = player->special_action_sleeping;
7524           int num_special_action = player->num_special_action_sleeping;
7525           int special_action =
7526             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7527              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7528              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7529              last_special_action + 1 : ACTION_SLEEPING);
7530           int special_graphic =
7531             el_act_dir2img(player->element_nr, special_action, move_dir);
7532
7533           player->anim_delay_counter =
7534             graphic_info[special_graphic].anim_delay_fixed +
7535             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7536           player->post_delay_counter =
7537             graphic_info[special_graphic].post_delay_fixed +
7538             SimpleRND(graphic_info[special_graphic].post_delay_random);
7539
7540           player->special_action_sleeping = special_action;
7541         }
7542
7543         if (player->anim_delay_counter > 0)
7544         {
7545           player->action_waiting = player->special_action_sleeping;
7546           player->anim_delay_counter--;
7547         }
7548         else if (player->post_delay_counter > 0)
7549         {
7550           player->post_delay_counter--;
7551         }
7552       }
7553     }
7554     else if (player->is_bored)
7555     {
7556       if (player->num_special_action_bored > 0)
7557       {
7558         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7559         {
7560           int special_action =
7561             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7562           int special_graphic =
7563             el_act_dir2img(player->element_nr, special_action, move_dir);
7564
7565           player->anim_delay_counter =
7566             graphic_info[special_graphic].anim_delay_fixed +
7567             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7568           player->post_delay_counter =
7569             graphic_info[special_graphic].post_delay_fixed +
7570             SimpleRND(graphic_info[special_graphic].post_delay_random);
7571
7572           player->special_action_bored = special_action;
7573         }
7574
7575         if (player->anim_delay_counter > 0)
7576         {
7577           player->action_waiting = player->special_action_bored;
7578           player->anim_delay_counter--;
7579         }
7580         else if (player->post_delay_counter > 0)
7581         {
7582           player->post_delay_counter--;
7583         }
7584       }
7585     }
7586   }
7587   else if (last_waiting)        /* waiting -> not waiting */
7588   {
7589     player->is_waiting = FALSE;
7590     player->is_bored = FALSE;
7591     player->is_sleeping = FALSE;
7592
7593     player->frame_counter_bored = -1;
7594     player->frame_counter_sleeping = -1;
7595
7596     player->anim_delay_counter = 0;
7597     player->post_delay_counter = 0;
7598
7599     player->action_waiting = ACTION_DEFAULT;
7600
7601     player->special_action_bored = ACTION_DEFAULT;
7602     player->special_action_sleeping = ACTION_DEFAULT;
7603   }
7604 }
7605
7606 #if 1
7607 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7608 {
7609 #if 0
7610   static byte stored_player_action[MAX_PLAYERS];
7611   static int num_stored_actions = 0;
7612 #endif
7613   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7614   int left      = player_action & JOY_LEFT;
7615   int right     = player_action & JOY_RIGHT;
7616   int up        = player_action & JOY_UP;
7617   int down      = player_action & JOY_DOWN;
7618   int button1   = player_action & JOY_BUTTON_1;
7619   int button2   = player_action & JOY_BUTTON_2;
7620   int dx        = (left ? -1    : right ? 1     : 0);
7621   int dy        = (up   ? -1    : down  ? 1     : 0);
7622
7623 #if 0
7624   stored_player_action[player->index_nr] = 0;
7625   num_stored_actions++;
7626 #endif
7627
7628 #if 0
7629   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7630 #endif
7631
7632   if (!player->active || tape.pausing)
7633     return 0;
7634
7635 #if 0
7636   printf("::: [%d %d %d %d] [%d %d]\n",
7637          left, right, up, down, button1, button2);
7638 #endif
7639
7640   if (player_action)
7641   {
7642 #if 0
7643     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7644 #endif
7645
7646 #if 0
7647     /* !!! TEST !!! */
7648     if (player->MovPos == 0)
7649       CheckGravityMovement(player);
7650 #endif
7651     if (button1)
7652       snapped = SnapField(player, dx, dy);
7653     else
7654     {
7655       if (button2)
7656         dropped = DropElement(player);
7657
7658       moved = MovePlayer(player, dx, dy);
7659     }
7660
7661     if (tape.single_step && tape.recording && !tape.pausing)
7662     {
7663       if (button1 || (dropped && !moved))
7664       {
7665         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7666         SnapField(player, 0, 0);                /* stop snapping */
7667       }
7668     }
7669
7670     SetPlayerWaiting(player, FALSE);
7671
7672 #if 1
7673     return player_action;
7674 #else
7675     stored_player_action[player->index_nr] = player_action;
7676 #endif
7677   }
7678   else
7679   {
7680 #if 0
7681     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7682 #endif
7683
7684     /* no actions for this player (no input at player's configured device) */
7685
7686     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7687     SnapField(player, 0, 0);
7688     CheckGravityMovementWhenNotMoving(player);
7689
7690     if (player->MovPos == 0)
7691       SetPlayerWaiting(player, TRUE);
7692
7693     if (player->MovPos == 0)    /* needed for tape.playing */
7694       player->is_moving = FALSE;
7695
7696     player->is_dropping = FALSE;
7697
7698     return 0;
7699   }
7700
7701 #if 0
7702   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7703   {
7704     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7705
7706     TapeRecordAction(stored_player_action);
7707     num_stored_actions = 0;
7708   }
7709 #endif
7710 }
7711
7712 #else
7713
7714 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7715 {
7716   static byte stored_player_action[MAX_PLAYERS];
7717   static int num_stored_actions = 0;
7718   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7719   int left      = player_action & JOY_LEFT;
7720   int right     = player_action & JOY_RIGHT;
7721   int up        = player_action & JOY_UP;
7722   int down      = player_action & JOY_DOWN;
7723   int button1   = player_action & JOY_BUTTON_1;
7724   int button2   = player_action & JOY_BUTTON_2;
7725   int dx        = (left ? -1    : right ? 1     : 0);
7726   int dy        = (up   ? -1    : down  ? 1     : 0);
7727
7728   stored_player_action[player->index_nr] = 0;
7729   num_stored_actions++;
7730
7731   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7732
7733   if (!player->active || tape.pausing)
7734     return;
7735
7736   if (player_action)
7737   {
7738     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7739
7740     if (button1)
7741       snapped = SnapField(player, dx, dy);
7742     else
7743     {
7744       if (button2)
7745         dropped = DropElement(player);
7746
7747       moved = MovePlayer(player, dx, dy);
7748     }
7749
7750     if (tape.single_step && tape.recording && !tape.pausing)
7751     {
7752       if (button1 || (dropped && !moved))
7753       {
7754         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7755         SnapField(player, 0, 0);                /* stop snapping */
7756       }
7757     }
7758
7759     stored_player_action[player->index_nr] = player_action;
7760   }
7761   else
7762   {
7763     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7764
7765     /* no actions for this player (no input at player's configured device) */
7766
7767     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7768     SnapField(player, 0, 0);
7769     CheckGravityMovementWhenNotMoving(player);
7770
7771     if (player->MovPos == 0)
7772       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7773
7774     if (player->MovPos == 0)    /* needed for tape.playing */
7775       player->is_moving = FALSE;
7776   }
7777
7778   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7779   {
7780     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7781
7782     TapeRecordAction(stored_player_action);
7783     num_stored_actions = 0;
7784   }
7785 }
7786 #endif
7787
7788 void GameActions()
7789 {
7790   static unsigned long action_delay = 0;
7791   unsigned long action_delay_value;
7792   int magic_wall_x = 0, magic_wall_y = 0;
7793   int i, x, y, element, graphic;
7794   byte *recorded_player_action;
7795   byte summarized_player_action = 0;
7796 #if 1
7797   byte tape_action[MAX_PLAYERS];
7798 #endif
7799
7800   if (game_status != GAME_MODE_PLAYING)
7801     return;
7802
7803   action_delay_value =
7804     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7805
7806   if (tape.playing && tape.warp_forward && !tape.pausing)
7807     action_delay_value = 0;
7808
7809   /* ---------- main game synchronization point ---------- */
7810
7811   WaitUntilDelayReached(&action_delay, action_delay_value);
7812
7813   if (network_playing && !network_player_action_received)
7814   {
7815     /*
7816 #ifdef DEBUG
7817     printf("DEBUG: try to get network player actions in time\n");
7818 #endif
7819     */
7820
7821 #if defined(PLATFORM_UNIX)
7822     /* last chance to get network player actions without main loop delay */
7823     HandleNetworking();
7824 #endif
7825
7826     if (game_status != GAME_MODE_PLAYING)
7827       return;
7828
7829     if (!network_player_action_received)
7830     {
7831       /*
7832 #ifdef DEBUG
7833       printf("DEBUG: failed to get network player actions in time\n");
7834 #endif
7835       */
7836       return;
7837     }
7838   }
7839
7840   if (tape.pausing)
7841     return;
7842
7843 #if 0
7844   printf("::: getting new tape action [%d]\n", FrameCounter);
7845 #endif
7846
7847   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7848
7849 #if 1
7850   if (recorded_player_action == NULL && tape.pausing)
7851     return;
7852 #endif
7853
7854 #if 0
7855   printf("::: %d\n", stored_player[0].action);
7856 #endif
7857
7858 #if 0
7859   if (recorded_player_action != NULL)
7860     for (i = 0; i < MAX_PLAYERS; i++)
7861       stored_player[i].action = recorded_player_action[i];
7862 #endif
7863
7864   for (i = 0; i < MAX_PLAYERS; i++)
7865   {
7866     summarized_player_action |= stored_player[i].action;
7867
7868     if (!network_playing)
7869       stored_player[i].effective_action = stored_player[i].action;
7870   }
7871
7872 #if defined(PLATFORM_UNIX)
7873   if (network_playing)
7874     SendToServer_MovePlayer(summarized_player_action);
7875 #endif
7876
7877   if (!options.network && !setup.team_mode)
7878     local_player->effective_action = summarized_player_action;
7879
7880 #if 1
7881   if (recorded_player_action != NULL)
7882     for (i = 0; i < MAX_PLAYERS; i++)
7883       stored_player[i].effective_action = recorded_player_action[i];
7884 #endif
7885
7886 #if 1
7887   for (i = 0; i < MAX_PLAYERS; i++)
7888   {
7889     tape_action[i] = stored_player[i].effective_action;
7890
7891     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7892       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7893   }
7894
7895   /* only save actions from input devices, but not programmed actions */
7896   if (tape.recording)
7897     TapeRecordAction(tape_action);
7898 #endif
7899
7900   for (i = 0; i < MAX_PLAYERS; i++)
7901   {
7902     int actual_player_action = stored_player[i].effective_action;
7903
7904 #if 1
7905     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7906        - rnd_equinox_tetrachloride 048
7907        - rnd_equinox_tetrachloride_ii 096
7908        - rnd_emanuel_schmieg 002
7909        - doctor_sloan_ww 001, 020
7910     */
7911     if (stored_player[i].MovPos == 0)
7912       CheckGravityMovement(&stored_player[i]);
7913 #endif
7914
7915 #if 1
7916     /* overwrite programmed action with tape action */
7917     if (stored_player[i].programmed_action)
7918       actual_player_action = stored_player[i].programmed_action;
7919 #endif
7920
7921 #if 0
7922     if (stored_player[i].programmed_action)
7923       printf("::: %d\n", stored_player[i].programmed_action);
7924 #endif
7925
7926     if (recorded_player_action)
7927     {
7928 #if 0
7929       if (stored_player[i].programmed_action &&
7930           stored_player[i].programmed_action != recorded_player_action[i])
7931         printf("::: %d: %d <-> %d\n", i,
7932                stored_player[i].programmed_action, recorded_player_action[i]);
7933 #endif
7934
7935 #if 0
7936       actual_player_action = recorded_player_action[i];
7937 #endif
7938     }
7939
7940 #if 0
7941     /* overwrite tape action with programmed action */
7942     if (stored_player[i].programmed_action)
7943       actual_player_action = stored_player[i].programmed_action;
7944 #endif
7945
7946 #if 0
7947     if (i == 0)
7948       printf("::: action: %d: %x [%d]\n",
7949              stored_player[i].MovPos, actual_player_action, FrameCounter);
7950 #endif
7951
7952 #if 1
7953     PlayerActions(&stored_player[i], actual_player_action);
7954 #else
7955     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7956
7957     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7958       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7959 #endif
7960
7961     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7962   }
7963
7964 #if 0
7965   if (tape.recording)
7966     TapeRecordAction(tape_action);
7967 #endif
7968
7969   network_player_action_received = FALSE;
7970
7971   ScrollScreen(NULL, SCROLL_GO_ON);
7972
7973 #if 0
7974   FrameCounter++;
7975   TimeFrames++;
7976
7977   for (i = 0; i < MAX_PLAYERS; i++)
7978     stored_player[i].Frame++;
7979 #endif
7980
7981 #if 1
7982   /* for downwards compatibility, the following code emulates a fixed bug that
7983      occured when pushing elements (causing elements that just made their last
7984      pushing step to already (if possible) make their first falling step in the
7985      same game frame, which is bad); this code is also needed to use the famous
7986      "spring push bug" which is used in older levels and might be wanted to be
7987      used also in newer levels, but in this case the buggy pushing code is only
7988      affecting the "spring" element and no other elements */
7989
7990 #if 1
7991   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7992 #else
7993   if (game.engine_version < VERSION_IDENT(2,2,0,7))
7994 #endif
7995   {
7996     for (i = 0; i < MAX_PLAYERS; i++)
7997     {
7998       struct PlayerInfo *player = &stored_player[i];
7999       int x = player->jx;
8000       int y = player->jy;
8001
8002 #if 1
8003       if (player->active && player->is_pushing && player->is_moving &&
8004           IS_MOVING(x, y) &&
8005           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8006            Feld[x][y] == EL_SPRING))
8007 #else
8008       if (player->active && player->is_pushing && player->is_moving &&
8009           IS_MOVING(x, y))
8010 #endif
8011       {
8012         ContinueMoving(x, y);
8013
8014         /* continue moving after pushing (this is actually a bug) */
8015         if (!IS_MOVING(x, y))
8016         {
8017           Stop[x][y] = FALSE;
8018         }
8019       }
8020     }
8021   }
8022 #endif
8023
8024   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8025   {
8026     Changed[x][y] = CE_BITMASK_DEFAULT;
8027     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8028
8029 #if DEBUG
8030     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8031     {
8032       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8033       printf("GameActions(): This should never happen!\n");
8034
8035       ChangePage[x][y] = -1;
8036     }
8037 #endif
8038
8039     Stop[x][y] = FALSE;
8040     if (WasJustMoving[x][y] > 0)
8041       WasJustMoving[x][y]--;
8042     if (WasJustFalling[x][y] > 0)
8043       WasJustFalling[x][y]--;
8044
8045     GfxFrame[x][y]++;
8046
8047 #if 1
8048     /* reset finished pushing action (not done in ContinueMoving() to allow
8049        continous pushing animation for elements with zero push delay) */
8050     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8051     {
8052       ResetGfxAnimation(x, y);
8053       DrawLevelField(x, y);
8054     }
8055 #endif
8056
8057 #if DEBUG
8058     if (IS_BLOCKED(x, y))
8059     {
8060       int oldx, oldy;
8061
8062       Blocked2Moving(x, y, &oldx, &oldy);
8063       if (!IS_MOVING(oldx, oldy))
8064       {
8065         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8066         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8067         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8068         printf("GameActions(): This should never happen!\n");
8069       }
8070     }
8071 #endif
8072   }
8073
8074   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8075   {
8076     element = Feld[x][y];
8077 #if 1
8078     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8079 #else
8080     graphic = el2img(element);
8081 #endif
8082
8083 #if 0
8084     if (element == -1)
8085     {
8086       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8087
8088       element = graphic = 0;
8089     }
8090 #endif
8091
8092     if (graphic_info[graphic].anim_global_sync)
8093       GfxFrame[x][y] = FrameCounter;
8094
8095     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8096         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8097       ResetRandomAnimationValue(x, y);
8098
8099     SetRandomAnimationValue(x, y);
8100
8101 #if 1
8102     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8103 #endif
8104
8105     if (IS_INACTIVE(element))
8106     {
8107       if (IS_ANIMATED(graphic))
8108         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8109
8110       continue;
8111     }
8112
8113 #if 1
8114     /* this may take place after moving, so 'element' may have changed */
8115 #if 0
8116     if (IS_CHANGING(x, y))
8117 #else
8118     if (IS_CHANGING(x, y) &&
8119         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8120 #endif
8121     {
8122 #if 0
8123       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8124                     element_info[element].event_page_nr[CE_DELAY]);
8125 #else
8126       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8127 #endif
8128
8129       element = Feld[x][y];
8130       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8131     }
8132 #endif
8133
8134     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8135     {
8136       StartMoving(x, y);
8137
8138 #if 1
8139       element = Feld[x][y];
8140       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8141 #if 0
8142       if (element == EL_MOLE)
8143         printf("::: %d, %d, %d [%d]\n",
8144                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8145                GfxAction[x][y]);
8146 #endif
8147 #if 0
8148       if (element == EL_YAMYAM)
8149         printf("::: %d, %d, %d\n",
8150                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8151 #endif
8152 #endif
8153
8154       if (IS_ANIMATED(graphic) &&
8155           !IS_MOVING(x, y) &&
8156           !Stop[x][y])
8157       {
8158         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8159
8160 #if 0
8161         if (element == EL_BUG)
8162           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8163 #endif
8164
8165 #if 0
8166         if (element == EL_MOLE)
8167           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8168 #endif
8169       }
8170
8171       if (IS_GEM(element) || element == EL_SP_INFOTRON)
8172         EdelsteinFunkeln(x, y);
8173     }
8174     else if ((element == EL_ACID ||
8175               element == EL_EXIT_OPEN ||
8176               element == EL_SP_EXIT_OPEN ||
8177               element == EL_SP_TERMINAL ||
8178               element == EL_SP_TERMINAL_ACTIVE ||
8179               element == EL_EXTRA_TIME ||
8180               element == EL_SHIELD_NORMAL ||
8181               element == EL_SHIELD_DEADLY) &&
8182              IS_ANIMATED(graphic))
8183       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8184     else if (IS_MOVING(x, y))
8185       ContinueMoving(x, y);
8186     else if (IS_ACTIVE_BOMB(element))
8187       CheckDynamite(x, y);
8188 #if 0
8189     else if (element == EL_EXPLOSION && !game.explosions_delayed)
8190       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8191 #endif
8192     else if (element == EL_AMOEBA_GROWING)
8193       AmoebeWaechst(x, y);
8194     else if (element == EL_AMOEBA_SHRINKING)
8195       AmoebaDisappearing(x, y);
8196
8197 #if !USE_NEW_AMOEBA_CODE
8198     else if (IS_AMOEBALIVE(element))
8199       AmoebeAbleger(x, y);
8200 #endif
8201
8202     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8203       Life(x, y);
8204     else if (element == EL_EXIT_CLOSED)
8205       CheckExit(x, y);
8206     else if (element == EL_SP_EXIT_CLOSED)
8207       CheckExitSP(x, y);
8208     else if (element == EL_EXPANDABLE_WALL_GROWING)
8209       MauerWaechst(x, y);
8210     else if (element == EL_EXPANDABLE_WALL ||
8211              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8212              element == EL_EXPANDABLE_WALL_VERTICAL ||
8213              element == EL_EXPANDABLE_WALL_ANY)
8214       MauerAbleger(x, y);
8215     else if (element == EL_FLAMES)
8216       CheckForDragon(x, y);
8217 #if 0
8218     else if (IS_AUTO_CHANGING(element))
8219       ChangeElement(x, y);
8220 #endif
8221     else if (element == EL_EXPLOSION)
8222       ; /* drawing of correct explosion animation is handled separately */
8223     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8224       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8225
8226 #if 0
8227     /* this may take place after moving, so 'element' may have changed */
8228     if (IS_AUTO_CHANGING(Feld[x][y]))
8229       ChangeElement(x, y);
8230 #endif
8231
8232     if (IS_BELT_ACTIVE(element))
8233       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8234
8235     if (game.magic_wall_active)
8236     {
8237       int jx = local_player->jx, jy = local_player->jy;
8238
8239       /* play the element sound at the position nearest to the player */
8240       if ((element == EL_MAGIC_WALL_FULL ||
8241            element == EL_MAGIC_WALL_ACTIVE ||
8242            element == EL_MAGIC_WALL_EMPTYING ||
8243            element == EL_BD_MAGIC_WALL_FULL ||
8244            element == EL_BD_MAGIC_WALL_ACTIVE ||
8245            element == EL_BD_MAGIC_WALL_EMPTYING) &&
8246           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8247       {
8248         magic_wall_x = x;
8249         magic_wall_y = y;
8250       }
8251     }
8252   }
8253
8254 #if USE_NEW_AMOEBA_CODE
8255   /* new experimental amoeba growth stuff */
8256 #if 1
8257   if (!(FrameCounter % 8))
8258 #endif
8259   {
8260     static unsigned long random = 1684108901;
8261
8262     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8263     {
8264 #if 0
8265       x = (random >> 10) % lev_fieldx;
8266       y = (random >> 20) % lev_fieldy;
8267 #else
8268       x = RND(lev_fieldx);
8269       y = RND(lev_fieldy);
8270 #endif
8271       element = Feld[x][y];
8272
8273       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8274       if (!IS_PLAYER(x,y) &&
8275           (element == EL_EMPTY ||
8276            element == EL_SAND ||
8277            element == EL_QUICKSAND_EMPTY ||
8278            element == EL_ACID_SPLASH_LEFT ||
8279            element == EL_ACID_SPLASH_RIGHT))
8280       {
8281         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8282             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8283             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8284             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8285           Feld[x][y] = EL_AMOEBA_DROP;
8286       }
8287
8288       random = random * 129 + 1;
8289     }
8290   }
8291 #endif
8292
8293 #if 0
8294   if (game.explosions_delayed)
8295 #endif
8296   {
8297     game.explosions_delayed = FALSE;
8298
8299     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8300     {
8301       element = Feld[x][y];
8302
8303       if (ExplodeField[x][y])
8304         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8305       else if (element == EL_EXPLOSION)
8306         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8307
8308       ExplodeField[x][y] = EX_TYPE_NONE;
8309     }
8310
8311     game.explosions_delayed = TRUE;
8312   }
8313
8314   if (game.magic_wall_active)
8315   {
8316     if (!(game.magic_wall_time_left % 4))
8317     {
8318       int element = Feld[magic_wall_x][magic_wall_y];
8319
8320       if (element == EL_BD_MAGIC_WALL_FULL ||
8321           element == EL_BD_MAGIC_WALL_ACTIVE ||
8322           element == EL_BD_MAGIC_WALL_EMPTYING)
8323         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8324       else
8325         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8326     }
8327
8328     if (game.magic_wall_time_left > 0)
8329     {
8330       game.magic_wall_time_left--;
8331       if (!game.magic_wall_time_left)
8332       {
8333         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8334         {
8335           element = Feld[x][y];
8336
8337           if (element == EL_MAGIC_WALL_ACTIVE ||
8338               element == EL_MAGIC_WALL_FULL)
8339           {
8340             Feld[x][y] = EL_MAGIC_WALL_DEAD;
8341             DrawLevelField(x, y);
8342           }
8343           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8344                    element == EL_BD_MAGIC_WALL_FULL)
8345           {
8346             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8347             DrawLevelField(x, y);
8348           }
8349         }
8350
8351         game.magic_wall_active = FALSE;
8352       }
8353     }
8354   }
8355
8356   if (game.light_time_left > 0)
8357   {
8358     game.light_time_left--;
8359
8360     if (game.light_time_left == 0)
8361       RedrawAllLightSwitchesAndInvisibleElements();
8362   }
8363
8364   if (game.timegate_time_left > 0)
8365   {
8366     game.timegate_time_left--;
8367
8368     if (game.timegate_time_left == 0)
8369       CloseAllOpenTimegates();
8370   }
8371
8372   for (i = 0; i < MAX_PLAYERS; i++)
8373   {
8374     struct PlayerInfo *player = &stored_player[i];
8375
8376     if (SHIELD_ON(player))
8377     {
8378       if (player->shield_deadly_time_left)
8379         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8380       else if (player->shield_normal_time_left)
8381         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8382     }
8383   }
8384
8385   if (TimeFrames >= FRAMES_PER_SECOND)
8386   {
8387     TimeFrames = 0;
8388     TapeTime++;
8389
8390     if (!level.use_step_counter)
8391     {
8392       TimePlayed++;
8393
8394       for (i = 0; i < MAX_PLAYERS; i++)
8395       {
8396         struct PlayerInfo *player = &stored_player[i];
8397
8398         if (SHIELD_ON(player))
8399         {
8400           player->shield_normal_time_left--;
8401
8402           if (player->shield_deadly_time_left > 0)
8403             player->shield_deadly_time_left--;
8404         }
8405       }
8406
8407       if (TimeLeft > 0)
8408       {
8409         TimeLeft--;
8410
8411         if (TimeLeft <= 10 && setup.time_limit)
8412           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8413
8414         DrawGameValue_Time(TimeLeft);
8415
8416         if (!TimeLeft && setup.time_limit)
8417           for (i = 0; i < MAX_PLAYERS; i++)
8418             KillHero(&stored_player[i]);
8419       }
8420       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8421         DrawGameValue_Time(TimePlayed);
8422     }
8423
8424     if (tape.recording || tape.playing)
8425       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8426   }
8427
8428   DrawAllPlayers();
8429   PlayAllPlayersSound();
8430
8431   if (options.debug)                    /* calculate frames per second */
8432   {
8433     static unsigned long fps_counter = 0;
8434     static int fps_frames = 0;
8435     unsigned long fps_delay_ms = Counter() - fps_counter;
8436
8437     fps_frames++;
8438
8439     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
8440     {
8441       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8442
8443       fps_frames = 0;
8444       fps_counter = Counter();
8445     }
8446
8447     redraw_mask |= REDRAW_FPS;
8448   }
8449
8450 #if 0
8451   if (stored_player[0].jx != stored_player[0].last_jx ||
8452       stored_player[0].jy != stored_player[0].last_jy)
8453     printf("::: %d, %d, %d, %d, %d\n",
8454            stored_player[0].MovDir,
8455            stored_player[0].MovPos,
8456            stored_player[0].GfxPos,
8457            stored_player[0].Frame,
8458            stored_player[0].StepFrame);
8459 #endif
8460
8461 #if 1
8462   FrameCounter++;
8463   TimeFrames++;
8464
8465   for (i = 0; i < MAX_PLAYERS; i++)
8466   {
8467     int move_frames =
8468       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
8469
8470     stored_player[i].Frame += move_frames;
8471
8472     if (stored_player[i].MovPos != 0)
8473       stored_player[i].StepFrame += move_frames;
8474
8475     if (stored_player[i].drop_delay > 0)
8476       stored_player[i].drop_delay--;
8477   }
8478 #endif
8479
8480 #if 1
8481   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8482   {
8483     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8484
8485     local_player->show_envelope = 0;
8486   }
8487 #endif
8488 }
8489
8490 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8491 {
8492   int min_x = x, min_y = y, max_x = x, max_y = y;
8493   int i;
8494
8495   for (i = 0; i < MAX_PLAYERS; i++)
8496   {
8497     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8498
8499     if (!stored_player[i].active || &stored_player[i] == player)
8500       continue;
8501
8502     min_x = MIN(min_x, jx);
8503     min_y = MIN(min_y, jy);
8504     max_x = MAX(max_x, jx);
8505     max_y = MAX(max_y, jy);
8506   }
8507
8508   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8509 }
8510
8511 static boolean AllPlayersInVisibleScreen()
8512 {
8513   int i;
8514
8515   for (i = 0; i < MAX_PLAYERS; i++)
8516   {
8517     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8518
8519     if (!stored_player[i].active)
8520       continue;
8521
8522     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8523       return FALSE;
8524   }
8525
8526   return TRUE;
8527 }
8528
8529 void ScrollLevel(int dx, int dy)
8530 {
8531   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8532   int x, y;
8533
8534   BlitBitmap(drawto_field, drawto_field,
8535              FX + TILEX * (dx == -1) - softscroll_offset,
8536              FY + TILEY * (dy == -1) - softscroll_offset,
8537              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8538              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8539              FX + TILEX * (dx == 1) - softscroll_offset,
8540              FY + TILEY * (dy == 1) - softscroll_offset);
8541
8542   if (dx)
8543   {
8544     x = (dx == 1 ? BX1 : BX2);
8545     for (y = BY1; y <= BY2; y++)
8546       DrawScreenField(x, y);
8547   }
8548
8549   if (dy)
8550   {
8551     y = (dy == 1 ? BY1 : BY2);
8552     for (x = BX1; x <= BX2; x++)
8553       DrawScreenField(x, y);
8554   }
8555
8556   redraw_mask |= REDRAW_FIELD;
8557 }
8558
8559 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8560 {
8561   int nextx = x + dx, nexty = y + dy;
8562   int element = Feld[x][y];
8563
8564   if ((dx == -1 &&
8565        element != EL_SP_PORT_LEFT &&
8566        element != EL_SP_GRAVITY_PORT_LEFT &&
8567        element != EL_SP_PORT_HORIZONTAL &&
8568        element != EL_SP_PORT_ANY) ||
8569       (dx == +1 &&
8570        element != EL_SP_PORT_RIGHT &&
8571        element != EL_SP_GRAVITY_PORT_RIGHT &&
8572        element != EL_SP_PORT_HORIZONTAL &&
8573        element != EL_SP_PORT_ANY) ||
8574       (dy == -1 &&
8575        element != EL_SP_PORT_UP &&
8576        element != EL_SP_GRAVITY_PORT_UP &&
8577        element != EL_SP_PORT_VERTICAL &&
8578        element != EL_SP_PORT_ANY) ||
8579       (dy == +1 &&
8580        element != EL_SP_PORT_DOWN &&
8581        element != EL_SP_GRAVITY_PORT_DOWN &&
8582        element != EL_SP_PORT_VERTICAL &&
8583        element != EL_SP_PORT_ANY) ||
8584       !IN_LEV_FIELD(nextx, nexty) ||
8585       !IS_FREE(nextx, nexty))
8586     return FALSE;
8587
8588   return TRUE;
8589 }
8590
8591 static void CheckGravityMovement(struct PlayerInfo *player)
8592 {
8593   if (game.gravity && !player->programmed_action)
8594   {
8595 #if 1
8596     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8597     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
8598 #else
8599     int move_dir_horizontal = player->action & MV_HORIZONTAL;
8600     int move_dir_vertical   = player->action & MV_VERTICAL;
8601 #endif
8602     int move_dir =
8603       (player->last_move_dir & MV_HORIZONTAL ?
8604        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
8605        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
8606     int jx = player->jx, jy = player->jy;
8607     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8608     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
8609     int new_jx = jx + dx, new_jy = jy + dy;
8610 #if 1
8611     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8612 #else
8613     boolean player_is_snapping = player->action & JOY_BUTTON_1;
8614 #endif
8615 #if 1
8616     boolean player_can_fall_down =
8617       (IN_LEV_FIELD(jx, jy + 1) &&
8618        (IS_FREE(jx, jy + 1) ||
8619         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
8620 #else
8621     boolean player_can_fall_down =
8622       (IN_LEV_FIELD(jx, jy + 1) &&
8623        (IS_FREE(jx, jy + 1)));
8624 #endif
8625     boolean player_is_moving_to_valid_field =
8626       (
8627 #if 1
8628        !player_is_snapping &&
8629 #endif
8630        IN_LEV_FIELD(new_jx, new_jy) &&
8631        (Feld[new_jx][new_jy] == EL_SP_BASE ||
8632         Feld[new_jx][new_jy] == EL_SAND ||
8633         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
8634          canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
8635     /* !!! extend EL_SAND to anything diggable !!! */
8636
8637     boolean player_is_standing_on_valid_field =
8638       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8639        (IS_WALKABLE(Feld[jx][jy]) &&
8640         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8641
8642 #if 0
8643     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
8644            player_can_fall_down,
8645            player_is_standing_on_valid_field,
8646            player_is_moving_to_valid_field,
8647            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
8648            player->effective_action,
8649            player->can_fall_into_acid);
8650 #endif
8651
8652     if (player_can_fall_down &&
8653         !player_is_standing_on_valid_field &&
8654         !player_is_moving_to_valid_field)
8655     {
8656 #if 0
8657       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
8658              jx, jy, FrameCounter);
8659 #endif
8660
8661       player->programmed_action = MV_DOWN;
8662     }
8663   }
8664 }
8665
8666 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8667 {
8668 #if 1
8669   return CheckGravityMovement(player);
8670 #endif
8671
8672   if (game.gravity && !player->programmed_action)
8673   {
8674     int jx = player->jx, jy = player->jy;
8675     boolean field_under_player_is_free =
8676       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8677     boolean player_is_standing_on_valid_field =
8678       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8679        (IS_WALKABLE(Feld[jx][jy]) &&
8680         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8681
8682     if (field_under_player_is_free && !player_is_standing_on_valid_field)
8683       player->programmed_action = MV_DOWN;
8684   }
8685 }
8686
8687 /*
8688   MovePlayerOneStep()
8689   -----------------------------------------------------------------------------
8690   dx, dy:               direction (non-diagonal) to try to move the player to
8691   real_dx, real_dy:     direction as read from input device (can be diagonal)
8692 */
8693
8694 boolean MovePlayerOneStep(struct PlayerInfo *player,
8695                           int dx, int dy, int real_dx, int real_dy)
8696 {
8697 #if 0
8698   static int trigger_sides[4][2] =
8699   {
8700     /* enter side        leave side */
8701     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
8702     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
8703     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
8704     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
8705   };
8706   int move_direction = (dx == -1 ? MV_LEFT :
8707                         dx == +1 ? MV_RIGHT :
8708                         dy == -1 ? MV_UP :
8709                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8710   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
8711   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
8712 #endif
8713   int jx = player->jx, jy = player->jy;
8714   int new_jx = jx + dx, new_jy = jy + dy;
8715   int element;
8716   int can_move;
8717
8718   if (!player->active || (!dx && !dy))
8719     return MF_NO_ACTION;
8720
8721   player->MovDir = (dx < 0 ? MV_LEFT :
8722                     dx > 0 ? MV_RIGHT :
8723                     dy < 0 ? MV_UP :
8724                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
8725
8726   if (!IN_LEV_FIELD(new_jx, new_jy))
8727     return MF_NO_ACTION;
8728
8729   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8730     return MF_NO_ACTION;
8731
8732 #if 0
8733   element = MovingOrBlocked2Element(new_jx, new_jy);
8734 #else
8735   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8736 #endif
8737
8738   if (DONT_RUN_INTO(element))
8739   {
8740     if (element == EL_ACID && dx == 0 && dy == 1)
8741     {
8742       SplashAcid(new_jx, new_jy);
8743       Feld[jx][jy] = EL_PLAYER_1;
8744       InitMovingField(jx, jy, MV_DOWN);
8745       Store[jx][jy] = EL_ACID;
8746       ContinueMoving(jx, jy);
8747       BuryHero(player);
8748     }
8749     else
8750       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
8751
8752     return MF_MOVING;
8753   }
8754
8755   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8756   if (can_move != MF_MOVING)
8757     return can_move;
8758
8759   /* check if DigField() has caused relocation of the player */
8760   if (player->jx != jx || player->jy != jy)
8761     return MF_NO_ACTION;
8762
8763   StorePlayer[jx][jy] = 0;
8764   player->last_jx = jx;
8765   player->last_jy = jy;
8766   player->jx = new_jx;
8767   player->jy = new_jy;
8768   StorePlayer[new_jx][new_jy] = player->element_nr;
8769
8770   player->MovPos =
8771     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8772
8773   player->step_counter++;
8774
8775 #if 0
8776   player->drop_delay = 0;
8777 #endif
8778
8779   PlayerVisit[jx][jy] = FrameCounter;
8780
8781   ScrollPlayer(player, SCROLL_INIT);
8782
8783 #if 0
8784   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8785   {
8786     CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
8787                                     leave_side);
8788     CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
8789   }
8790
8791   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
8792   {
8793     CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8794                                     CE_OTHER_GETS_ENTERED, enter_side);
8795     CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
8796                            CE_ENTERED_BY_PLAYER, enter_side);
8797   }
8798 #endif
8799
8800   return MF_MOVING;
8801 }
8802
8803 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8804 {
8805   int jx = player->jx, jy = player->jy;
8806   int old_jx = jx, old_jy = jy;
8807   int moved = MF_NO_ACTION;
8808
8809 #if 1
8810   if (!player->active)
8811     return FALSE;
8812
8813   if (!dx && !dy)
8814   {
8815     if (player->MovPos == 0)
8816     {
8817       player->is_moving = FALSE;
8818       player->is_digging = FALSE;
8819       player->is_collecting = FALSE;
8820       player->is_snapping = FALSE;
8821       player->is_pushing = FALSE;
8822     }
8823
8824     return FALSE;
8825   }
8826 #else
8827   if (!player->active || (!dx && !dy))
8828     return FALSE;
8829 #endif
8830
8831 #if 0
8832   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8833       !tape.playing)
8834     return FALSE;
8835 #else
8836
8837 #if 1
8838   if (!FrameReached(&player->move_delay, player->move_delay_value))
8839     return FALSE;
8840 #else
8841   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
8842       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
8843     return FALSE;
8844 #endif
8845
8846 #endif
8847
8848   /* remove the last programmed player action */
8849   player->programmed_action = 0;
8850
8851   if (player->MovPos)
8852   {
8853     /* should only happen if pre-1.2 tape recordings are played */
8854     /* this is only for backward compatibility */
8855
8856     int original_move_delay_value = player->move_delay_value;
8857
8858 #if DEBUG
8859     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8860            tape.counter);
8861 #endif
8862
8863     /* scroll remaining steps with finest movement resolution */
8864     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8865
8866     while (player->MovPos)
8867     {
8868       ScrollPlayer(player, SCROLL_GO_ON);
8869       ScrollScreen(NULL, SCROLL_GO_ON);
8870       FrameCounter++;
8871       DrawAllPlayers();
8872       BackToFront();
8873     }
8874
8875     player->move_delay_value = original_move_delay_value;
8876   }
8877
8878   if (player->last_move_dir & MV_HORIZONTAL)
8879   {
8880     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8881       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8882   }
8883   else
8884   {
8885     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8886       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8887   }
8888
8889   jx = player->jx;
8890   jy = player->jy;
8891
8892   if (moved & MF_MOVING && !ScreenMovPos &&
8893       (player == local_player || !options.network))
8894   {
8895     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8896     int offset = (setup.scroll_delay ? 3 : 0);
8897
8898     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8899     {
8900       /* actual player has left the screen -- scroll in that direction */
8901       if (jx != old_jx)         /* player has moved horizontally */
8902         scroll_x += (jx - old_jx);
8903       else                      /* player has moved vertically */
8904         scroll_y += (jy - old_jy);
8905     }
8906     else
8907     {
8908       if (jx != old_jx)         /* player has moved horizontally */
8909       {
8910         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
8911             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8912           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8913
8914         /* don't scroll over playfield boundaries */
8915         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8916           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8917
8918         /* don't scroll more than one field at a time */
8919         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8920
8921         /* don't scroll against the player's moving direction */
8922         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
8923             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8924           scroll_x = old_scroll_x;
8925       }
8926       else                      /* player has moved vertically */
8927       {
8928         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
8929             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8930           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8931
8932         /* don't scroll over playfield boundaries */
8933         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8934           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8935
8936         /* don't scroll more than one field at a time */
8937         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8938
8939         /* don't scroll against the player's moving direction */
8940         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
8941             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8942           scroll_y = old_scroll_y;
8943       }
8944     }
8945
8946     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8947     {
8948       if (!options.network && !AllPlayersInVisibleScreen())
8949       {
8950         scroll_x = old_scroll_x;
8951         scroll_y = old_scroll_y;
8952       }
8953       else
8954       {
8955         ScrollScreen(player, SCROLL_INIT);
8956         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8957       }
8958     }
8959   }
8960
8961 #if 0
8962 #if 1
8963   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8964 #else
8965   if (!(moved & MF_MOVING) && !player->is_pushing)
8966     player->Frame = 0;
8967 #endif
8968 #endif
8969
8970   player->StepFrame = 0;
8971
8972   if (moved & MF_MOVING)
8973   {
8974     if (old_jx != jx && old_jy == jy)
8975       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8976     else if (old_jx == jx && old_jy != jy)
8977       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8978
8979     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8980
8981     player->last_move_dir = player->MovDir;
8982     player->is_moving = TRUE;
8983 #if 1
8984     player->is_snapping = FALSE;
8985 #endif
8986
8987 #if 1
8988     player->is_switching = FALSE;
8989 #endif
8990
8991     player->is_dropping = FALSE;
8992
8993
8994 #if 0
8995     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
8996     {
8997       static int trigger_sides[4][2] =
8998       {
8999         /* enter side           leave side */
9000         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9001         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9002         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9003         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9004       };
9005       int move_direction = player->MovDir;
9006       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9007       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9008
9009 #if 1
9010       CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9011                                         CE_OTHER_GETS_LEFT,
9012                                         player->index_bit, leave_side);
9013
9014       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9015         CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9016                                  CE_LEFT_BY_PLAYER,
9017                                  player->index_bit, leave_side);
9018
9019       CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9020                                         CE_OTHER_GETS_ENTERED,
9021                                         player->index_bit, enter_side);
9022
9023       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9024         CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9025                                  player->index_bit, enter_side);
9026 #endif
9027
9028     }
9029 #endif
9030
9031
9032   }
9033   else
9034   {
9035     CheckGravityMovementWhenNotMoving(player);
9036
9037     /*
9038     player->last_move_dir = MV_NO_MOVING;
9039     */
9040     player->is_moving = FALSE;
9041   }
9042
9043   if (game.engine_version < VERSION_IDENT(3,0,7,0))
9044   {
9045     TestIfHeroTouchesBadThing(jx, jy);
9046     TestIfPlayerTouchesCustomElement(jx, jy);
9047   }
9048
9049   if (!player->active)
9050     RemoveHero(player);
9051
9052   return moved;
9053 }
9054
9055 void ScrollPlayer(struct PlayerInfo *player, int mode)
9056 {
9057   int jx = player->jx, jy = player->jy;
9058   int last_jx = player->last_jx, last_jy = player->last_jy;
9059   int move_stepsize = TILEX / player->move_delay_value;
9060
9061   if (!player->active || !player->MovPos)
9062     return;
9063
9064   if (mode == SCROLL_INIT)
9065   {
9066     player->actual_frame_counter = FrameCounter;
9067     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9068
9069     if (Feld[last_jx][last_jy] == EL_EMPTY)
9070       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9071
9072 #if 0
9073     DrawPlayer(player);
9074 #endif
9075
9076     return;
9077   }
9078   else if (!FrameReached(&player->actual_frame_counter, 1))
9079     return;
9080
9081   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9082   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9083
9084   if (!player->block_last_field &&
9085       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9086     Feld[last_jx][last_jy] = EL_EMPTY;
9087
9088   /* before DrawPlayer() to draw correct player graphic for this case */
9089   if (player->MovPos == 0)
9090     CheckGravityMovement(player);
9091
9092 #if 0
9093   DrawPlayer(player);   /* needed here only to cleanup last field */
9094 #endif
9095
9096   if (player->MovPos == 0)      /* player reached destination field */
9097   {
9098 #if 1
9099     if (player->move_delay_reset_counter > 0)
9100     {
9101       player->move_delay_reset_counter--;
9102
9103       if (player->move_delay_reset_counter == 0)
9104       {
9105         /* continue with normal speed after quickly moving through gate */
9106         HALVE_PLAYER_SPEED(player);
9107
9108         /* be able to make the next move without delay */
9109         player->move_delay = 0;
9110       }
9111     }
9112 #else
9113     if (IS_PASSABLE(Feld[last_jx][last_jy]))
9114     {
9115       /* continue with normal speed after quickly moving through gate */
9116       HALVE_PLAYER_SPEED(player);
9117
9118       /* be able to make the next move without delay */
9119       player->move_delay = 0;
9120     }
9121 #endif
9122
9123     if (player->block_last_field &&
9124         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9125       Feld[last_jx][last_jy] = EL_EMPTY;
9126
9127     player->last_jx = jx;
9128     player->last_jy = jy;
9129
9130     if (Feld[jx][jy] == EL_EXIT_OPEN ||
9131         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9132         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
9133     {
9134       DrawPlayer(player);       /* needed here only to cleanup last field */
9135       RemoveHero(player);
9136
9137       if (local_player->friends_still_needed == 0 ||
9138           IS_SP_ELEMENT(Feld[jx][jy]))
9139         player->LevelSolved = player->GameOver = TRUE;
9140     }
9141
9142 #if 1
9143     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9144     /* this breaks one level: "machine", level 000 */
9145     {
9146       static int trigger_sides[4][2] =
9147       {
9148         /* enter side           leave side */
9149         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9150         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9151         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9152         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9153       };
9154       int move_direction = player->MovDir;
9155       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9156       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9157       int old_jx = last_jx;
9158       int old_jy = last_jy;
9159
9160 #if 1
9161       CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9162                                         CE_OTHER_GETS_LEFT,
9163                                         player->index_bit, leave_side);
9164
9165       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
9166         CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
9167                                  CE_LEFT_BY_PLAYER,
9168                                  player->index_bit, leave_side);
9169
9170       CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
9171                                         CE_OTHER_GETS_ENTERED,
9172                                         player->index_bit, enter_side);
9173
9174       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9175         CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
9176                                  player->index_bit, enter_side);
9177 #endif
9178
9179     }
9180 #endif
9181
9182     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9183     {
9184       TestIfHeroTouchesBadThing(jx, jy);
9185       TestIfPlayerTouchesCustomElement(jx, jy);
9186 #if 1
9187       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
9188 #endif
9189
9190       if (!player->active)
9191         RemoveHero(player);
9192     }
9193
9194     if (level.use_step_counter)
9195     {
9196       int i;
9197
9198       TimePlayed++;
9199
9200       for (i = 0; i < MAX_PLAYERS; i++)
9201       {
9202         struct PlayerInfo *player = &stored_player[i];
9203
9204         if (SHIELD_ON(player))
9205         {
9206           player->shield_normal_time_left--;
9207
9208           if (player->shield_deadly_time_left > 0)
9209             player->shield_deadly_time_left--;
9210         }
9211       }
9212
9213       if (TimeLeft > 0)
9214       {
9215         TimeLeft--;
9216
9217         if (TimeLeft <= 10 && setup.time_limit)
9218           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9219
9220         DrawGameValue_Time(TimeLeft);
9221
9222         if (!TimeLeft && setup.time_limit)
9223           for (i = 0; i < MAX_PLAYERS; i++)
9224             KillHero(&stored_player[i]);
9225       }
9226       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9227         DrawGameValue_Time(TimePlayed);
9228     }
9229
9230     if (tape.single_step && tape.recording && !tape.pausing &&
9231         !player->programmed_action)
9232       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9233   }
9234 }
9235
9236 void ScrollScreen(struct PlayerInfo *player, int mode)
9237 {
9238   static unsigned long screen_frame_counter = 0;
9239
9240   if (mode == SCROLL_INIT)
9241   {
9242     /* set scrolling step size according to actual player's moving speed */
9243     ScrollStepSize = TILEX / player->move_delay_value;
9244
9245     screen_frame_counter = FrameCounter;
9246     ScreenMovDir = player->MovDir;
9247     ScreenMovPos = player->MovPos;
9248     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9249     return;
9250   }
9251   else if (!FrameReached(&screen_frame_counter, 1))
9252     return;
9253
9254   if (ScreenMovPos)
9255   {
9256     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9257     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9258     redraw_mask |= REDRAW_FIELD;
9259   }
9260   else
9261     ScreenMovDir = MV_NO_MOVING;
9262 }
9263
9264 void TestIfPlayerTouchesCustomElement(int x, int y)
9265 {
9266   static int xy[4][2] =
9267   {
9268     { 0, -1 },
9269     { -1, 0 },
9270     { +1, 0 },
9271     { 0, +1 }
9272   };
9273   static int trigger_sides[4][2] =
9274   {
9275     /* center side       border side */
9276     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9277     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9278     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9279     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9280   };
9281   static int touch_dir[4] =
9282   {
9283     MV_LEFT | MV_RIGHT,
9284     MV_UP   | MV_DOWN,
9285     MV_UP   | MV_DOWN,
9286     MV_LEFT | MV_RIGHT
9287   };
9288   int center_element = Feld[x][y];      /* should always be non-moving! */
9289   int i;
9290
9291   for (i = 0; i < NUM_DIRECTIONS; i++)
9292   {
9293     int xx = x + xy[i][0];
9294     int yy = y + xy[i][1];
9295     int center_side = trigger_sides[i][0];
9296     int border_side = trigger_sides[i][1];
9297     int border_element;
9298
9299     if (!IN_LEV_FIELD(xx, yy))
9300       continue;
9301
9302     if (IS_PLAYER(x, y))
9303     {
9304       struct PlayerInfo *player = PLAYERINFO(x, y);
9305
9306       if (game.engine_version < VERSION_IDENT(3,0,7,0))
9307         border_element = Feld[xx][yy];          /* may be moving! */
9308       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9309         border_element = Feld[xx][yy];
9310       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
9311         border_element = MovingOrBlocked2Element(xx, yy);
9312       else
9313         continue;               /* center and border element do not touch */
9314
9315       CheckTriggeredElementChangePlayer(xx, yy, border_element,
9316                                         CE_OTHER_GETS_TOUCHED,
9317                                         player->index_bit, border_side);
9318       CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9319                                player->index_bit, border_side);
9320     }
9321     else if (IS_PLAYER(xx, yy))
9322     {
9323       struct PlayerInfo *player = PLAYERINFO(xx, yy);
9324
9325       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9326       {
9327         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9328           continue;             /* center and border element do not touch */
9329       }
9330
9331       CheckTriggeredElementChangePlayer(x, y, center_element,
9332                                         CE_OTHER_GETS_TOUCHED,
9333                                         player->index_bit, center_side);
9334       CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9335                                player->index_bit, center_side);
9336
9337       break;
9338     }
9339   }
9340 }
9341
9342 void TestIfElementTouchesCustomElement(int x, int y)
9343 {
9344   static int xy[4][2] =
9345   {
9346     { 0, -1 },
9347     { -1, 0 },
9348     { +1, 0 },
9349     { 0, +1 }
9350   };
9351   static int trigger_sides[4][2] =
9352   {
9353     /* center side      border side */
9354     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9355     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9356     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9357     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9358   };
9359   static int touch_dir[4] =
9360   {
9361     MV_LEFT | MV_RIGHT,
9362     MV_UP   | MV_DOWN,
9363     MV_UP   | MV_DOWN,
9364     MV_LEFT | MV_RIGHT
9365   };
9366   boolean change_center_element = FALSE;
9367   int center_element_change_page = 0;
9368   int center_element = Feld[x][y];      /* should always be non-moving! */
9369   int border_trigger_element;
9370   int i, j;
9371
9372   for (i = 0; i < NUM_DIRECTIONS; i++)
9373   {
9374     int xx = x + xy[i][0];
9375     int yy = y + xy[i][1];
9376     int center_side = trigger_sides[i][0];
9377     int border_side = trigger_sides[i][1];
9378     int border_element;
9379
9380     if (!IN_LEV_FIELD(xx, yy))
9381       continue;
9382
9383     if (game.engine_version < VERSION_IDENT(3,0,7,0))
9384       border_element = Feld[xx][yy];    /* may be moving! */
9385     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9386       border_element = Feld[xx][yy];
9387     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
9388       border_element = MovingOrBlocked2Element(xx, yy);
9389     else
9390       continue;                 /* center and border element do not touch */
9391
9392     /* check for change of center element (but change it only once) */
9393     if (IS_CUSTOM_ELEMENT(center_element) &&
9394         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9395         !change_center_element)
9396     {
9397       for (j = 0; j < element_info[center_element].num_change_pages; j++)
9398       {
9399         struct ElementChangeInfo *change =
9400           &element_info[center_element].change_page[j];
9401
9402         if (change->can_change &&
9403             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9404             change->trigger_side & border_side &&
9405 #if 1
9406             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9407 #else
9408             change->trigger_element == border_element
9409 #endif
9410             )
9411         {
9412           change_center_element = TRUE;
9413           center_element_change_page = j;
9414           border_trigger_element = border_element;
9415
9416           break;
9417         }
9418       }
9419     }
9420
9421     /* check for change of border element */
9422     if (IS_CUSTOM_ELEMENT(border_element) &&
9423         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9424     {
9425       for (j = 0; j < element_info[border_element].num_change_pages; j++)
9426       {
9427         struct ElementChangeInfo *change =
9428           &element_info[border_element].change_page[j];
9429
9430         if (change->can_change &&
9431             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9432             change->trigger_side & center_side &&
9433 #if 1
9434             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9435 #else
9436             change->trigger_element == center_element
9437 #endif
9438             )
9439         {
9440 #if 0
9441           printf("::: border_element %d, %d\n", x, y);
9442 #endif
9443
9444           CheckElementChangePage(xx, yy, border_element, center_element,
9445                                  CE_OTHER_IS_TOUCHING, j);
9446           break;
9447         }
9448       }
9449     }
9450   }
9451
9452   if (change_center_element)
9453   {
9454 #if 0
9455     printf("::: center_element %d, %d\n", x, y);
9456 #endif
9457
9458     CheckElementChangePage(x, y, center_element, border_trigger_element,
9459                            CE_OTHER_IS_TOUCHING, center_element_change_page);
9460   }
9461 }
9462
9463 void TestIfElementHitsCustomElement(int x, int y, int direction)
9464 {
9465   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9466   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9467   int hitx = x + dx, hity = y + dy;
9468   int hitting_element = Feld[x][y];
9469   int touched_element;
9470 #if 0
9471   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9472                         !IS_FREE(hitx, hity) &&
9473                         (!IS_MOVING(hitx, hity) ||
9474                          MovDir[hitx][hity] != direction ||
9475                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9476 #endif
9477
9478   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9479     return;
9480
9481 #if 0
9482   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9483     return;
9484 #endif
9485
9486   touched_element = (IN_LEV_FIELD(hitx, hity) ?
9487                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9488
9489   CheckElementChangeSide(x, y, hitting_element, touched_element,
9490                          CE_HITTING_SOMETHING, direction);
9491
9492   if (IN_LEV_FIELD(hitx, hity))
9493   {
9494     int opposite_direction = MV_DIR_OPPOSITE(direction);
9495     int hitting_side = direction;
9496     int touched_side = opposite_direction;
9497 #if 0
9498     int touched_element = MovingOrBlocked2Element(hitx, hity);
9499 #endif
9500 #if 1
9501     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9502                           MovDir[hitx][hity] != direction ||
9503                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9504
9505     object_hit = TRUE;
9506 #endif
9507
9508     if (object_hit)
9509     {
9510       int i;
9511
9512       CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9513                              CE_HIT_BY_SOMETHING, opposite_direction);
9514
9515       if (IS_CUSTOM_ELEMENT(hitting_element) &&
9516           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
9517       {
9518         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9519         {
9520           struct ElementChangeInfo *change =
9521             &element_info[hitting_element].change_page[i];
9522
9523           if (change->can_change &&
9524               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
9525               change->trigger_side & touched_side &&
9526           
9527 #if 1
9528               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9529 #else
9530               change->trigger_element == touched_element
9531 #endif
9532               )
9533           {
9534             CheckElementChangePage(x, y, hitting_element, touched_element,
9535                                    CE_OTHER_IS_HITTING, i);
9536             break;
9537           }
9538         }
9539       }
9540
9541       if (IS_CUSTOM_ELEMENT(touched_element) &&
9542           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
9543       {
9544         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9545         {
9546           struct ElementChangeInfo *change =
9547             &element_info[touched_element].change_page[i];
9548
9549           if (change->can_change &&
9550               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
9551               change->trigger_side & hitting_side &&
9552 #if 1
9553               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9554 #else
9555               change->trigger_element == hitting_element
9556 #endif
9557               )
9558           {
9559             CheckElementChangePage(hitx, hity, touched_element,
9560                                    hitting_element, CE_OTHER_GETS_HIT, i);
9561             break;
9562           }
9563         }
9564       }
9565     }
9566   }
9567 }
9568
9569 #if 0
9570 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9571 {
9572   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9573   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9574   int hitx = x + dx, hity = y + dy;
9575   int hitting_element = Feld[x][y];
9576   int touched_element;
9577 #if 0
9578   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9579                         !IS_FREE(hitx, hity) &&
9580                         (!IS_MOVING(hitx, hity) ||
9581                          MovDir[hitx][hity] != direction ||
9582                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9583 #endif
9584
9585   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9586     return;
9587
9588 #if 0
9589   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9590     return;
9591 #endif
9592
9593   touched_element = (IN_LEV_FIELD(hitx, hity) ?
9594                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9595
9596   CheckElementChangeSide(x, y, hitting_element, touched_element,
9597                          EP_CAN_SMASH_EVERYTHING, direction);
9598
9599   if (IN_LEV_FIELD(hitx, hity))
9600   {
9601     int opposite_direction = MV_DIR_OPPOSITE(direction);
9602     int hitting_side = direction;
9603     int touched_side = opposite_direction;
9604 #if 0
9605     int touched_element = MovingOrBlocked2Element(hitx, hity);
9606 #endif
9607 #if 1
9608     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9609                           MovDir[hitx][hity] != direction ||
9610                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9611
9612     object_hit = TRUE;
9613 #endif
9614
9615     if (object_hit)
9616     {
9617       int i;
9618
9619       CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
9620                              CE_SMASHED_BY_SOMETHING, opposite_direction);
9621
9622       if (IS_CUSTOM_ELEMENT(hitting_element) &&
9623           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
9624       {
9625         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
9626         {
9627           struct ElementChangeInfo *change =
9628             &element_info[hitting_element].change_page[i];
9629
9630           if (change->can_change &&
9631               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
9632               change->trigger_side & touched_side &&
9633           
9634 #if 1
9635               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
9636 #else
9637               change->trigger_element == touched_element
9638 #endif
9639               )
9640           {
9641             CheckElementChangePage(x, y, hitting_element, touched_element,
9642                                    CE_OTHER_IS_SMASHING, i);
9643             break;
9644           }
9645         }
9646       }
9647
9648       if (IS_CUSTOM_ELEMENT(touched_element) &&
9649           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
9650       {
9651         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
9652         {
9653           struct ElementChangeInfo *change =
9654             &element_info[touched_element].change_page[i];
9655
9656           if (change->can_change &&
9657               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
9658               change->trigger_side & hitting_side &&
9659 #if 1
9660               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
9661 #else
9662               change->trigger_element == hitting_element
9663 #endif
9664               )
9665           {
9666             CheckElementChangePage(hitx, hity, touched_element,
9667                                    hitting_element, CE_OTHER_GETS_SMASHED, i);
9668             break;
9669           }
9670         }
9671       }
9672     }
9673   }
9674 }
9675 #endif
9676
9677 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9678 {
9679   int i, kill_x = -1, kill_y = -1;
9680   static int test_xy[4][2] =
9681   {
9682     { 0, -1 },
9683     { -1, 0 },
9684     { +1, 0 },
9685     { 0, +1 }
9686   };
9687   static int test_dir[4] =
9688   {
9689     MV_UP,
9690     MV_LEFT,
9691     MV_RIGHT,
9692     MV_DOWN
9693   };
9694
9695   for (i = 0; i < NUM_DIRECTIONS; i++)
9696   {
9697     int test_x, test_y, test_move_dir, test_element;
9698
9699     test_x = good_x + test_xy[i][0];
9700     test_y = good_y + test_xy[i][1];
9701     if (!IN_LEV_FIELD(test_x, test_y))
9702       continue;
9703
9704     test_move_dir =
9705       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9706
9707 #if 0
9708     test_element = Feld[test_x][test_y];
9709 #else
9710     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9711 #endif
9712
9713     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9714        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9715     */
9716     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9717         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
9718     {
9719       kill_x = test_x;
9720       kill_y = test_y;
9721       break;
9722     }
9723   }
9724
9725   if (kill_x != -1 || kill_y != -1)
9726   {
9727     if (IS_PLAYER(good_x, good_y))
9728     {
9729       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9730
9731       if (player->shield_deadly_time_left > 0)
9732         Bang(kill_x, kill_y);
9733       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9734         KillHero(player);
9735     }
9736     else
9737       Bang(good_x, good_y);
9738   }
9739 }
9740
9741 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9742 {
9743   int i, kill_x = -1, kill_y = -1;
9744   int bad_element = Feld[bad_x][bad_y];
9745   static int test_xy[4][2] =
9746   {
9747     { 0, -1 },
9748     { -1, 0 },
9749     { +1, 0 },
9750     { 0, +1 }
9751   };
9752   static int touch_dir[4] =
9753   {
9754     MV_LEFT | MV_RIGHT,
9755     MV_UP   | MV_DOWN,
9756     MV_UP   | MV_DOWN,
9757     MV_LEFT | MV_RIGHT
9758   };
9759   static int test_dir[4] =
9760   {
9761     MV_UP,
9762     MV_LEFT,
9763     MV_RIGHT,
9764     MV_DOWN
9765   };
9766
9767   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
9768     return;
9769
9770   for (i = 0; i < NUM_DIRECTIONS; i++)
9771   {
9772     int test_x, test_y, test_move_dir, test_element;
9773
9774     test_x = bad_x + test_xy[i][0];
9775     test_y = bad_y + test_xy[i][1];
9776     if (!IN_LEV_FIELD(test_x, test_y))
9777       continue;
9778
9779     test_move_dir =
9780       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9781
9782     test_element = Feld[test_x][test_y];
9783
9784     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9785        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9786     */
9787     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
9788         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
9789     {
9790       /* good thing is player or penguin that does not move away */
9791       if (IS_PLAYER(test_x, test_y))
9792       {
9793         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9794
9795         if (bad_element == EL_ROBOT && player->is_moving)
9796           continue;     /* robot does not kill player if he is moving */
9797
9798         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9799         {
9800           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9801             continue;           /* center and border element do not touch */
9802         }
9803
9804         kill_x = test_x;
9805         kill_y = test_y;
9806         break;
9807       }
9808       else if (test_element == EL_PENGUIN)
9809       {
9810         kill_x = test_x;
9811         kill_y = test_y;
9812         break;
9813       }
9814     }
9815   }
9816
9817   if (kill_x != -1 || kill_y != -1)
9818   {
9819     if (IS_PLAYER(kill_x, kill_y))
9820     {
9821       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9822
9823       if (player->shield_deadly_time_left > 0)
9824         Bang(bad_x, bad_y);
9825       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9826         KillHero(player);
9827     }
9828     else
9829       Bang(kill_x, kill_y);
9830   }
9831 }
9832
9833 void TestIfHeroTouchesBadThing(int x, int y)
9834 {
9835   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9836 }
9837
9838 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
9839 {
9840   TestIfGoodThingHitsBadThing(x, y, move_dir);
9841 }
9842
9843 void TestIfBadThingTouchesHero(int x, int y)
9844 {
9845   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9846 }
9847
9848 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
9849 {
9850   TestIfBadThingHitsGoodThing(x, y, move_dir);
9851 }
9852
9853 void TestIfFriendTouchesBadThing(int x, int y)
9854 {
9855   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9856 }
9857
9858 void TestIfBadThingTouchesFriend(int x, int y)
9859 {
9860   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9861 }
9862
9863 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9864 {
9865   int i, kill_x = bad_x, kill_y = bad_y;
9866   static int xy[4][2] =
9867   {
9868     { 0, -1 },
9869     { -1, 0 },
9870     { +1, 0 },
9871     { 0, +1 }
9872   };
9873
9874   for (i = 0; i < NUM_DIRECTIONS; i++)
9875   {
9876     int x, y, element;
9877
9878     x = bad_x + xy[i][0];
9879     y = bad_y + xy[i][1];
9880     if (!IN_LEV_FIELD(x, y))
9881       continue;
9882
9883     element = Feld[x][y];
9884     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9885         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9886     {
9887       kill_x = x;
9888       kill_y = y;
9889       break;
9890     }
9891   }
9892
9893   if (kill_x != bad_x || kill_y != bad_y)
9894     Bang(bad_x, bad_y);
9895 }
9896
9897 void KillHero(struct PlayerInfo *player)
9898 {
9899   int jx = player->jx, jy = player->jy;
9900
9901   if (!player->active)
9902     return;
9903
9904   /* remove accessible field at the player's position */
9905   Feld[jx][jy] = EL_EMPTY;
9906
9907   /* deactivate shield (else Bang()/Explode() would not work right) */
9908   player->shield_normal_time_left = 0;
9909   player->shield_deadly_time_left = 0;
9910
9911   Bang(jx, jy);
9912   BuryHero(player);
9913 }
9914
9915 static void KillHeroUnlessEnemyProtected(int x, int y)
9916 {
9917   if (!PLAYER_ENEMY_PROTECTED(x, y))
9918     KillHero(PLAYERINFO(x, y));
9919 }
9920
9921 static void KillHeroUnlessExplosionProtected(int x, int y)
9922 {
9923   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9924     KillHero(PLAYERINFO(x, y));
9925 }
9926
9927 void BuryHero(struct PlayerInfo *player)
9928 {
9929   int jx = player->jx, jy = player->jy;
9930
9931   if (!player->active)
9932     return;
9933
9934 #if 1
9935   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9936 #else
9937   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
9938 #endif
9939   PlayLevelSound(jx, jy, SND_GAME_LOSING);
9940
9941   player->GameOver = TRUE;
9942   RemoveHero(player);
9943 }
9944
9945 void RemoveHero(struct PlayerInfo *player)
9946 {
9947   int jx = player->jx, jy = player->jy;
9948   int i, found = FALSE;
9949
9950   player->present = FALSE;
9951   player->active = FALSE;
9952
9953   if (!ExplodeField[jx][jy])
9954     StorePlayer[jx][jy] = 0;
9955
9956   for (i = 0; i < MAX_PLAYERS; i++)
9957     if (stored_player[i].active)
9958       found = TRUE;
9959
9960   if (!found)
9961     AllPlayersGone = TRUE;
9962
9963   ExitX = ZX = jx;
9964   ExitY = ZY = jy;
9965 }
9966
9967 /*
9968   =============================================================================
9969   checkDiagonalPushing()
9970   -----------------------------------------------------------------------------
9971   check if diagonal input device direction results in pushing of object
9972   (by checking if the alternative direction is walkable, diggable, ...)
9973   =============================================================================
9974 */
9975
9976 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9977                                     int x, int y, int real_dx, int real_dy)
9978 {
9979   int jx, jy, dx, dy, xx, yy;
9980
9981   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
9982     return TRUE;
9983
9984   /* diagonal direction: check alternative direction */
9985   jx = player->jx;
9986   jy = player->jy;
9987   dx = x - jx;
9988   dy = y - jy;
9989   xx = jx + (dx == 0 ? real_dx : 0);
9990   yy = jy + (dy == 0 ? real_dy : 0);
9991
9992   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9993 }
9994
9995 /*
9996   =============================================================================
9997   DigField()
9998   -----------------------------------------------------------------------------
9999   x, y:                 field next to player (non-diagonal) to try to dig to
10000   real_dx, real_dy:     direction as read from input device (can be diagonal)
10001   =============================================================================
10002 */
10003
10004 int DigField(struct PlayerInfo *player,
10005              int oldx, int oldy, int x, int y,
10006              int real_dx, int real_dy, int mode)
10007 {
10008   static int trigger_sides[4] =
10009   {
10010     CH_SIDE_RIGHT,      /* moving left  */
10011     CH_SIDE_LEFT,       /* moving right */
10012     CH_SIDE_BOTTOM,     /* moving up    */
10013     CH_SIDE_TOP,        /* moving down  */
10014   };
10015 #if 0
10016   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10017 #endif
10018   int jx = oldx, jy = oldy;
10019   int dx = x - jx, dy = y - jy;
10020   int nextx = x + dx, nexty = y + dy;
10021   int move_direction = (dx == -1 ? MV_LEFT :
10022                         dx == +1 ? MV_RIGHT :
10023                         dy == -1 ? MV_UP :
10024                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10025   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10026   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10027   int old_element = Feld[jx][jy];
10028   int element;
10029
10030   if (player->MovPos == 0)
10031   {
10032     player->is_digging = FALSE;
10033     player->is_collecting = FALSE;
10034   }
10035
10036   if (player->MovPos == 0)      /* last pushing move finished */
10037     player->is_pushing = FALSE;
10038
10039   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
10040   {
10041     player->is_switching = FALSE;
10042     player->push_delay = 0;
10043
10044     return MF_NO_ACTION;
10045   }
10046
10047   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10048     return MF_NO_ACTION;
10049
10050 #if 0
10051
10052 #if 0
10053   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10054 #else
10055   if (IS_TUBE(Feld[jx][jy]) ||
10056       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10057 #endif
10058   {
10059     int i = 0;
10060     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10061     int tube_leave_directions[][2] =
10062     {
10063       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10064       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
10065       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
10066       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
10067       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
10068       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
10069       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
10070       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
10071       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
10072       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
10073       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
10074       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10075     };
10076
10077     while (tube_leave_directions[i][0] != tube_element)
10078     {
10079       i++;
10080       if (tube_leave_directions[i][0] == -1)    /* should not happen */
10081         break;
10082     }
10083
10084     if (!(tube_leave_directions[i][1] & move_direction))
10085       return MF_NO_ACTION;      /* tube has no opening in this direction */
10086   }
10087
10088 #else
10089
10090   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10091     old_element = Back[jx][jy];
10092
10093 #endif
10094
10095   if (IS_WALKABLE(old_element) &&
10096       !(element_info[old_element].access_direction & move_direction))
10097     return MF_NO_ACTION;        /* field has no opening in this direction */
10098
10099   element = Feld[x][y];
10100
10101   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10102       game.engine_version >= VERSION_IDENT(2,2,0,0))
10103     return MF_NO_ACTION;
10104
10105   switch (element)
10106   {
10107     case EL_SP_PORT_LEFT:
10108     case EL_SP_PORT_RIGHT:
10109     case EL_SP_PORT_UP:
10110     case EL_SP_PORT_DOWN:
10111     case EL_SP_PORT_HORIZONTAL:
10112     case EL_SP_PORT_VERTICAL:
10113     case EL_SP_PORT_ANY:
10114     case EL_SP_GRAVITY_PORT_LEFT:
10115     case EL_SP_GRAVITY_PORT_RIGHT:
10116     case EL_SP_GRAVITY_PORT_UP:
10117     case EL_SP_GRAVITY_PORT_DOWN:
10118 #if 1
10119       if (!canEnterSupaplexPort(x, y, dx, dy))
10120         return MF_NO_ACTION;
10121 #else
10122       if ((dx == -1 &&
10123            element != EL_SP_PORT_LEFT &&
10124            element != EL_SP_GRAVITY_PORT_LEFT &&
10125            element != EL_SP_PORT_HORIZONTAL &&
10126            element != EL_SP_PORT_ANY) ||
10127           (dx == +1 &&
10128            element != EL_SP_PORT_RIGHT &&
10129            element != EL_SP_GRAVITY_PORT_RIGHT &&
10130            element != EL_SP_PORT_HORIZONTAL &&
10131            element != EL_SP_PORT_ANY) ||
10132           (dy == -1 &&
10133            element != EL_SP_PORT_UP &&
10134            element != EL_SP_GRAVITY_PORT_UP &&
10135            element != EL_SP_PORT_VERTICAL &&
10136            element != EL_SP_PORT_ANY) ||
10137           (dy == +1 &&
10138            element != EL_SP_PORT_DOWN &&
10139            element != EL_SP_GRAVITY_PORT_DOWN &&
10140            element != EL_SP_PORT_VERTICAL &&
10141            element != EL_SP_PORT_ANY) ||
10142           !IN_LEV_FIELD(nextx, nexty) ||
10143           !IS_FREE(nextx, nexty))
10144         return MF_NO_ACTION;
10145 #endif
10146
10147       if (element == EL_SP_GRAVITY_PORT_LEFT ||
10148           element == EL_SP_GRAVITY_PORT_RIGHT ||
10149           element == EL_SP_GRAVITY_PORT_UP ||
10150           element == EL_SP_GRAVITY_PORT_DOWN)
10151         game.gravity = !game.gravity;
10152
10153       /* automatically move to the next field with double speed */
10154       player->programmed_action = move_direction;
10155 #if 1
10156       if (player->move_delay_reset_counter == 0)
10157       {
10158         player->move_delay_reset_counter = 2;   /* two double speed steps */
10159
10160         DOUBLE_PLAYER_SPEED(player);
10161       }
10162 #else
10163       player->move_delay_reset_counter = 2;
10164
10165       DOUBLE_PLAYER_SPEED(player);
10166 #endif
10167
10168 #if 0
10169       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10170 #endif
10171
10172       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10173       break;
10174
10175 #if 0
10176     case EL_TUBE_ANY:
10177     case EL_TUBE_VERTICAL:
10178     case EL_TUBE_HORIZONTAL:
10179     case EL_TUBE_VERTICAL_LEFT:
10180     case EL_TUBE_VERTICAL_RIGHT:
10181     case EL_TUBE_HORIZONTAL_UP:
10182     case EL_TUBE_HORIZONTAL_DOWN:
10183     case EL_TUBE_LEFT_UP:
10184     case EL_TUBE_LEFT_DOWN:
10185     case EL_TUBE_RIGHT_UP:
10186     case EL_TUBE_RIGHT_DOWN:
10187       {
10188         int i = 0;
10189         int tube_enter_directions[][2] =
10190         {
10191           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10192           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
10193           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
10194           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
10195           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
10196           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
10197           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
10198           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
10199           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
10200           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
10201           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
10202           { -1,                         MV_NO_MOVING                         }
10203         };
10204
10205         while (tube_enter_directions[i][0] != element)
10206         {
10207           i++;
10208           if (tube_enter_directions[i][0] == -1)        /* should not happen */
10209             break;
10210         }
10211
10212         if (!(tube_enter_directions[i][1] & move_direction))
10213           return MF_NO_ACTION;  /* tube has no opening in this direction */
10214
10215         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10216       }
10217       break;
10218 #endif
10219
10220     default:
10221
10222       if (IS_WALKABLE(element))
10223       {
10224         int sound_action = ACTION_WALKING;
10225
10226         if (!(element_info[element].access_direction & opposite_direction))
10227           return MF_NO_ACTION;  /* field not accessible from this direction */
10228
10229         if (element >= EL_GATE_1 && element <= EL_GATE_4)
10230         {
10231           if (!player->key[element - EL_GATE_1])
10232             return MF_NO_ACTION;
10233         }
10234         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
10235         {
10236           if (!player->key[element - EL_GATE_1_GRAY])
10237             return MF_NO_ACTION;
10238         }
10239         else if (element == EL_EXIT_OPEN ||
10240                  element == EL_SP_EXIT_OPEN ||
10241                  element == EL_SP_EXIT_OPENING)
10242         {
10243           sound_action = ACTION_PASSING;        /* player is passing exit */
10244         }
10245         else if (element == EL_EMPTY)
10246         {
10247           sound_action = ACTION_MOVING;         /* nothing to walk on */
10248         }
10249
10250         /* play sound from background or player, whatever is available */
10251         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10252           PlayLevelSoundElementAction(x, y, element, sound_action);
10253         else
10254           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10255
10256         break;
10257       }
10258       else if (IS_PASSABLE(element))
10259       {
10260         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10261           return MF_NO_ACTION;
10262
10263         if (IS_CUSTOM_ELEMENT(element) &&
10264             !(element_info[element].access_direction & opposite_direction))
10265           return MF_NO_ACTION;  /* field not accessible from this direction */
10266
10267 #if 1
10268         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
10269           return MF_NO_ACTION;
10270 #endif
10271
10272         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
10273         {
10274           if (!player->key[element - EL_EM_GATE_1])
10275             return MF_NO_ACTION;
10276         }
10277         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
10278         {
10279           if (!player->key[element - EL_EM_GATE_1_GRAY])
10280             return MF_NO_ACTION;
10281         }
10282
10283         /* automatically move to the next field with double speed */
10284         player->programmed_action = move_direction;
10285 #if 1
10286         if (player->move_delay_reset_counter == 0)
10287         {
10288           player->move_delay_reset_counter = 2; /* two double speed steps */
10289
10290           DOUBLE_PLAYER_SPEED(player);
10291         }
10292 #else
10293         player->move_delay_reset_counter = 2;
10294
10295         DOUBLE_PLAYER_SPEED(player);
10296 #endif
10297
10298         PlayLevelSoundAction(x, y, ACTION_PASSING);
10299
10300         break;
10301       }
10302       else if (IS_DIGGABLE(element))
10303       {
10304         RemoveField(x, y);
10305
10306         if (mode != DF_SNAP)
10307         {
10308 #if 1
10309           GfxElement[x][y] = GFX_ELEMENT(element);
10310 #else
10311           GfxElement[x][y] =
10312             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10313 #endif
10314           player->is_digging = TRUE;
10315         }
10316
10317         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10318
10319         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
10320                                           player->index_bit, dig_side);
10321
10322 #if 1
10323         if (mode == DF_SNAP)
10324           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
10325 #endif
10326
10327         break;
10328       }
10329       else if (IS_COLLECTIBLE(element))
10330       {
10331         RemoveField(x, y);
10332
10333         if (mode != DF_SNAP)
10334         {
10335           GfxElement[x][y] = element;
10336           player->is_collecting = TRUE;
10337         }
10338
10339         if (element == EL_SPEED_PILL)
10340           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10341         else if (element == EL_EXTRA_TIME && level.time > 0)
10342         {
10343           TimeLeft += 10;
10344           DrawGameValue_Time(TimeLeft);
10345         }
10346         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10347         {
10348           player->shield_normal_time_left += 10;
10349           if (element == EL_SHIELD_DEADLY)
10350             player->shield_deadly_time_left += 10;
10351         }
10352         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10353         {
10354           if (player->inventory_size < MAX_INVENTORY_SIZE)
10355             player->inventory_element[player->inventory_size++] = element;
10356
10357           DrawGameValue_Dynamite(local_player->inventory_size);
10358         }
10359         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10360         {
10361           player->dynabomb_count++;
10362           player->dynabombs_left++;
10363         }
10364         else if (element == EL_DYNABOMB_INCREASE_SIZE)
10365         {
10366           player->dynabomb_size++;
10367         }
10368         else if (element == EL_DYNABOMB_INCREASE_POWER)
10369         {
10370           player->dynabomb_xl = TRUE;
10371         }
10372         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10373                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10374         {
10375           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10376                         element - EL_KEY_1 : element - EL_EM_KEY_1);
10377
10378           player->key[key_nr] = TRUE;
10379
10380           DrawGameValue_Keys(player);
10381
10382           redraw_mask |= REDRAW_DOOR_1;
10383         }
10384         else if (IS_ENVELOPE(element))
10385         {
10386 #if 1
10387           player->show_envelope = element;
10388 #else
10389           ShowEnvelope(element - EL_ENVELOPE_1);
10390 #endif
10391         }
10392         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
10393         {
10394           int i;
10395
10396           if (element_info[element].collect_count == 0)
10397             player->inventory_infinite_element = element;
10398           else
10399             for (i = 0; i < element_info[element].collect_count; i++)
10400               if (player->inventory_size < MAX_INVENTORY_SIZE)
10401                 player->inventory_element[player->inventory_size++] = element;
10402
10403           DrawGameValue_Dynamite(local_player->inventory_size);
10404         }
10405         else if (element_info[element].collect_count > 0)
10406         {
10407           local_player->gems_still_needed -=
10408             element_info[element].collect_count;
10409           if (local_player->gems_still_needed < 0)
10410             local_player->gems_still_needed = 0;
10411
10412           DrawGameValue_Emeralds(local_player->gems_still_needed);
10413         }
10414
10415         RaiseScoreElement(element);
10416         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10417
10418         CheckTriggeredElementChangePlayer(x, y, element,
10419                                           CE_OTHER_GETS_COLLECTED,
10420                                           player->index_bit, dig_side);
10421
10422 #if 1
10423         if (mode == DF_SNAP)
10424           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
10425 #endif
10426
10427         break;
10428       }
10429       else if (IS_PUSHABLE(element))
10430       {
10431         if (mode == DF_SNAP && element != EL_BD_ROCK)
10432           return MF_NO_ACTION;
10433
10434         if (CAN_FALL(element) && dy)
10435           return MF_NO_ACTION;
10436
10437         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10438             !(element == EL_SPRING && level.use_spring_bug))
10439           return MF_NO_ACTION;
10440
10441 #if 1
10442         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10443             ((move_direction & MV_VERTICAL &&
10444               ((element_info[element].move_pattern & MV_LEFT &&
10445                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10446                (element_info[element].move_pattern & MV_RIGHT &&
10447                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10448              (move_direction & MV_HORIZONTAL &&
10449               ((element_info[element].move_pattern & MV_UP &&
10450                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10451                (element_info[element].move_pattern & MV_DOWN &&
10452                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10453           return MF_NO_ACTION;
10454 #endif
10455
10456 #if 1
10457         /* do not push elements already moving away faster than player */
10458         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10459             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10460           return MF_NO_ACTION;
10461 #else
10462         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
10463           return MF_NO_ACTION;
10464 #endif
10465
10466 #if 1
10467         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10468         {
10469           if (player->push_delay_value == -1)
10470             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10471         }
10472         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10473         {
10474           if (!player->is_pushing)
10475             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10476         }
10477
10478         /*
10479         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
10480             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
10481              !player_is_pushing))
10482           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10483         */
10484 #else
10485         if (!player->is_pushing &&
10486             game.engine_version >= VERSION_IDENT(2,2,0,7))
10487           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10488 #endif
10489
10490 #if 0
10491         printf("::: push delay: %ld [%d, %d] [%d]\n",
10492                player->push_delay_value, FrameCounter, game.engine_version,
10493                player->is_pushing);
10494 #endif
10495
10496         player->is_pushing = TRUE;
10497
10498         if (!(IN_LEV_FIELD(nextx, nexty) &&
10499               (IS_FREE(nextx, nexty) ||
10500                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10501                 IS_SB_ELEMENT(element)))))
10502           return MF_NO_ACTION;
10503
10504         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10505           return MF_NO_ACTION;
10506
10507         if (player->push_delay == 0)    /* new pushing; restart delay */
10508           player->push_delay = FrameCounter;
10509
10510         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
10511             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10512             element != EL_SPRING && element != EL_BALLOON)
10513         {
10514           /* make sure that there is no move delay before next try to push */
10515           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10516             player->move_delay = INITIAL_MOVE_DELAY_OFF;
10517
10518           return MF_NO_ACTION;
10519         }
10520
10521 #if 0
10522         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
10523 #endif
10524
10525         if (IS_SB_ELEMENT(element))
10526         {
10527           if (element == EL_SOKOBAN_FIELD_FULL)
10528           {
10529             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10530             local_player->sokobanfields_still_needed++;
10531           }
10532
10533           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10534           {
10535             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10536             local_player->sokobanfields_still_needed--;
10537           }
10538
10539           Feld[x][y] = EL_SOKOBAN_OBJECT;
10540
10541           if (Back[x][y] == Back[nextx][nexty])
10542             PlayLevelSoundAction(x, y, ACTION_PUSHING);
10543           else if (Back[x][y] != 0)
10544             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10545                                         ACTION_EMPTYING);
10546           else
10547             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10548                                         ACTION_FILLING);
10549
10550           if (local_player->sokobanfields_still_needed == 0 &&
10551               game.emulation == EMU_SOKOBAN)
10552           {
10553             player->LevelSolved = player->GameOver = TRUE;
10554             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10555           }
10556         }
10557         else
10558           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10559
10560         InitMovingField(x, y, move_direction);
10561         GfxAction[x][y] = ACTION_PUSHING;
10562
10563         if (mode == DF_SNAP)
10564           ContinueMoving(x, y);
10565         else
10566           MovPos[x][y] = (dx != 0 ? dx : dy);
10567
10568         Pushed[x][y] = TRUE;
10569         Pushed[nextx][nexty] = TRUE;
10570
10571         if (game.engine_version < VERSION_IDENT(2,2,0,7))
10572           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10573         else
10574           player->push_delay_value = -1;        /* get new value later */
10575
10576         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
10577                                           player->index_bit, dig_side);
10578         CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10579                                  player->index_bit, dig_side);
10580
10581         break;
10582       }
10583       else if (IS_SWITCHABLE(element))
10584       {
10585         if (PLAYER_SWITCHING(player, x, y))
10586           return MF_ACTION;
10587
10588         player->is_switching = TRUE;
10589         player->switch_x = x;
10590         player->switch_y = y;
10591
10592         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10593
10594         if (element == EL_ROBOT_WHEEL)
10595         {
10596           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10597           ZX = x;
10598           ZY = y;
10599
10600           DrawLevelField(x, y);
10601         }
10602         else if (element == EL_SP_TERMINAL)
10603         {
10604           int xx, yy;
10605
10606           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
10607           {
10608             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10609               Bang(xx, yy);
10610             else if (Feld[xx][yy] == EL_SP_TERMINAL)
10611               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10612           }
10613         }
10614         else if (IS_BELT_SWITCH(element))
10615         {
10616           ToggleBeltSwitch(x, y);
10617         }
10618         else if (element == EL_SWITCHGATE_SWITCH_UP ||
10619                  element == EL_SWITCHGATE_SWITCH_DOWN)
10620         {
10621           ToggleSwitchgateSwitch(x, y);
10622         }
10623         else if (element == EL_LIGHT_SWITCH ||
10624                  element == EL_LIGHT_SWITCH_ACTIVE)
10625         {
10626           ToggleLightSwitch(x, y);
10627
10628 #if 0
10629           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
10630                          SND_LIGHT_SWITCH_ACTIVATING :
10631                          SND_LIGHT_SWITCH_DEACTIVATING);
10632 #endif
10633         }
10634         else if (element == EL_TIMEGATE_SWITCH)
10635         {
10636           ActivateTimegateSwitch(x, y);
10637         }
10638         else if (element == EL_BALLOON_SWITCH_LEFT ||
10639                  element == EL_BALLOON_SWITCH_RIGHT ||
10640                  element == EL_BALLOON_SWITCH_UP ||
10641                  element == EL_BALLOON_SWITCH_DOWN ||
10642                  element == EL_BALLOON_SWITCH_ANY)
10643         {
10644           if (element == EL_BALLOON_SWITCH_ANY)
10645             game.balloon_dir = move_direction;
10646           else
10647             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
10648                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10649                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
10650                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
10651                                 MV_NO_MOVING);
10652         }
10653         else if (element == EL_LAMP)
10654         {
10655           Feld[x][y] = EL_LAMP_ACTIVE;
10656           local_player->lights_still_needed--;
10657
10658           DrawLevelField(x, y);
10659         }
10660         else if (element == EL_TIME_ORB_FULL)
10661         {
10662           Feld[x][y] = EL_TIME_ORB_EMPTY;
10663           TimeLeft += 10;
10664           DrawGameValue_Time(TimeLeft);
10665
10666           DrawLevelField(x, y);
10667
10668 #if 0
10669           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
10670 #endif
10671         }
10672
10673         return MF_ACTION;
10674       }
10675       else
10676       {
10677         if (!PLAYER_SWITCHING(player, x, y))
10678         {
10679           player->is_switching = TRUE;
10680           player->switch_x = x;
10681           player->switch_y = y;
10682
10683           CheckTriggeredElementChangePlayer(x, y, element,
10684                                             CE_OTHER_IS_SWITCHING,
10685                                             player->index_bit, dig_side);
10686           CheckElementChangePlayer(x, y, element, CE_SWITCHED,
10687                                    player->index_bit, dig_side);
10688         }
10689
10690         CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
10691                                           player->index_bit, dig_side);
10692         CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10693                                  player->index_bit, dig_side);
10694       }
10695
10696       return MF_NO_ACTION;
10697   }
10698
10699   player->push_delay = 0;
10700
10701   if (Feld[x][y] != element)            /* really digged/collected something */
10702     player->is_collecting = !player->is_digging;
10703
10704   return MF_MOVING;
10705 }
10706
10707 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10708 {
10709   int jx = player->jx, jy = player->jy;
10710   int x = jx + dx, y = jy + dy;
10711   int snap_direction = (dx == -1 ? MV_LEFT :
10712                         dx == +1 ? MV_RIGHT :
10713                         dy == -1 ? MV_UP :
10714                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10715
10716 #if 0
10717   if (player->MovPos)
10718     return FALSE;
10719 #else
10720   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
10721     return FALSE;
10722 #endif
10723
10724   if (!player->active || !IN_LEV_FIELD(x, y))
10725     return FALSE;
10726
10727   if (dx && dy)
10728     return FALSE;
10729
10730   if (!dx && !dy)
10731   {
10732     if (player->MovPos == 0)
10733       player->is_pushing = FALSE;
10734
10735     player->is_snapping = FALSE;
10736
10737     if (player->MovPos == 0)
10738     {
10739       player->is_moving = FALSE;
10740       player->is_digging = FALSE;
10741       player->is_collecting = FALSE;
10742     }
10743
10744     return FALSE;
10745   }
10746
10747   if (player->is_snapping)
10748     return FALSE;
10749
10750   player->MovDir = snap_direction;
10751
10752 #if 1
10753   if (player->MovPos == 0)
10754 #endif
10755   {
10756     player->is_moving = FALSE;
10757     player->is_digging = FALSE;
10758     player->is_collecting = FALSE;
10759   }
10760
10761   player->is_dropping = FALSE;
10762
10763   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10764     return FALSE;
10765
10766   player->is_snapping = TRUE;
10767
10768 #if 1
10769   if (player->MovPos == 0)
10770 #endif
10771   {
10772     player->is_moving = FALSE;
10773     player->is_digging = FALSE;
10774     player->is_collecting = FALSE;
10775   }
10776
10777   DrawLevelField(x, y);
10778   BackToFront();
10779
10780   return TRUE;
10781 }
10782
10783 boolean DropElement(struct PlayerInfo *player)
10784 {
10785   static int trigger_sides[4] =
10786   {
10787     CH_SIDE_LEFT,       /* dropping left  */
10788     CH_SIDE_RIGHT,      /* dropping right */
10789     CH_SIDE_TOP,        /* dropping up    */
10790     CH_SIDE_BOTTOM,     /* dropping down  */
10791   };
10792   int jx = player->jx, jy = player->jy;
10793   int drop_direction = player->MovDir;
10794   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
10795   int old_element = Feld[jx][jy];
10796   int drop_element = (player->inventory_size > 0 ?
10797                       player->inventory_element[player->inventory_size - 1] :
10798                       player->inventory_infinite_element != EL_UNDEFINED ?
10799                       player->inventory_infinite_element :
10800                       player->dynabombs_left > 0 ?
10801                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10802                       EL_UNDEFINED);
10803   int new_element = drop_element;       /* default: element does not change */
10804
10805   /* check if player is active, not moving and ready to drop */
10806   if (!player->active || player->MovPos || player->drop_delay > 0)
10807     return FALSE;
10808
10809   /* check if player has anything that can be dropped */
10810 #if 1
10811   if (new_element == EL_UNDEFINED)
10812     return FALSE;
10813 #else
10814   if (player->inventory_size == 0 &&
10815       player->inventory_infinite_element == EL_UNDEFINED &&
10816       player->dynabombs_left == 0)
10817     return FALSE;
10818 #endif
10819
10820   /* check if anything can be dropped at the current position */
10821   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10822     return FALSE;
10823
10824   /* collected custom elements can only be dropped on empty fields */
10825 #if 1
10826   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10827     return FALSE;
10828 #else
10829   if (player->inventory_size > 0 &&
10830       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
10831       && old_element != EL_EMPTY)
10832     return FALSE;
10833 #endif
10834
10835   if (old_element != EL_EMPTY)
10836     Back[jx][jy] = old_element;         /* store old element on this field */
10837
10838   ResetGfxAnimation(jx, jy);
10839   ResetRandomAnimationValue(jx, jy);
10840
10841   if (player->inventory_size > 0 ||
10842       player->inventory_infinite_element != EL_UNDEFINED)
10843   {
10844     if (player->inventory_size > 0)
10845     {
10846       player->inventory_size--;
10847
10848 #if 0
10849       new_element = player->inventory_element[player->inventory_size];
10850 #endif
10851
10852       DrawGameValue_Dynamite(local_player->inventory_size);
10853
10854       if (new_element == EL_DYNAMITE)
10855         new_element = EL_DYNAMITE_ACTIVE;
10856       else if (new_element == EL_SP_DISK_RED)
10857         new_element = EL_SP_DISK_RED_ACTIVE;
10858     }
10859
10860     Feld[jx][jy] = new_element;
10861
10862     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10863       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10864
10865     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10866
10867 #if 1
10868     /* needed if previous element just changed to "empty" in the last frame */
10869     Changed[jx][jy] = 0;                /* allow another change */
10870 #endif
10871
10872     CheckTriggeredElementChangePlayer(jx, jy, new_element,
10873                                       CE_OTHER_GETS_DROPPED,
10874                                       player->index_bit, drop_side);
10875     CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
10876                              player->index_bit, drop_side);
10877
10878     TestIfElementTouchesCustomElement(jx, jy);
10879   }
10880   else          /* player is dropping a dyna bomb */
10881   {
10882     player->dynabombs_left--;
10883
10884 #if 0
10885     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
10886 #endif
10887
10888     Feld[jx][jy] = new_element;
10889
10890     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
10891       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
10892
10893     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
10894   }
10895
10896
10897
10898 #if 1
10899
10900   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
10901   {
10902 #if 1
10903     InitField_WithBug1(jx, jy, FALSE);
10904 #else
10905     InitField(jx, jy, FALSE);
10906     if (CAN_MOVE(Feld[jx][jy]))
10907       InitMovDir(jx, jy);
10908 #endif
10909   }
10910
10911   new_element = Feld[jx][jy];           /* element might have changed */
10912
10913   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10914       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10915   {
10916 #if 0
10917     int move_stepsize = element_info[new_element].move_stepsize;
10918 #endif
10919     int direction, dx, dy, nextx, nexty;
10920
10921     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10922       MovDir[jx][jy] = player->MovDir;
10923
10924     direction = MovDir[jx][jy];
10925     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10926     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10927     nextx = jx + dx;
10928     nexty = jy + dy;
10929
10930     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
10931     {
10932 #if 0
10933       WasJustMoving[jx][jy] = 3;
10934 #else
10935       InitMovingField(jx, jy, direction);
10936       ContinueMoving(jx, jy);
10937 #endif
10938     }
10939     else
10940     {
10941       Changed[jx][jy] = 0;              /* allow another change */
10942
10943 #if 1
10944       TestIfElementHitsCustomElement(jx, jy, direction);
10945 #else
10946       CheckElementChangeSide(jx, jy, new_element, touched_element,
10947                              CE_HITTING_SOMETHING, direction);
10948 #endif
10949     }
10950
10951 #if 0
10952     player->drop_delay = 2 * TILEX / move_stepsize + 1;
10953 #endif
10954   }
10955
10956 #if 0
10957   player->drop_delay = 8 + 8 + 8;
10958 #endif
10959
10960 #if 1
10961   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10962 #endif
10963
10964 #endif
10965
10966   player->is_dropping = TRUE;
10967
10968
10969   return TRUE;
10970 }
10971
10972 /* ------------------------------------------------------------------------- */
10973 /* game sound playing functions                                              */
10974 /* ------------------------------------------------------------------------- */
10975
10976 static int *loop_sound_frame = NULL;
10977 static int *loop_sound_volume = NULL;
10978
10979 void InitPlayLevelSound()
10980 {
10981   int num_sounds = getSoundListSize();
10982
10983   checked_free(loop_sound_frame);
10984   checked_free(loop_sound_volume);
10985
10986   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
10987   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10988 }
10989
10990 static void PlayLevelSound(int x, int y, int nr)
10991 {
10992   int sx = SCREENX(x), sy = SCREENY(y);
10993   int volume, stereo_position;
10994   int max_distance = 8;
10995   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10996
10997   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10998       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10999     return;
11000
11001   if (!IN_LEV_FIELD(x, y) ||
11002       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11003       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11004     return;
11005
11006   volume = SOUND_MAX_VOLUME;
11007
11008   if (!IN_SCR_FIELD(sx, sy))
11009   {
11010     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11011     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11012
11013     volume -= volume * (dx > dy ? dx : dy) / max_distance;
11014   }
11015
11016   stereo_position = (SOUND_MAX_LEFT +
11017                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11018                      (SCR_FIELDX + 2 * max_distance));
11019
11020   if (IS_LOOP_SOUND(nr))
11021   {
11022     /* This assures that quieter loop sounds do not overwrite louder ones,
11023        while restarting sound volume comparison with each new game frame. */
11024
11025     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11026       return;
11027
11028     loop_sound_volume[nr] = volume;
11029     loop_sound_frame[nr] = FrameCounter;
11030   }
11031
11032   PlaySoundExt(nr, volume, stereo_position, type);
11033 }
11034
11035 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11036 {
11037   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11038                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
11039                  y < LEVELY(BY1) ? LEVELY(BY1) :
11040                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
11041                  sound_action);
11042 }
11043
11044 static void PlayLevelSoundAction(int x, int y, int action)
11045 {
11046   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11047 }
11048
11049 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11050 {
11051   int sound_effect = element_info[element].sound[action];
11052
11053   if (sound_effect != SND_UNDEFINED)
11054     PlayLevelSound(x, y, sound_effect);
11055 }
11056
11057 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11058                                               int action)
11059 {
11060   int sound_effect = element_info[element].sound[action];
11061
11062   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11063     PlayLevelSound(x, y, sound_effect);
11064 }
11065
11066 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11067 {
11068   int sound_effect = element_info[Feld[x][y]].sound[action];
11069
11070   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11071     PlayLevelSound(x, y, sound_effect);
11072 }
11073
11074 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11075 {
11076   int sound_effect = element_info[Feld[x][y]].sound[action];
11077
11078   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11079     StopSound(sound_effect);
11080 }
11081
11082 static void PlayLevelMusic()
11083 {
11084   if (levelset.music[level_nr] != MUS_UNDEFINED)
11085     PlayMusic(levelset.music[level_nr]);        /* from config file */
11086   else
11087     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
11088 }
11089
11090 void RaiseScore(int value)
11091 {
11092   local_player->score += value;
11093
11094   DrawGameValue_Score(local_player->score);
11095 }
11096
11097 void RaiseScoreElement(int element)
11098 {
11099   switch(element)
11100   {
11101     case EL_EMERALD:
11102     case EL_BD_DIAMOND:
11103     case EL_EMERALD_YELLOW:
11104     case EL_EMERALD_RED:
11105     case EL_EMERALD_PURPLE:
11106     case EL_SP_INFOTRON:
11107       RaiseScore(level.score[SC_EMERALD]);
11108       break;
11109     case EL_DIAMOND:
11110       RaiseScore(level.score[SC_DIAMOND]);
11111       break;
11112     case EL_CRYSTAL:
11113       RaiseScore(level.score[SC_CRYSTAL]);
11114       break;
11115     case EL_PEARL:
11116       RaiseScore(level.score[SC_PEARL]);
11117       break;
11118     case EL_BUG:
11119     case EL_BD_BUTTERFLY:
11120     case EL_SP_ELECTRON:
11121       RaiseScore(level.score[SC_BUG]);
11122       break;
11123     case EL_SPACESHIP:
11124     case EL_BD_FIREFLY:
11125     case EL_SP_SNIKSNAK:
11126       RaiseScore(level.score[SC_SPACESHIP]);
11127       break;
11128     case EL_YAMYAM:
11129     case EL_DARK_YAMYAM:
11130       RaiseScore(level.score[SC_YAMYAM]);
11131       break;
11132     case EL_ROBOT:
11133       RaiseScore(level.score[SC_ROBOT]);
11134       break;
11135     case EL_PACMAN:
11136       RaiseScore(level.score[SC_PACMAN]);
11137       break;
11138     case EL_NUT:
11139       RaiseScore(level.score[SC_NUT]);
11140       break;
11141     case EL_DYNAMITE:
11142     case EL_SP_DISK_RED:
11143     case EL_DYNABOMB_INCREASE_NUMBER:
11144     case EL_DYNABOMB_INCREASE_SIZE:
11145     case EL_DYNABOMB_INCREASE_POWER:
11146       RaiseScore(level.score[SC_DYNAMITE]);
11147       break;
11148     case EL_SHIELD_NORMAL:
11149     case EL_SHIELD_DEADLY:
11150       RaiseScore(level.score[SC_SHIELD]);
11151       break;
11152     case EL_EXTRA_TIME:
11153       RaiseScore(level.score[SC_TIME_BONUS]);
11154       break;
11155     case EL_KEY_1:
11156     case EL_KEY_2:
11157     case EL_KEY_3:
11158     case EL_KEY_4:
11159       RaiseScore(level.score[SC_KEY]);
11160       break;
11161     default:
11162       RaiseScore(element_info[element].collect_score);
11163       break;
11164   }
11165 }
11166
11167 void RequestQuitGame(boolean ask_if_really_quit)
11168 {
11169   if (AllPlayersGone ||
11170       !ask_if_really_quit ||
11171       level_editor_test_game ||
11172       Request("Do you really want to quit the game ?",
11173               REQ_ASK | REQ_STAY_CLOSED))
11174   {
11175 #if defined(PLATFORM_UNIX)
11176     if (options.network)
11177       SendToServer_StopPlaying();
11178     else
11179 #endif
11180     {
11181       game_status = GAME_MODE_MAIN;
11182       DrawMainMenu();
11183     }
11184   }
11185   else
11186   {
11187
11188 #if 1
11189     if (tape.playing && tape.deactivate_display)
11190       TapeDeactivateDisplayOff(TRUE);
11191 #endif
11192
11193     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11194
11195 #if 1
11196     if (tape.playing && tape.deactivate_display)
11197       TapeDeactivateDisplayOn();
11198 #endif
11199
11200   }
11201 }
11202
11203
11204 /* ---------- new game button stuff ---------------------------------------- */
11205
11206 /* graphic position values for game buttons */
11207 #define GAME_BUTTON_XSIZE       30
11208 #define GAME_BUTTON_YSIZE       30
11209 #define GAME_BUTTON_XPOS        5
11210 #define GAME_BUTTON_YPOS        215
11211 #define SOUND_BUTTON_XPOS       5
11212 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11213
11214 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11215 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11216 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11217 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11218 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11219 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11220
11221 static struct
11222 {
11223   int x, y;
11224   int gadget_id;
11225   char *infotext;
11226 } gamebutton_info[NUM_GAME_BUTTONS] =
11227 {
11228   {
11229     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
11230     GAME_CTRL_ID_STOP,
11231     "stop game"
11232   },
11233   {
11234     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
11235     GAME_CTRL_ID_PAUSE,
11236     "pause game"
11237   },
11238   {
11239     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
11240     GAME_CTRL_ID_PLAY,
11241     "play game"
11242   },
11243   {
11244     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
11245     SOUND_CTRL_ID_MUSIC,
11246     "background music on/off"
11247   },
11248   {
11249     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
11250     SOUND_CTRL_ID_LOOPS,
11251     "sound loops on/off"
11252   },
11253   {
11254     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
11255     SOUND_CTRL_ID_SIMPLE,
11256     "normal sounds on/off"
11257   }
11258 };
11259
11260 void CreateGameButtons()
11261 {
11262   int i;
11263
11264   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11265   {
11266     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11267     struct GadgetInfo *gi;
11268     int button_type;
11269     boolean checked;
11270     unsigned long event_mask;
11271     int gd_xoffset, gd_yoffset;
11272     int gd_x1, gd_x2, gd_y1, gd_y2;
11273     int id = i;
11274
11275     gd_xoffset = gamebutton_info[i].x;
11276     gd_yoffset = gamebutton_info[i].y;
11277     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11278     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11279
11280     if (id == GAME_CTRL_ID_STOP ||
11281         id == GAME_CTRL_ID_PAUSE ||
11282         id == GAME_CTRL_ID_PLAY)
11283     {
11284       button_type = GD_TYPE_NORMAL_BUTTON;
11285       checked = FALSE;
11286       event_mask = GD_EVENT_RELEASED;
11287       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11288       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11289     }
11290     else
11291     {
11292       button_type = GD_TYPE_CHECK_BUTTON;
11293       checked =
11294         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11295          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11296          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11297       event_mask = GD_EVENT_PRESSED;
11298       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
11299       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11300     }
11301
11302     gi = CreateGadget(GDI_CUSTOM_ID, id,
11303                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
11304                       GDI_X, DX + gd_xoffset,
11305                       GDI_Y, DY + gd_yoffset,
11306                       GDI_WIDTH, GAME_BUTTON_XSIZE,
11307                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
11308                       GDI_TYPE, button_type,
11309                       GDI_STATE, GD_BUTTON_UNPRESSED,
11310                       GDI_CHECKED, checked,
11311                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11312                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11313                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11314                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11315                       GDI_EVENT_MASK, event_mask,
11316                       GDI_CALLBACK_ACTION, HandleGameButtons,
11317                       GDI_END);
11318
11319     if (gi == NULL)
11320       Error(ERR_EXIT, "cannot create gadget");
11321
11322     game_gadget[id] = gi;
11323   }
11324 }
11325
11326 void FreeGameButtons()
11327 {
11328   int i;
11329
11330   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11331     FreeGadget(game_gadget[i]);
11332 }
11333
11334 static void MapGameButtons()
11335 {
11336   int i;
11337
11338   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11339     MapGadget(game_gadget[i]);
11340 }
11341
11342 void UnmapGameButtons()
11343 {
11344   int i;
11345
11346   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11347     UnmapGadget(game_gadget[i]);
11348 }
11349
11350 static void HandleGameButtons(struct GadgetInfo *gi)
11351 {
11352   int id = gi->custom_id;
11353
11354   if (game_status != GAME_MODE_PLAYING)
11355     return;
11356
11357   switch (id)
11358   {
11359     case GAME_CTRL_ID_STOP:
11360       RequestQuitGame(TRUE);
11361       break;
11362
11363     case GAME_CTRL_ID_PAUSE:
11364       if (options.network)
11365       {
11366 #if defined(PLATFORM_UNIX)
11367         if (tape.pausing)
11368           SendToServer_ContinuePlaying();
11369         else
11370           SendToServer_PausePlaying();
11371 #endif
11372       }
11373       else
11374         TapeTogglePause(TAPE_TOGGLE_MANUAL);
11375       break;
11376
11377     case GAME_CTRL_ID_PLAY:
11378       if (tape.pausing)
11379       {
11380 #if defined(PLATFORM_UNIX)
11381         if (options.network)
11382           SendToServer_ContinuePlaying();
11383         else
11384 #endif
11385         {
11386           tape.pausing = FALSE;
11387           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11388         }
11389       }
11390       break;
11391
11392     case SOUND_CTRL_ID_MUSIC:
11393       if (setup.sound_music)
11394       { 
11395         setup.sound_music = FALSE;
11396         FadeMusic();
11397       }
11398       else if (audio.music_available)
11399       { 
11400         setup.sound = setup.sound_music = TRUE;
11401
11402         SetAudioMode(setup.sound);
11403
11404         PlayLevelMusic();
11405       }
11406       break;
11407
11408     case SOUND_CTRL_ID_LOOPS:
11409       if (setup.sound_loops)
11410         setup.sound_loops = FALSE;
11411       else if (audio.loops_available)
11412       {
11413         setup.sound = setup.sound_loops = TRUE;
11414         SetAudioMode(setup.sound);
11415       }
11416       break;
11417
11418     case SOUND_CTRL_ID_SIMPLE:
11419       if (setup.sound_simple)
11420         setup.sound_simple = FALSE;
11421       else if (audio.sound_available)
11422       {
11423         setup.sound = setup.sound_simple = TRUE;
11424         SetAudioMode(setup.sound);
11425       }
11426       break;
11427
11428     default:
11429       break;
11430   }
11431 }