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