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