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