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