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