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