rnd-20051120-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF                   (TRUE                           * 1)
32
33 #define USE_NEW_MOVE_STYLE              (TRUE   * USE_NEW_STUFF         * 1)
34 #define USE_NEW_MOVE_DELAY              (TRUE   * USE_NEW_STUFF         * 1)
35 #define USE_NEW_PUSH_DELAY              (TRUE   * USE_NEW_STUFF         * 1)
36 #define USE_NEW_BLOCK_STYLE             (TRUE   * USE_NEW_STUFF         * 1)
37 #define USE_NEW_SP_SLIPPERY             (TRUE   * USE_NEW_STUFF         * 1)
38 #define USE_NEW_RANDOMIZE               (TRUE   * USE_NEW_STUFF         * 1)
39
40 #define USE_CAN_MOVE_NOT_MOVING         (TRUE   * USE_NEW_STUFF         * 1)
41 #define USE_PREVIOUS_MOVE_DIR           (TRUE   * USE_NEW_STUFF         * 1)
42
43 #define USE_PUSH_BUGFIX                 (TRUE   * USE_NEW_STUFF         * 1)
44 #if 0
45 #define USE_BLOCK_DELAY_BUGFIX          (TRUE   * USE_NEW_STUFF         * 1)
46 #endif
47 #define USE_GRAVITY_BUGFIX_NEW          (TRUE   * USE_NEW_STUFF         * 1)
48 #define USE_GRAVITY_BUGFIX_OLD          (TRUE   * USE_NEW_STUFF         * 0)
49
50 #define USE_PENGUIN_COLLECT_BUGFIX      (TRUE   * USE_NEW_STUFF         * 1)
51
52 #define USE_IMPACT_BUGFIX               (TRUE   * USE_NEW_STUFF         * 1)
53
54 #define USE_HITTING_SOMETHING_BUGFIX    (TRUE   * USE_NEW_STUFF         * 1)
55 #define USE_HIT_BY_SOMETHING_BUGFIX     (TRUE   * USE_NEW_STUFF         * 1)
56
57 #define USE_DROP_BUGFIX                 (TRUE   * USE_NEW_STUFF         * 1)
58
59 #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 HandleChangeAction(int change_action)
7863 {
7864   if (change_action == CA_EXIT_PLAYER)
7865   {
7866     printf("::: CA_EXIT_GAME\n");
7867
7868     /* !!! local_player <-> 4 players !!! (EXTEND THIS) !!! */
7869     local_player->LevelSolved = local_player->GameOver = TRUE;
7870   }
7871   else if (change_action == CA_KILL_PLAYER)
7872   {
7873     printf("::: CA_KILL_PLAYER\n");
7874
7875     /* !!! local_player <-> 4 players !!! (EXTEND THIS) !!! */
7876     KillHero(local_player);
7877   }
7878 }
7879
7880 static void ChangeElementNowExt(struct ElementChangeInfo *change,
7881                                 int x, int y, int target_element)
7882 {
7883   int previous_move_direction = MovDir[x][y];
7884 #if 1
7885   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7886                         IS_WALKABLE(Feld[x][y]));
7887 #else
7888   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7889                         IS_WALKABLE(Feld[x][y]) &&
7890                         !IS_MOVING(x, y));
7891 #endif
7892
7893   /* check if element under player changes from accessible to unaccessible
7894      (needed for special case of dropping element which then changes) */
7895   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7896       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7897   {
7898 #if 0
7899     printf("::: BOOOM! [%d, '%s']\n", target_element,
7900            element_info[target_element].token_name);
7901 #endif
7902
7903     Bang(x, y);
7904     return;
7905   }
7906
7907 #if 1
7908   if (!add_player)
7909 #endif
7910   {
7911 #if 1
7912     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7913       RemoveMovingField(x, y);
7914     else
7915       RemoveField(x, y);
7916
7917     Feld[x][y] = target_element;
7918 #else
7919     RemoveField(x, y);
7920     Feld[x][y] = target_element;
7921 #endif
7922
7923     ResetGfxAnimation(x, y);
7924     ResetRandomAnimationValue(x, y);
7925
7926     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7927       MovDir[x][y] = previous_move_direction;
7928
7929 #if 1
7930     InitField_WithBug1(x, y, FALSE);
7931 #else
7932     InitField(x, y, FALSE);
7933     if (CAN_MOVE(Feld[x][y]))
7934       InitMovDir(x, y);
7935 #endif
7936
7937     DrawLevelField(x, y);
7938
7939     if (GFX_CRUMBLED(Feld[x][y]))
7940       DrawLevelFieldCrumbledSandNeighbours(x, y);
7941   }
7942
7943 #if 0
7944   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7945 #endif
7946
7947 #if 0
7948   TestIfBadThingTouchesHero(x, y);
7949   TestIfPlayerTouchesCustomElement(x, y);
7950   TestIfElementTouchesCustomElement(x, y);
7951 #endif
7952
7953   /* "Changed[][]" not set yet to allow "entered by player" change one time */
7954   if (ELEM_IS_PLAYER(target_element))
7955     RelocatePlayer(x, y, target_element);
7956
7957 #if 1
7958   Changed[x][y] = TRUE;         /* ignore all further changes in this frame */
7959 #else
7960   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7961 #endif
7962
7963 #if 1
7964   TestIfBadThingTouchesHero(x, y);
7965   TestIfPlayerTouchesCustomElement(x, y);
7966   TestIfElementTouchesCustomElement(x, y);
7967 #endif
7968
7969   if (change->use_change_action)
7970     HandleChangeAction(change->change_action);
7971 }
7972
7973 static boolean ChangeElementNow(int x, int y, int element, int page)
7974 {
7975   struct ElementChangeInfo *change = &element_info[element].change_page[page];
7976   int target_element;
7977   int old_element = Feld[x][y];
7978
7979   /* always use default change event to prevent running into a loop */
7980   if (ChangeEvent[x][y] == -1)
7981     ChangeEvent[x][y] = CE_DELAY;
7982
7983   if (ChangeEvent[x][y] == CE_DELAY)
7984   {
7985     /* reset actual trigger element and player */
7986     change->actual_trigger_element = EL_EMPTY;
7987     change->actual_trigger_player = EL_PLAYER_1;
7988   }
7989
7990 #if 1
7991   /* do not change any elements that have already changed in this frame */
7992   if (Changed[x][y])
7993     return FALSE;
7994 #else
7995   /* do not change already changed elements with same change event */
7996   if (Changed[x][y] & ChangeEvent[x][y])
7997     return FALSE;
7998 #endif
7999
8000 #if 1
8001   Changed[x][y] = TRUE;         /* ignore all further changes in this frame */
8002 #else
8003   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
8004 #endif
8005
8006 #if 0
8007   /* !!! indirect change before direct change !!! */
8008   CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
8009 #endif
8010
8011   if (change->explode)
8012   {
8013     Bang(x, y);
8014
8015     return TRUE;
8016   }
8017
8018   if (change->use_target_content)
8019   {
8020     boolean complete_replace = TRUE;
8021     boolean can_replace[3][3];
8022     int xx, yy;
8023
8024     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8025     {
8026       boolean is_empty;
8027       boolean is_walkable;
8028       boolean is_diggable;
8029       boolean is_collectible;
8030       boolean is_removable;
8031       boolean is_destructible;
8032       int ex = x + xx - 1;
8033       int ey = y + yy - 1;
8034       int content_element = change->target_content[xx][yy];
8035       int e;
8036
8037       can_replace[xx][yy] = TRUE;
8038
8039       if (ex == x && ey == y)   /* do not check changing element itself */
8040         continue;
8041
8042       if (content_element == EL_EMPTY_SPACE)
8043       {
8044         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8045
8046         continue;
8047       }
8048
8049       if (!IN_LEV_FIELD(ex, ey))
8050       {
8051         can_replace[xx][yy] = FALSE;
8052         complete_replace = FALSE;
8053
8054         continue;
8055       }
8056
8057 #if 0
8058       if (Changed[ex][ey])      /* do not change already changed elements */
8059       {
8060         can_replace[xx][yy] = FALSE;
8061         complete_replace = FALSE;
8062
8063         continue;
8064       }
8065 #endif
8066
8067       e = Feld[ex][ey];
8068
8069       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8070         e = MovingOrBlocked2Element(ex, ey);
8071
8072 #if 1
8073
8074 #if 0
8075       is_empty = (IS_FREE(ex, ey) ||
8076                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8077                   (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8078                    !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8079 #else
8080
8081 #if 0
8082       is_empty = (IS_FREE(ex, ey) ||
8083                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8084 #else
8085       is_empty = (IS_FREE(ex, ey) ||
8086                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8087 #endif
8088
8089 #endif
8090
8091       is_walkable     = (is_empty || IS_WALKABLE(e));
8092       is_diggable     = (is_empty || IS_DIGGABLE(e));
8093       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8094       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8095       is_removable    = (is_diggable || is_collectible);
8096
8097       can_replace[xx][yy] =
8098         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8099           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8100           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8101           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8102           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8103           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8104          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8105
8106       if (!can_replace[xx][yy])
8107         complete_replace = FALSE;
8108 #else
8109       empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8110                                                IS_WALKABLE(content_element)));
8111 #if 1
8112       half_destructible = (empty_for_element || IS_DIGGABLE(e));
8113 #else
8114       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8115 #endif
8116
8117       if ((change->replace_when <= CP_WHEN_EMPTY  && !empty_for_element) ||
8118           (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8119           (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8120       {
8121         can_replace[xx][yy] = FALSE;
8122         complete_replace = FALSE;
8123       }
8124 #endif
8125     }
8126
8127     if (!change->only_if_complete || complete_replace)
8128     {
8129       boolean something_has_changed = FALSE;
8130
8131       if (change->only_if_complete && change->use_random_replace &&
8132           RND(100) < change->random_percentage)
8133         return FALSE;
8134
8135       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8136       {
8137         int ex = x + xx - 1;
8138         int ey = y + yy - 1;
8139         int content_element;
8140
8141         if (can_replace[xx][yy] && (!change->use_random_replace ||
8142                                     RND(100) < change->random_percentage))
8143         {
8144           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8145             RemoveMovingField(ex, ey);
8146
8147           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8148
8149           content_element = change->target_content[xx][yy];
8150           target_element = GET_TARGET_ELEMENT(content_element, change);
8151
8152           ChangeElementNowExt(change, ex, ey, target_element);
8153
8154           something_has_changed = TRUE;
8155
8156           /* for symmetry reasons, freeze newly created border elements */
8157           if (ex != x || ey != y)
8158             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
8159         }
8160       }
8161
8162       if (something_has_changed)
8163         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8164     }
8165   }
8166   else
8167   {
8168     target_element = GET_TARGET_ELEMENT(change->target_element, change);
8169
8170     ChangeElementNowExt(change, x, y, target_element);
8171
8172     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8173   }
8174
8175 #if 1
8176   /* this uses direct change before indirect change */
8177   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8178 #endif
8179
8180   return TRUE;
8181 }
8182
8183 static void ChangeElement(int x, int y, int page)
8184 {
8185   int element = MovingOrBlocked2Element(x, y);
8186   struct ElementInfo *ei = &element_info[element];
8187   struct ElementChangeInfo *change = &ei->change_page[page];
8188
8189 #ifdef DEBUG
8190   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8191   {
8192     printf("\n\n");
8193     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8194            x, y, element, element_info[element].token_name);
8195     printf("ChangeElement(): This should never happen!\n");
8196     printf("\n\n");
8197   }
8198 #endif
8199
8200   /* this can happen with classic bombs on walkable, changing elements */
8201   if (!CAN_CHANGE(element))
8202   {
8203 #if 0
8204     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8205       ChangeDelay[x][y] = 0;
8206 #endif
8207
8208     return;
8209   }
8210
8211   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8212   {
8213     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
8214                          RND(change->delay_random * change->delay_frames)) + 1;
8215
8216     ResetGfxAnimation(x, y);
8217     ResetRandomAnimationValue(x, y);
8218
8219     if (change->pre_change_function)
8220       change->pre_change_function(x, y);
8221   }
8222
8223   ChangeDelay[x][y]--;
8224
8225   if (ChangeDelay[x][y] != 0)           /* continue element change */
8226   {
8227     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8228
8229     if (IS_ANIMATED(graphic))
8230       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8231
8232     if (change->change_function)
8233       change->change_function(x, y);
8234   }
8235   else                                  /* finish element change */
8236   {
8237     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8238     {
8239       page = ChangePage[x][y];
8240       ChangePage[x][y] = -1;
8241
8242       change = &ei->change_page[page];
8243     }
8244
8245 #if 0
8246     if (IS_MOVING(x, y) && !change->explode)
8247 #else
8248     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8249 #endif
8250     {
8251       ChangeDelay[x][y] = 1;            /* try change after next move step */
8252       ChangePage[x][y] = page;          /* remember page to use for change */
8253
8254       return;
8255     }
8256
8257     if (ChangeElementNow(x, y, element, page))
8258     {
8259       if (change->post_change_function)
8260         change->post_change_function(x, y);
8261     }
8262   }
8263 }
8264
8265 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8266                                               int trigger_element,
8267                                               int trigger_event,
8268                                               int trigger_player,
8269                                               int trigger_side,
8270                                               int trigger_page)
8271 {
8272   int i, j, x, y;
8273   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8274
8275   if (!(trigger_events[trigger_element][trigger_event]))
8276     return FALSE;
8277
8278   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8279   {
8280     int element = EL_CUSTOM_START + i;
8281
8282     boolean change_element = FALSE;
8283     int page = 0;
8284
8285     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8286       continue;
8287
8288     for (j = 0; j < element_info[element].num_change_pages; j++)
8289     {
8290       struct ElementChangeInfo *change = &element_info[element].change_page[j];
8291
8292       if (change->can_change &&
8293           change->has_event[trigger_event] &&
8294           change->trigger_side & trigger_side &&
8295           change->trigger_player & trigger_player &&
8296           change->trigger_page & trigger_page_bits &&
8297           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8298       {
8299 #if 0
8300         if (!(change->has_event[trigger_event]))
8301           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8302                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8303 #endif
8304
8305         change_element = TRUE;
8306         page = j;
8307
8308         change->actual_trigger_element = trigger_element;
8309         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8310
8311         break;
8312       }
8313     }
8314
8315     if (!change_element)
8316       continue;
8317
8318     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8319     {
8320 #if 0
8321       if (x == lx && y == ly)   /* do not change trigger element itself */
8322         continue;
8323 #endif
8324
8325       if (Feld[x][y] == element)
8326       {
8327         ChangeDelay[x][y] = 1;
8328         ChangeEvent[x][y] = trigger_event;
8329         ChangeElement(x, y, page);
8330       }
8331     }
8332   }
8333
8334   return TRUE;
8335 }
8336
8337 static boolean CheckElementChangeExt(int x, int y,
8338                                      int element,
8339                                      int trigger_element,
8340                                      int trigger_event,
8341                                      int trigger_player,
8342                                      int trigger_side,
8343                                      int trigger_page)
8344 {
8345   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8346     return FALSE;
8347
8348   if (Feld[x][y] == EL_BLOCKED)
8349   {
8350     Blocked2Moving(x, y, &x, &y);
8351     element = Feld[x][y];
8352   }
8353
8354 #if 1
8355   if (Feld[x][y] != element)    /* check if element has already changed */
8356   {
8357 #if 0
8358     printf("::: %d ('%s') != %d ('%s') [%d]\n",
8359            Feld[x][y], element_info[Feld[x][y]].token_name,
8360            element, element_info[element].token_name,
8361            trigger_event);
8362 #endif
8363
8364     return FALSE;
8365   }
8366 #endif
8367
8368 #if 1
8369   if (trigger_page < 0)
8370   {
8371     boolean change_element = FALSE;
8372     int i;
8373
8374     for (i = 0; i < element_info[element].num_change_pages; i++)
8375     {
8376       struct ElementChangeInfo *change = &element_info[element].change_page[i];
8377
8378       if (change->can_change &&
8379           change->has_event[trigger_event] &&
8380           change->trigger_side & trigger_side &&
8381           change->trigger_player & trigger_player)
8382       {
8383         change_element = TRUE;
8384         trigger_page = i;
8385
8386         change->actual_trigger_element = trigger_element;
8387         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8388
8389         break;
8390       }
8391     }
8392
8393     if (!change_element)
8394       return FALSE;
8395   }
8396   else
8397   {
8398     struct ElementInfo *ei = &element_info[element];
8399     struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8400
8401     change->actual_trigger_element = trigger_element;
8402     change->actual_trigger_player = EL_PLAYER_1;        /* unused */
8403   }
8404
8405 #else
8406
8407   /* !!! this check misses pages with same event, but different side !!! */
8408
8409   if (trigger_page < 0)
8410     trigger_page = element_info[element].event_page_nr[trigger_event];
8411
8412   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8413     return FALSE;
8414 #endif
8415
8416   ChangeDelay[x][y] = 1;
8417   ChangeEvent[x][y] = trigger_event;
8418   ChangeElement(x, y, trigger_page);
8419
8420   return TRUE;
8421 }
8422
8423 static void PlayPlayerSound(struct PlayerInfo *player)
8424 {
8425   int jx = player->jx, jy = player->jy;
8426   int element = player->element_nr;
8427   int last_action = player->last_action_waiting;
8428   int action = player->action_waiting;
8429
8430   if (player->is_waiting)
8431   {
8432     if (action != last_action)
8433       PlayLevelSoundElementAction(jx, jy, element, action);
8434     else
8435       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8436   }
8437   else
8438   {
8439     if (action != last_action)
8440       StopSound(element_info[element].sound[last_action]);
8441
8442     if (last_action == ACTION_SLEEPING)
8443       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8444   }
8445 }
8446
8447 static void PlayAllPlayersSound()
8448 {
8449   int i;
8450
8451   for (i = 0; i < MAX_PLAYERS; i++)
8452     if (stored_player[i].active)
8453       PlayPlayerSound(&stored_player[i]);
8454 }
8455
8456 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8457 {
8458   boolean last_waiting = player->is_waiting;
8459   int move_dir = player->MovDir;
8460
8461   player->last_action_waiting = player->action_waiting;
8462
8463   if (is_waiting)
8464   {
8465     if (!last_waiting)          /* not waiting -> waiting */
8466     {
8467       player->is_waiting = TRUE;
8468
8469       player->frame_counter_bored =
8470         FrameCounter +
8471         game.player_boring_delay_fixed +
8472         SimpleRND(game.player_boring_delay_random);
8473       player->frame_counter_sleeping =
8474         FrameCounter +
8475         game.player_sleeping_delay_fixed +
8476         SimpleRND(game.player_sleeping_delay_random);
8477
8478       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8479     }
8480
8481     if (game.player_sleeping_delay_fixed +
8482         game.player_sleeping_delay_random > 0 &&
8483         player->anim_delay_counter == 0 &&
8484         player->post_delay_counter == 0 &&
8485         FrameCounter >= player->frame_counter_sleeping)
8486       player->is_sleeping = TRUE;
8487     else if (game.player_boring_delay_fixed +
8488              game.player_boring_delay_random > 0 &&
8489              FrameCounter >= player->frame_counter_bored)
8490       player->is_bored = TRUE;
8491
8492     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8493                               player->is_bored ? ACTION_BORING :
8494                               ACTION_WAITING);
8495
8496     if (player->is_sleeping)
8497     {
8498       if (player->num_special_action_sleeping > 0)
8499       {
8500         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8501         {
8502           int last_special_action = player->special_action_sleeping;
8503           int num_special_action = player->num_special_action_sleeping;
8504           int special_action =
8505             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8506              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8507              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8508              last_special_action + 1 : ACTION_SLEEPING);
8509           int special_graphic =
8510             el_act_dir2img(player->element_nr, special_action, move_dir);
8511
8512           player->anim_delay_counter =
8513             graphic_info[special_graphic].anim_delay_fixed +
8514             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8515           player->post_delay_counter =
8516             graphic_info[special_graphic].post_delay_fixed +
8517             SimpleRND(graphic_info[special_graphic].post_delay_random);
8518
8519           player->special_action_sleeping = special_action;
8520         }
8521
8522         if (player->anim_delay_counter > 0)
8523         {
8524           player->action_waiting = player->special_action_sleeping;
8525           player->anim_delay_counter--;
8526         }
8527         else if (player->post_delay_counter > 0)
8528         {
8529           player->post_delay_counter--;
8530         }
8531       }
8532     }
8533     else if (player->is_bored)
8534     {
8535       if (player->num_special_action_bored > 0)
8536       {
8537         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8538         {
8539           int special_action =
8540             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8541           int special_graphic =
8542             el_act_dir2img(player->element_nr, special_action, move_dir);
8543
8544           player->anim_delay_counter =
8545             graphic_info[special_graphic].anim_delay_fixed +
8546             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8547           player->post_delay_counter =
8548             graphic_info[special_graphic].post_delay_fixed +
8549             SimpleRND(graphic_info[special_graphic].post_delay_random);
8550
8551           player->special_action_bored = special_action;
8552         }
8553
8554         if (player->anim_delay_counter > 0)
8555         {
8556           player->action_waiting = player->special_action_bored;
8557           player->anim_delay_counter--;
8558         }
8559         else if (player->post_delay_counter > 0)
8560         {
8561           player->post_delay_counter--;
8562         }
8563       }
8564     }
8565   }
8566   else if (last_waiting)        /* waiting -> not waiting */
8567   {
8568     player->is_waiting = FALSE;
8569     player->is_bored = FALSE;
8570     player->is_sleeping = FALSE;
8571
8572     player->frame_counter_bored = -1;
8573     player->frame_counter_sleeping = -1;
8574
8575     player->anim_delay_counter = 0;
8576     player->post_delay_counter = 0;
8577
8578     player->action_waiting = ACTION_DEFAULT;
8579
8580     player->special_action_bored = ACTION_DEFAULT;
8581     player->special_action_sleeping = ACTION_DEFAULT;
8582   }
8583 }
8584
8585 #if 1
8586 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8587 {
8588 #if 0
8589   static byte stored_player_action[MAX_PLAYERS];
8590   static int num_stored_actions = 0;
8591 #endif
8592   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8593   int left      = player_action & JOY_LEFT;
8594   int right     = player_action & JOY_RIGHT;
8595   int up        = player_action & JOY_UP;
8596   int down      = player_action & JOY_DOWN;
8597   int button1   = player_action & JOY_BUTTON_1;
8598   int button2   = player_action & JOY_BUTTON_2;
8599   int dx        = (left ? -1    : right ? 1     : 0);
8600   int dy        = (up   ? -1    : down  ? 1     : 0);
8601
8602 #if 0
8603   stored_player_action[player->index_nr] = 0;
8604   num_stored_actions++;
8605 #endif
8606
8607 #if 0
8608   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8609 #endif
8610
8611   if (!player->active || tape.pausing)
8612     return 0;
8613
8614 #if 0
8615   printf("::: [%d %d %d %d] [%d %d]\n",
8616          left, right, up, down, button1, button2);
8617 #endif
8618
8619   if (player_action)
8620   {
8621 #if 0
8622     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8623 #endif
8624
8625 #if 0
8626     /* !!! TEST !!! */
8627     if (player->MovPos == 0)
8628       CheckGravityMovement(player);
8629 #endif
8630     if (button1)
8631       snapped = SnapField(player, dx, dy);
8632     else
8633     {
8634       if (button2)
8635         dropped = DropElement(player);
8636
8637       moved = MovePlayer(player, dx, dy);
8638     }
8639
8640     if (tape.single_step && tape.recording && !tape.pausing)
8641     {
8642       if (button1 || (dropped && !moved))
8643       {
8644         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8645         SnapField(player, 0, 0);                /* stop snapping */
8646       }
8647     }
8648
8649     SetPlayerWaiting(player, FALSE);
8650
8651 #if 1
8652     return player_action;
8653 #else
8654     stored_player_action[player->index_nr] = player_action;
8655 #endif
8656   }
8657   else
8658   {
8659 #if 0
8660     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8661 #endif
8662
8663     /* no actions for this player (no input at player's configured device) */
8664
8665     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8666     SnapField(player, 0, 0);
8667     CheckGravityMovementWhenNotMoving(player);
8668
8669     if (player->MovPos == 0)
8670       SetPlayerWaiting(player, TRUE);
8671
8672     if (player->MovPos == 0)    /* needed for tape.playing */
8673       player->is_moving = FALSE;
8674
8675     player->is_dropping = FALSE;
8676
8677     return 0;
8678   }
8679
8680 #if 0
8681   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8682   {
8683     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8684
8685     TapeRecordAction(stored_player_action);
8686     num_stored_actions = 0;
8687   }
8688 #endif
8689 }
8690
8691 #else
8692
8693 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8694 {
8695   static byte stored_player_action[MAX_PLAYERS];
8696   static int num_stored_actions = 0;
8697   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8698   int left      = player_action & JOY_LEFT;
8699   int right     = player_action & JOY_RIGHT;
8700   int up        = player_action & JOY_UP;
8701   int down      = player_action & JOY_DOWN;
8702   int button1   = player_action & JOY_BUTTON_1;
8703   int button2   = player_action & JOY_BUTTON_2;
8704   int dx        = (left ? -1    : right ? 1     : 0);
8705   int dy        = (up   ? -1    : down  ? 1     : 0);
8706
8707   stored_player_action[player->index_nr] = 0;
8708   num_stored_actions++;
8709
8710   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8711
8712   if (!player->active || tape.pausing)
8713     return;
8714
8715   if (player_action)
8716   {
8717     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8718
8719     if (button1)
8720       snapped = SnapField(player, dx, dy);
8721     else
8722     {
8723       if (button2)
8724         dropped = DropElement(player);
8725
8726       moved = MovePlayer(player, dx, dy);
8727     }
8728
8729     if (tape.single_step && tape.recording && !tape.pausing)
8730     {
8731       if (button1 || (dropped && !moved))
8732       {
8733         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8734         SnapField(player, 0, 0);                /* stop snapping */
8735       }
8736     }
8737
8738     stored_player_action[player->index_nr] = player_action;
8739   }
8740   else
8741   {
8742     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8743
8744     /* no actions for this player (no input at player's configured device) */
8745
8746     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8747     SnapField(player, 0, 0);
8748     CheckGravityMovementWhenNotMoving(player);
8749
8750     if (player->MovPos == 0)
8751       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8752
8753     if (player->MovPos == 0)    /* needed for tape.playing */
8754       player->is_moving = FALSE;
8755   }
8756
8757   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8758   {
8759     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8760
8761     TapeRecordAction(stored_player_action);
8762     num_stored_actions = 0;
8763   }
8764 }
8765 #endif
8766
8767 void AdvanceFrameAndPlayerCounters(int player_nr)
8768 {
8769   int i;
8770
8771   /* advance frame counters (global frame counter and time frame counter) */
8772   FrameCounter++;
8773   TimeFrames++;
8774
8775   /* advance player counters (counters for move delay, move animation etc.) */
8776   for (i = 0; i < MAX_PLAYERS; i++)
8777   {
8778     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8779     int move_frames =
8780       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
8781
8782     if (!advance_player_counters)       /* not all players may be affected */
8783       continue;
8784
8785     stored_player[i].Frame += move_frames;
8786
8787     if (stored_player[i].MovPos != 0)
8788       stored_player[i].StepFrame += move_frames;
8789
8790 #if USE_NEW_MOVE_DELAY
8791     if (stored_player[i].move_delay > 0)
8792       stored_player[i].move_delay--;
8793 #endif
8794
8795 #if USE_NEW_PUSH_DELAY
8796     /* due to bugs in previous versions, counter must count up, not down */
8797     if (stored_player[i].push_delay != -1)
8798       stored_player[i].push_delay++;
8799 #endif
8800
8801     if (stored_player[i].drop_delay > 0)
8802       stored_player[i].drop_delay--;
8803   }
8804 }
8805
8806 void GameActions()
8807 {
8808   static unsigned long game_frame_delay = 0;
8809   unsigned long game_frame_delay_value;
8810   int magic_wall_x = 0, magic_wall_y = 0;
8811   int i, x, y, element, graphic;
8812   byte *recorded_player_action;
8813   byte summarized_player_action = 0;
8814 #if 1
8815   byte tape_action[MAX_PLAYERS];
8816 #endif
8817
8818   if (game_status != GAME_MODE_PLAYING)
8819     return;
8820
8821   game_frame_delay_value =
8822     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8823
8824   if (tape.playing && tape.warp_forward && !tape.pausing)
8825     game_frame_delay_value = 0;
8826
8827   /* ---------- main game synchronization point ---------- */
8828
8829   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8830
8831   if (network_playing && !network_player_action_received)
8832   {
8833     /*
8834 #ifdef DEBUG
8835     printf("DEBUG: try to get network player actions in time\n");
8836 #endif
8837     */
8838
8839 #if defined(NETWORK_AVALIABLE)
8840     /* last chance to get network player actions without main loop delay */
8841     HandleNetworking();
8842 #endif
8843
8844     if (game_status != GAME_MODE_PLAYING)
8845       return;
8846
8847     if (!network_player_action_received)
8848     {
8849       /*
8850 #ifdef DEBUG
8851       printf("DEBUG: failed to get network player actions in time\n");
8852 #endif
8853       */
8854       return;
8855     }
8856   }
8857
8858   if (tape.pausing)
8859     return;
8860
8861 #if 0
8862   printf("::: getting new tape action [%d]\n", FrameCounter);
8863 #endif
8864
8865   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8866
8867 #if 1
8868   /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8869   if (recorded_player_action == NULL && tape.pausing)
8870     return;
8871 #endif
8872
8873 #if 0
8874   printf("::: %d\n", stored_player[0].action);
8875 #endif
8876
8877 #if 0
8878   if (recorded_player_action != NULL)
8879     for (i = 0; i < MAX_PLAYERS; i++)
8880       stored_player[i].action = recorded_player_action[i];
8881 #endif
8882
8883   for (i = 0; i < MAX_PLAYERS; i++)
8884   {
8885     summarized_player_action |= stored_player[i].action;
8886
8887     if (!network_playing)
8888       stored_player[i].effective_action = stored_player[i].action;
8889   }
8890
8891 #if defined(NETWORK_AVALIABLE)
8892   if (network_playing)
8893     SendToServer_MovePlayer(summarized_player_action);
8894 #endif
8895
8896   if (!options.network && !setup.team_mode)
8897     local_player->effective_action = summarized_player_action;
8898
8899 #if 1
8900   if (recorded_player_action != NULL)
8901     for (i = 0; i < MAX_PLAYERS; i++)
8902       stored_player[i].effective_action = recorded_player_action[i];
8903 #endif
8904
8905 #if 1
8906   for (i = 0; i < MAX_PLAYERS; i++)
8907   {
8908     tape_action[i] = stored_player[i].effective_action;
8909
8910     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8911       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8912   }
8913
8914   /* only save actions from input devices, but not programmed actions */
8915   if (tape.recording)
8916     TapeRecordAction(tape_action);
8917 #endif
8918
8919   for (i = 0; i < MAX_PLAYERS; i++)
8920   {
8921     int actual_player_action = stored_player[i].effective_action;
8922
8923 #if 1
8924     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8925        - rnd_equinox_tetrachloride 048
8926        - rnd_equinox_tetrachloride_ii 096
8927        - rnd_emanuel_schmieg 002
8928        - doctor_sloan_ww 001, 020
8929     */
8930     if (stored_player[i].MovPos == 0)
8931       CheckGravityMovement(&stored_player[i]);
8932 #endif
8933
8934 #if 1
8935     /* overwrite programmed action with tape action */
8936     if (stored_player[i].programmed_action)
8937       actual_player_action = stored_player[i].programmed_action;
8938 #endif
8939
8940 #if 0
8941     if (stored_player[i].programmed_action)
8942       printf("::: %d\n", stored_player[i].programmed_action);
8943 #endif
8944
8945     if (recorded_player_action)
8946     {
8947 #if 0
8948       if (stored_player[i].programmed_action &&
8949           stored_player[i].programmed_action != recorded_player_action[i])
8950         printf("::: %d: %d <-> %d\n", i,
8951                stored_player[i].programmed_action, recorded_player_action[i]);
8952 #endif
8953
8954 #if 0
8955       actual_player_action = recorded_player_action[i];
8956 #endif
8957     }
8958
8959 #if 0
8960     /* overwrite tape action with programmed action */
8961     if (stored_player[i].programmed_action)
8962       actual_player_action = stored_player[i].programmed_action;
8963 #endif
8964
8965 #if 0
8966     if (i == 0)
8967       printf("::: action: %d: %x [%d]\n",
8968              stored_player[i].MovPos, actual_player_action, FrameCounter);
8969 #endif
8970
8971 #if 1
8972     PlayerActions(&stored_player[i], actual_player_action);
8973 #else
8974     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8975
8976     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8977       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8978 #endif
8979
8980     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8981   }
8982
8983 #if 0
8984   if (tape.recording)
8985     TapeRecordAction(tape_action);
8986 #endif
8987
8988   network_player_action_received = FALSE;
8989
8990   ScrollScreen(NULL, SCROLL_GO_ON);
8991
8992 #if 0
8993   FrameCounter++;
8994   TimeFrames++;
8995
8996   for (i = 0; i < MAX_PLAYERS; i++)
8997     stored_player[i].Frame++;
8998 #endif
8999
9000 #if 1
9001   /* for backwards compatibility, the following code emulates a fixed bug that
9002      occured when pushing elements (causing elements that just made their last
9003      pushing step to already (if possible) make their first falling step in the
9004      same game frame, which is bad); this code is also needed to use the famous
9005      "spring push bug" which is used in older levels and might be wanted to be
9006      used also in newer levels, but in this case the buggy pushing code is only
9007      affecting the "spring" element and no other elements */
9008
9009 #if 1
9010   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9011 #else
9012   if (game.engine_version < VERSION_IDENT(2,2,0,7))
9013 #endif
9014   {
9015     for (i = 0; i < MAX_PLAYERS; i++)
9016     {
9017       struct PlayerInfo *player = &stored_player[i];
9018       int x = player->jx;
9019       int y = player->jy;
9020
9021 #if 1
9022       if (player->active && player->is_pushing && player->is_moving &&
9023           IS_MOVING(x, y) &&
9024           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9025            Feld[x][y] == EL_SPRING))
9026 #else
9027       if (player->active && player->is_pushing && player->is_moving &&
9028           IS_MOVING(x, y))
9029 #endif
9030       {
9031         ContinueMoving(x, y);
9032
9033         /* continue moving after pushing (this is actually a bug) */
9034         if (!IS_MOVING(x, y))
9035         {
9036           Stop[x][y] = FALSE;
9037         }
9038       }
9039     }
9040   }
9041 #endif
9042
9043   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9044   {
9045     Changed[x][y] = FALSE;
9046     ChangeEvent[x][y] = -1;
9047
9048 #if USE_NEW_BLOCK_STYLE
9049     /* this must be handled before main playfield loop */
9050     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9051     {
9052       MovDelay[x][y]--;
9053       if (MovDelay[x][y] <= 0)
9054         RemoveField(x, y);
9055     }
9056 #endif
9057
9058 #if DEBUG
9059     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9060     {
9061       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9062       printf("GameActions(): This should never happen!\n");
9063
9064       ChangePage[x][y] = -1;
9065     }
9066 #endif
9067
9068     Stop[x][y] = FALSE;
9069     if (WasJustMoving[x][y] > 0)
9070       WasJustMoving[x][y]--;
9071     if (WasJustFalling[x][y] > 0)
9072       WasJustFalling[x][y]--;
9073     if (CheckCollision[x][y] > 0)
9074       CheckCollision[x][y]--;
9075
9076     GfxFrame[x][y]++;
9077
9078 #if 1
9079     /* reset finished pushing action (not done in ContinueMoving() to allow
9080        continous pushing animation for elements with zero push delay) */
9081     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9082     {
9083       ResetGfxAnimation(x, y);
9084       DrawLevelField(x, y);
9085     }
9086 #endif
9087
9088 #if DEBUG
9089     if (IS_BLOCKED(x, y))
9090     {
9091       int oldx, oldy;
9092
9093       Blocked2Moving(x, y, &oldx, &oldy);
9094       if (!IS_MOVING(oldx, oldy))
9095       {
9096         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9097         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9098         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9099         printf("GameActions(): This should never happen!\n");
9100       }
9101     }
9102 #endif
9103   }
9104
9105   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9106   {
9107     element = Feld[x][y];
9108 #if 1
9109     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9110 #else
9111     graphic = el2img(element);
9112 #endif
9113
9114 #if 0
9115     if (element == -1)
9116     {
9117       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9118
9119       element = graphic = 0;
9120     }
9121 #endif
9122
9123     if (graphic_info[graphic].anim_global_sync)
9124       GfxFrame[x][y] = FrameCounter;
9125
9126     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9127         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9128       ResetRandomAnimationValue(x, y);
9129
9130     SetRandomAnimationValue(x, y);
9131
9132 #if 1
9133     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9134 #endif
9135
9136     if (IS_INACTIVE(element))
9137     {
9138       if (IS_ANIMATED(graphic))
9139         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9140
9141       continue;
9142     }
9143
9144 #if 1
9145     /* this may take place after moving, so 'element' may have changed */
9146 #if 0
9147     if (IS_CHANGING(x, y))
9148 #else
9149     if (IS_CHANGING(x, y) &&
9150         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9151 #endif
9152     {
9153 #if 0
9154       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9155                     element_info[element].event_page_nr[CE_DELAY]);
9156 #else
9157       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9158 #endif
9159
9160       element = Feld[x][y];
9161       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9162     }
9163 #endif
9164
9165     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9166     {
9167       StartMoving(x, y);
9168
9169 #if 1
9170       element = Feld[x][y];
9171       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9172 #if 0
9173       if (element == EL_MOLE)
9174         printf("::: %d, %d, %d [%d]\n",
9175                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9176                GfxAction[x][y]);
9177 #endif
9178 #if 0
9179       if (element == EL_YAMYAM)
9180         printf("::: %d, %d, %d\n",
9181                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9182 #endif
9183 #endif
9184
9185       if (IS_ANIMATED(graphic) &&
9186           !IS_MOVING(x, y) &&
9187           !Stop[x][y])
9188       {
9189         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9190
9191 #if 0
9192         if (element == EL_BUG)
9193           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9194 #endif
9195
9196 #if 0
9197         if (element == EL_MOLE)
9198           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9199 #endif
9200       }
9201
9202       if (IS_GEM(element) || element == EL_SP_INFOTRON)
9203         EdelsteinFunkeln(x, y);
9204     }
9205     else if ((element == EL_ACID ||
9206               element == EL_EXIT_OPEN ||
9207               element == EL_SP_EXIT_OPEN ||
9208               element == EL_SP_TERMINAL ||
9209               element == EL_SP_TERMINAL_ACTIVE ||
9210               element == EL_EXTRA_TIME ||
9211               element == EL_SHIELD_NORMAL ||
9212               element == EL_SHIELD_DEADLY) &&
9213              IS_ANIMATED(graphic))
9214       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9215     else if (IS_MOVING(x, y))
9216       ContinueMoving(x, y);
9217     else if (IS_ACTIVE_BOMB(element))
9218       CheckDynamite(x, y);
9219 #if 0
9220     else if (element == EL_EXPLOSION && !game.explosions_delayed)
9221       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9222 #endif
9223     else if (element == EL_AMOEBA_GROWING)
9224       AmoebeWaechst(x, y);
9225     else if (element == EL_AMOEBA_SHRINKING)
9226       AmoebaDisappearing(x, y);
9227
9228 #if !USE_NEW_AMOEBA_CODE
9229     else if (IS_AMOEBALIVE(element))
9230       AmoebeAbleger(x, y);
9231 #endif
9232
9233     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9234       Life(x, y);
9235     else if (element == EL_EXIT_CLOSED)
9236       CheckExit(x, y);
9237     else if (element == EL_SP_EXIT_CLOSED)
9238       CheckExitSP(x, y);
9239     else if (element == EL_EXPANDABLE_WALL_GROWING)
9240       MauerWaechst(x, y);
9241     else if (element == EL_EXPANDABLE_WALL ||
9242              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9243              element == EL_EXPANDABLE_WALL_VERTICAL ||
9244              element == EL_EXPANDABLE_WALL_ANY)
9245       MauerAbleger(x, y);
9246     else if (element == EL_FLAMES)
9247       CheckForDragon(x, y);
9248 #if 0
9249     else if (IS_AUTO_CHANGING(element))
9250       ChangeElement(x, y);
9251 #endif
9252     else if (element == EL_EXPLOSION)
9253       ; /* drawing of correct explosion animation is handled separately */
9254     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9255       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9256
9257 #if 0
9258     /* this may take place after moving, so 'element' may have changed */
9259     if (IS_AUTO_CHANGING(Feld[x][y]))
9260       ChangeElement(x, y);
9261 #endif
9262
9263     if (IS_BELT_ACTIVE(element))
9264       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9265
9266     if (game.magic_wall_active)
9267     {
9268       int jx = local_player->jx, jy = local_player->jy;
9269
9270       /* play the element sound at the position nearest to the player */
9271       if ((element == EL_MAGIC_WALL_FULL ||
9272            element == EL_MAGIC_WALL_ACTIVE ||
9273            element == EL_MAGIC_WALL_EMPTYING ||
9274            element == EL_BD_MAGIC_WALL_FULL ||
9275            element == EL_BD_MAGIC_WALL_ACTIVE ||
9276            element == EL_BD_MAGIC_WALL_EMPTYING) &&
9277           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9278       {
9279         magic_wall_x = x;
9280         magic_wall_y = y;
9281       }
9282     }
9283   }
9284
9285 #if USE_NEW_AMOEBA_CODE
9286   /* new experimental amoeba growth stuff */
9287 #if 1
9288   if (!(FrameCounter % 8))
9289 #endif
9290   {
9291     static unsigned long random = 1684108901;
9292
9293     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9294     {
9295 #if 0
9296       x = (random >> 10) % lev_fieldx;
9297       y = (random >> 20) % lev_fieldy;
9298 #else
9299       x = RND(lev_fieldx);
9300       y = RND(lev_fieldy);
9301 #endif
9302       element = Feld[x][y];
9303
9304 #if 1
9305       if (!IS_PLAYER(x,y) &&
9306           (element == EL_EMPTY ||
9307            CAN_GROW_INTO(element) ||
9308            element == EL_QUICKSAND_EMPTY ||
9309            element == EL_ACID_SPLASH_LEFT ||
9310            element == EL_ACID_SPLASH_RIGHT))
9311       {
9312         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9313             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9314             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9315             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9316           Feld[x][y] = EL_AMOEBA_DROP;
9317       }
9318 #else
9319       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9320       if (!IS_PLAYER(x,y) &&
9321           (element == EL_EMPTY ||
9322            element == EL_SAND ||
9323            element == EL_QUICKSAND_EMPTY ||
9324            element == EL_ACID_SPLASH_LEFT ||
9325            element == EL_ACID_SPLASH_RIGHT))
9326       {
9327         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9328             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9329             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9330             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9331           Feld[x][y] = EL_AMOEBA_DROP;
9332       }
9333 #endif
9334
9335       random = random * 129 + 1;
9336     }
9337   }
9338 #endif
9339
9340 #if 0
9341   if (game.explosions_delayed)
9342 #endif
9343   {
9344     game.explosions_delayed = FALSE;
9345
9346     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9347     {
9348       element = Feld[x][y];
9349
9350       if (ExplodeField[x][y])
9351         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9352       else if (element == EL_EXPLOSION)
9353         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9354
9355       ExplodeField[x][y] = EX_TYPE_NONE;
9356     }
9357
9358     game.explosions_delayed = TRUE;
9359   }
9360
9361   if (game.magic_wall_active)
9362   {
9363     if (!(game.magic_wall_time_left % 4))
9364     {
9365       int element = Feld[magic_wall_x][magic_wall_y];
9366
9367       if (element == EL_BD_MAGIC_WALL_FULL ||
9368           element == EL_BD_MAGIC_WALL_ACTIVE ||
9369           element == EL_BD_MAGIC_WALL_EMPTYING)
9370         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9371       else
9372         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9373     }
9374
9375     if (game.magic_wall_time_left > 0)
9376     {
9377       game.magic_wall_time_left--;
9378       if (!game.magic_wall_time_left)
9379       {
9380         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9381         {
9382           element = Feld[x][y];
9383
9384           if (element == EL_MAGIC_WALL_ACTIVE ||
9385               element == EL_MAGIC_WALL_FULL)
9386           {
9387             Feld[x][y] = EL_MAGIC_WALL_DEAD;
9388             DrawLevelField(x, y);
9389           }
9390           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9391                    element == EL_BD_MAGIC_WALL_FULL)
9392           {
9393             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9394             DrawLevelField(x, y);
9395           }
9396         }
9397
9398         game.magic_wall_active = FALSE;
9399       }
9400     }
9401   }
9402
9403   if (game.light_time_left > 0)
9404   {
9405     game.light_time_left--;
9406
9407     if (game.light_time_left == 0)
9408       RedrawAllLightSwitchesAndInvisibleElements();
9409   }
9410
9411   if (game.timegate_time_left > 0)
9412   {
9413     game.timegate_time_left--;
9414
9415     if (game.timegate_time_left == 0)
9416       CloseAllOpenTimegates();
9417   }
9418
9419   for (i = 0; i < MAX_PLAYERS; i++)
9420   {
9421     struct PlayerInfo *player = &stored_player[i];
9422
9423     if (SHIELD_ON(player))
9424     {
9425       if (player->shield_deadly_time_left)
9426         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9427       else if (player->shield_normal_time_left)
9428         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9429     }
9430   }
9431
9432   if (TimeFrames >= FRAMES_PER_SECOND)
9433   {
9434     TimeFrames = 0;
9435     TapeTime++;
9436
9437     for (i = 0; i < MAX_PLAYERS; i++)
9438     {
9439       struct PlayerInfo *player = &stored_player[i];
9440
9441       if (SHIELD_ON(player))
9442       {
9443         player->shield_normal_time_left--;
9444
9445         if (player->shield_deadly_time_left > 0)
9446           player->shield_deadly_time_left--;
9447       }
9448     }
9449
9450     if (!level.use_step_counter)
9451     {
9452       TimePlayed++;
9453
9454       if (TimeLeft > 0)
9455       {
9456         TimeLeft--;
9457
9458         if (TimeLeft <= 10 && setup.time_limit)
9459           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9460
9461         DrawGameValue_Time(TimeLeft);
9462
9463         if (!TimeLeft && setup.time_limit)
9464           for (i = 0; i < MAX_PLAYERS; i++)
9465             KillHero(&stored_player[i]);
9466       }
9467       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9468         DrawGameValue_Time(TimePlayed);
9469     }
9470
9471     if (tape.recording || tape.playing)
9472       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9473   }
9474
9475   DrawAllPlayers();
9476   PlayAllPlayersSound();
9477
9478   if (options.debug)                    /* calculate frames per second */
9479   {
9480     static unsigned long fps_counter = 0;
9481     static int fps_frames = 0;
9482     unsigned long fps_delay_ms = Counter() - fps_counter;
9483
9484     fps_frames++;
9485
9486     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
9487     {
9488       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9489
9490       fps_frames = 0;
9491       fps_counter = Counter();
9492     }
9493
9494     redraw_mask |= REDRAW_FPS;
9495   }
9496
9497 #if 0
9498   if (stored_player[0].jx != stored_player[0].last_jx ||
9499       stored_player[0].jy != stored_player[0].last_jy)
9500     printf("::: %d, %d, %d, %d, %d\n",
9501            stored_player[0].MovDir,
9502            stored_player[0].MovPos,
9503            stored_player[0].GfxPos,
9504            stored_player[0].Frame,
9505            stored_player[0].StepFrame);
9506 #endif
9507
9508 #if USE_NEW_MOVE_DELAY
9509   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9510 #else
9511   FrameCounter++;
9512   TimeFrames++;
9513
9514   for (i = 0; i < MAX_PLAYERS; i++)
9515   {
9516     int move_frames =
9517       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
9518
9519     stored_player[i].Frame += move_frames;
9520
9521     if (stored_player[i].MovPos != 0)
9522       stored_player[i].StepFrame += move_frames;
9523
9524 #if USE_NEW_MOVE_DELAY
9525     if (stored_player[i].move_delay > 0)
9526       stored_player[i].move_delay--;
9527 #endif
9528
9529     if (stored_player[i].drop_delay > 0)
9530       stored_player[i].drop_delay--;
9531   }
9532 #endif
9533
9534 #if 1
9535   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9536   {
9537     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9538
9539     local_player->show_envelope = 0;
9540   }
9541 #endif
9542
9543 #if USE_NEW_RANDOMIZE
9544   /* use random number generator in every frame to make it less predictable */
9545   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9546     RND(1);
9547 #endif
9548 }
9549
9550 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9551 {
9552   int min_x = x, min_y = y, max_x = x, max_y = y;
9553   int i;
9554
9555   for (i = 0; i < MAX_PLAYERS; i++)
9556   {
9557     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9558
9559     if (!stored_player[i].active || &stored_player[i] == player)
9560       continue;
9561
9562     min_x = MIN(min_x, jx);
9563     min_y = MIN(min_y, jy);
9564     max_x = MAX(max_x, jx);
9565     max_y = MAX(max_y, jy);
9566   }
9567
9568   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9569 }
9570
9571 static boolean AllPlayersInVisibleScreen()
9572 {
9573   int i;
9574
9575   for (i = 0; i < MAX_PLAYERS; i++)
9576   {
9577     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9578
9579     if (!stored_player[i].active)
9580       continue;
9581
9582     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9583       return FALSE;
9584   }
9585
9586   return TRUE;
9587 }
9588
9589 void ScrollLevel(int dx, int dy)
9590 {
9591   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9592   int x, y;
9593
9594   BlitBitmap(drawto_field, drawto_field,
9595              FX + TILEX * (dx == -1) - softscroll_offset,
9596              FY + TILEY * (dy == -1) - softscroll_offset,
9597              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9598              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9599              FX + TILEX * (dx == 1) - softscroll_offset,
9600              FY + TILEY * (dy == 1) - softscroll_offset);
9601
9602   if (dx)
9603   {
9604     x = (dx == 1 ? BX1 : BX2);
9605     for (y = BY1; y <= BY2; y++)
9606       DrawScreenField(x, y);
9607   }
9608
9609   if (dy)
9610   {
9611     y = (dy == 1 ? BY1 : BY2);
9612     for (x = BX1; x <= BX2; x++)
9613       DrawScreenField(x, y);
9614   }
9615
9616   redraw_mask |= REDRAW_FIELD;
9617 }
9618
9619 #if 0
9620 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9621 {
9622   int nextx = x + dx, nexty = y + dy;
9623   int element = Feld[x][y];
9624
9625   if ((dx == -1 &&
9626        element != EL_SP_PORT_LEFT &&
9627        element != EL_SP_GRAVITY_PORT_LEFT &&
9628        element != EL_SP_PORT_HORIZONTAL &&
9629        element != EL_SP_PORT_ANY) ||
9630       (dx == +1 &&
9631        element != EL_SP_PORT_RIGHT &&
9632        element != EL_SP_GRAVITY_PORT_RIGHT &&
9633        element != EL_SP_PORT_HORIZONTAL &&
9634        element != EL_SP_PORT_ANY) ||
9635       (dy == -1 &&
9636        element != EL_SP_PORT_UP &&
9637        element != EL_SP_GRAVITY_PORT_UP &&
9638        element != EL_SP_PORT_VERTICAL &&
9639        element != EL_SP_PORT_ANY) ||
9640       (dy == +1 &&
9641        element != EL_SP_PORT_DOWN &&
9642        element != EL_SP_GRAVITY_PORT_DOWN &&
9643        element != EL_SP_PORT_VERTICAL &&
9644        element != EL_SP_PORT_ANY) ||
9645       !IN_LEV_FIELD(nextx, nexty) ||
9646       !IS_FREE(nextx, nexty))
9647     return FALSE;
9648
9649   return TRUE;
9650 }
9651 #endif
9652
9653 static boolean canFallDown(struct PlayerInfo *player)
9654 {
9655   int jx = player->jx, jy = player->jy;
9656
9657   return (IN_LEV_FIELD(jx, jy + 1) &&
9658           (IS_FREE(jx, jy + 1) ||
9659 #if USE_NEW_BLOCK_STYLE
9660 #if USE_GRAVITY_BUGFIX_OLD
9661            Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
9662 #endif
9663 #endif
9664            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9665           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9666           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9667 }
9668
9669 static boolean canPassField(int x, int y, int move_dir)
9670 {
9671   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9672   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9673   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9674   int nextx = x + dx;
9675   int nexty = y + dy;
9676   int element = Feld[x][y];
9677
9678   return (IS_PASSABLE_FROM(element, opposite_dir) &&
9679           !CAN_MOVE(element) &&
9680           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9681           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9682           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9683 }
9684
9685 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9686 {
9687   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9688   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9689   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9690   int newx = x + dx;
9691   int newy = y + dy;
9692 #if 0
9693   int nextx = newx + dx;
9694   int nexty = newy + dy;
9695 #endif
9696
9697 #if 1
9698   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9699           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9700 #if 0
9701           (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9702 #endif
9703           (IS_DIGGABLE(Feld[newx][newy]) ||
9704            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9705            canPassField(newx, newy, move_dir)));
9706 #else
9707 #if 1
9708   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9709           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9710           (IS_DIGGABLE(Feld[newx][newy]) ||
9711            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9712            canPassField(newx, newy, move_dir)));
9713 #else
9714 #if 1
9715   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9716           (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9717            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9718            canPassField(newx, newy, move_dir)));
9719 #else
9720   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9721           (IS_DIGGABLE(Feld[newx][newy]) ||
9722            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9723            (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9724             !CAN_MOVE(Feld[newx][newy]) &&
9725             IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9726             IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9727             (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9728 #endif
9729 #endif
9730 #endif
9731 }
9732
9733 static void CheckGravityMovement(struct PlayerInfo *player)
9734 {
9735   if (game.gravity && !player->programmed_action)
9736   {
9737 #if 1
9738     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9739     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9740 #else
9741     int move_dir_horizontal = player->action & MV_HORIZONTAL;
9742     int move_dir_vertical   = player->action & MV_VERTICAL;
9743 #endif
9744
9745 #if 1
9746     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9747 #else
9748     boolean player_is_snapping = player->action & JOY_BUTTON_1;
9749 #endif
9750
9751     int jx = player->jx, jy = player->jy;
9752
9753     boolean player_is_moving_to_valid_field =
9754       (!player_is_snapping &&
9755        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9756         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9757
9758 #if 0
9759     int move_dir =
9760       (player->last_move_dir & MV_HORIZONTAL ?
9761        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9762        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9763 #endif
9764
9765 #if 0
9766     int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9767     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9768     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9769     int new_jx = jx + dx, new_jy = jy + dy;
9770     int nextx = new_jx + dx, nexty = new_jy + dy;
9771 #endif
9772
9773 #if 1
9774
9775 #if 1
9776     boolean player_can_fall_down = canFallDown(player);
9777 #else
9778     boolean player_can_fall_down =
9779       (IN_LEV_FIELD(jx, jy + 1) &&
9780        (IS_FREE(jx, jy + 1) ||
9781         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9782 #endif
9783
9784 #else
9785     boolean player_can_fall_down =
9786       (IN_LEV_FIELD(jx, jy + 1) &&
9787        (IS_FREE(jx, jy + 1)));
9788 #endif
9789
9790 #if 0
9791     boolean player_is_moving_to_valid_field =
9792       (
9793 #if 1
9794        !player_is_snapping &&
9795 #endif
9796
9797 #if 1
9798        IN_LEV_FIELD(new_jx, new_jy) &&
9799        (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9800         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9801          element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9802          IN_LEV_FIELD(nextx, nexty) &&
9803          element_info[Feld[nextx][nexty]].access_direction & move_dir))
9804 #else
9805        IN_LEV_FIELD(new_jx, new_jy) &&
9806        (Feld[new_jx][new_jy] == EL_SP_BASE ||
9807         Feld[new_jx][new_jy] == EL_SAND ||
9808         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9809          canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9810     /* !!! extend EL_SAND to anything diggable !!! */
9811 #endif
9812        );
9813 #endif
9814
9815 #if 0
9816     boolean player_is_standing_on_valid_field =
9817       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9818        (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9819 #endif
9820
9821 #if 0
9822     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9823            player_can_fall_down,
9824            player_is_standing_on_valid_field,
9825            player_is_moving_to_valid_field,
9826            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9827            player->effective_action,
9828            player->can_fall_into_acid);
9829 #endif
9830
9831     if (player_can_fall_down &&
9832 #if 0
9833         !player_is_standing_on_valid_field &&
9834 #endif
9835         !player_is_moving_to_valid_field)
9836     {
9837 #if 0
9838       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9839              jx, jy, FrameCounter);
9840 #endif
9841
9842       player->programmed_action = MV_DOWN;
9843     }
9844   }
9845 }
9846
9847 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9848 {
9849 #if 1
9850   return CheckGravityMovement(player);
9851 #endif
9852
9853   if (game.gravity && !player->programmed_action)
9854   {
9855     int jx = player->jx, jy = player->jy;
9856     boolean field_under_player_is_free =
9857       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9858     boolean player_is_standing_on_valid_field =
9859       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9860        (IS_WALKABLE(Feld[jx][jy]) &&
9861         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9862
9863     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9864       player->programmed_action = MV_DOWN;
9865   }
9866 }
9867
9868 /*
9869   MovePlayerOneStep()
9870   -----------------------------------------------------------------------------
9871   dx, dy:               direction (non-diagonal) to try to move the player to
9872   real_dx, real_dy:     direction as read from input device (can be diagonal)
9873 */
9874
9875 boolean MovePlayerOneStep(struct PlayerInfo *player,
9876                           int dx, int dy, int real_dx, int real_dy)
9877 {
9878 #if 0
9879   static int trigger_sides[4][2] =
9880   {
9881     /* enter side        leave side */
9882     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
9883     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
9884     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
9885     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
9886   };
9887   int move_direction = (dx == -1 ? MV_LEFT :
9888                         dx == +1 ? MV_RIGHT :
9889                         dy == -1 ? MV_UP :
9890                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9891   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9892   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9893 #endif
9894   int jx = player->jx, jy = player->jy;
9895   int new_jx = jx + dx, new_jy = jy + dy;
9896   int element;
9897   int can_move;
9898
9899   if (!player->active || (!dx && !dy))
9900     return MF_NO_ACTION;
9901
9902   player->MovDir = (dx < 0 ? MV_LEFT :
9903                     dx > 0 ? MV_RIGHT :
9904                     dy < 0 ? MV_UP :
9905                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
9906
9907   if (!IN_LEV_FIELD(new_jx, new_jy))
9908     return MF_NO_ACTION;
9909
9910   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9911     return MF_NO_ACTION;
9912
9913 #if 0
9914   element = MovingOrBlocked2Element(new_jx, new_jy);
9915 #else
9916   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9917 #endif
9918
9919   if (DONT_RUN_INTO(element))
9920   {
9921     if (element == EL_ACID && dx == 0 && dy == 1)
9922     {
9923       SplashAcid(new_jx, new_jy);
9924       Feld[jx][jy] = EL_PLAYER_1;
9925       InitMovingField(jx, jy, MV_DOWN);
9926       Store[jx][jy] = EL_ACID;
9927       ContinueMoving(jx, jy);
9928       BuryHero(player);
9929     }
9930     else
9931       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9932
9933     return MF_MOVING;
9934   }
9935
9936   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9937   if (can_move != MF_MOVING)
9938     return can_move;
9939
9940   /* check if DigField() has caused relocation of the player */
9941   if (player->jx != jx || player->jy != jy)
9942     return MF_NO_ACTION;        /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9943
9944   StorePlayer[jx][jy] = 0;
9945   player->last_jx = jx;
9946   player->last_jy = jy;
9947   player->jx = new_jx;
9948   player->jy = new_jy;
9949   StorePlayer[new_jx][new_jy] = player->element_nr;
9950
9951   player->MovPos =
9952     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9953
9954   player->step_counter++;
9955
9956 #if 0
9957   player->drop_delay = 0;
9958 #endif
9959
9960   PlayerVisit[jx][jy] = FrameCounter;
9961
9962   ScrollPlayer(player, SCROLL_INIT);
9963
9964 #if 0
9965   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9966   {
9967     CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
9968                                       leave_side);
9969     CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9970   }
9971
9972   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9973   {
9974     CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9975                                       CE_PLAYER_ENTERS_X, enter_side);
9976     CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9977                              CE_ENTERED_BY_PLAYER, enter_side);
9978   }
9979 #endif
9980
9981   return MF_MOVING;
9982 }
9983
9984 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9985 {
9986   int jx = player->jx, jy = player->jy;
9987   int old_jx = jx, old_jy = jy;
9988   int moved = MF_NO_ACTION;
9989
9990 #if 1
9991   if (!player->active)
9992     return FALSE;
9993
9994   if (!dx && !dy)
9995   {
9996     if (player->MovPos == 0)
9997     {
9998       player->is_moving = FALSE;
9999       player->is_digging = FALSE;
10000       player->is_collecting = FALSE;
10001       player->is_snapping = FALSE;
10002       player->is_pushing = FALSE;
10003     }
10004
10005     return FALSE;
10006   }
10007 #else
10008   if (!player->active || (!dx && !dy))
10009     return FALSE;
10010 #endif
10011
10012 #if 0
10013   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10014       !tape.playing)
10015     return FALSE;
10016 #else
10017
10018 #if 1
10019
10020 #if 0
10021   printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
10022          player->move_delay + player->move_delay_value);
10023 #endif
10024
10025 #if USE_NEW_MOVE_DELAY
10026   if (player->move_delay > 0)
10027 #else
10028   if (!FrameReached(&player->move_delay, player->move_delay_value))
10029 #endif
10030   {
10031 #if 0
10032     printf("::: can NOT move\n");
10033 #endif
10034
10035     return FALSE;
10036   }
10037 #else
10038   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10039       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10040     return FALSE;
10041 #endif
10042
10043 #endif
10044
10045 #if 0
10046   printf("::: COULD move now\n");
10047 #endif
10048
10049 #if USE_NEW_MOVE_DELAY
10050   player->move_delay = -1;              /* set to "uninitialized" value */
10051 #endif
10052
10053   /* store if player is automatically moved to next field */
10054   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10055
10056   /* remove the last programmed player action */
10057   player->programmed_action = 0;
10058
10059   if (player->MovPos)
10060   {
10061     /* should only happen if pre-1.2 tape recordings are played */
10062     /* this is only for backward compatibility */
10063
10064     int original_move_delay_value = player->move_delay_value;
10065
10066 #if DEBUG
10067     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10068            tape.counter);
10069 #endif
10070
10071     /* scroll remaining steps with finest movement resolution */
10072     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10073
10074     while (player->MovPos)
10075     {
10076       ScrollPlayer(player, SCROLL_GO_ON);
10077       ScrollScreen(NULL, SCROLL_GO_ON);
10078
10079 #if USE_NEW_MOVE_DELAY
10080       AdvanceFrameAndPlayerCounters(player->index_nr);
10081 #else
10082       FrameCounter++;
10083 #endif
10084
10085       DrawAllPlayers();
10086       BackToFront();
10087     }
10088
10089     player->move_delay_value = original_move_delay_value;
10090   }
10091
10092   if (player->last_move_dir & MV_HORIZONTAL)
10093   {
10094     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10095       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10096   }
10097   else
10098   {
10099     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10100       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10101   }
10102
10103   jx = player->jx;
10104   jy = player->jy;
10105
10106   if (moved & MF_MOVING && !ScreenMovPos &&
10107       (player == local_player || !options.network))
10108   {
10109     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10110     int offset = (setup.scroll_delay ? 3 : 0);
10111
10112     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10113     {
10114       /* actual player has left the screen -- scroll in that direction */
10115       if (jx != old_jx)         /* player has moved horizontally */
10116         scroll_x += (jx - old_jx);
10117       else                      /* player has moved vertically */
10118         scroll_y += (jy - old_jy);
10119     }
10120     else
10121     {
10122       if (jx != old_jx)         /* player has moved horizontally */
10123       {
10124         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10125             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10126           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10127
10128         /* don't scroll over playfield boundaries */
10129         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10130           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10131
10132         /* don't scroll more than one field at a time */
10133         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10134
10135         /* don't scroll against the player's moving direction */
10136         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10137             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10138           scroll_x = old_scroll_x;
10139       }
10140       else                      /* player has moved vertically */
10141       {
10142         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10143             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10144           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10145
10146         /* don't scroll over playfield boundaries */
10147         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10148           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10149
10150         /* don't scroll more than one field at a time */
10151         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10152
10153         /* don't scroll against the player's moving direction */
10154         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10155             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10156           scroll_y = old_scroll_y;
10157       }
10158     }
10159
10160     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10161     {
10162       if (!options.network && !AllPlayersInVisibleScreen())
10163       {
10164         scroll_x = old_scroll_x;
10165         scroll_y = old_scroll_y;
10166       }
10167       else
10168       {
10169         ScrollScreen(player, SCROLL_INIT);
10170         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10171       }
10172     }
10173   }
10174
10175 #if 0
10176 #if 1
10177   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10178 #else
10179   if (!(moved & MF_MOVING) && !player->is_pushing)
10180     player->Frame = 0;
10181 #endif
10182 #endif
10183
10184   player->StepFrame = 0;
10185
10186   if (moved & MF_MOVING)
10187   {
10188 #if 0
10189     printf("::: REALLY moves now\n");
10190 #endif
10191
10192     if (old_jx != jx && old_jy == jy)
10193       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10194     else if (old_jx == jx && old_jy != jy)
10195       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10196
10197     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10198
10199     player->last_move_dir = player->MovDir;
10200     player->is_moving = TRUE;
10201 #if 1
10202     player->is_snapping = FALSE;
10203 #endif
10204
10205 #if 1
10206     player->is_switching = FALSE;
10207 #endif
10208
10209     player->is_dropping = FALSE;
10210
10211
10212 #if 0
10213     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10214
10215 #if 1
10216     if (game.engine_version < VERSION_IDENT(3,1,0,0))
10217 #endif
10218     {
10219       int move_direction = player->MovDir;
10220 #if 1
10221       int enter_side = MV_DIR_OPPOSITE(move_direction);
10222       int leave_side = move_direction;
10223 #else
10224       static int trigger_sides[4][2] =
10225       {
10226         /* enter side           leave side */
10227         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
10228         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
10229         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
10230         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
10231       };
10232       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10233       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10234 #endif
10235       int old_element = Feld[old_jx][old_jy];
10236       int new_element = Feld[jx][jy];
10237
10238 #if 1
10239       /* !!! TEST ONLY !!! */
10240       if (IS_CUSTOM_ELEMENT(old_element))
10241         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10242                                    CE_LEFT_BY_PLAYER,
10243                                    player->index_bit, leave_side);
10244
10245       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10246                                           CE_PLAYER_LEAVES_X,
10247                                           player->index_bit, leave_side);
10248
10249       if (IS_CUSTOM_ELEMENT(new_element))
10250         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10251                                    player->index_bit, enter_side);
10252
10253       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10254                                           CE_PLAYER_ENTERS_X,
10255                                           player->index_bit, enter_side);
10256 #endif
10257
10258     }
10259 #endif
10260
10261
10262   }
10263   else
10264   {
10265     CheckGravityMovementWhenNotMoving(player);
10266
10267     /*
10268     player->last_move_dir = MV_NO_MOVING;
10269     */
10270     player->is_moving = FALSE;
10271
10272 #if USE_NEW_MOVE_STYLE
10273     /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10274     /* ensure that the player is also allowed to move in the next frame */
10275     /* (currently, the player is forced to wait eight frames before he can try
10276        again!!!) */
10277
10278     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10279       player->move_delay = 0;   /* allow direct movement in the next frame */
10280 #endif
10281   }
10282
10283 #if USE_NEW_MOVE_DELAY
10284   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10285     player->move_delay = player->move_delay_value;
10286 #endif
10287
10288   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10289   {
10290     TestIfHeroTouchesBadThing(jx, jy);
10291     TestIfPlayerTouchesCustomElement(jx, jy);
10292   }
10293
10294   if (!player->active)
10295     RemoveHero(player);
10296
10297   return moved;
10298 }
10299
10300 void ScrollPlayer(struct PlayerInfo *player, int mode)
10301 {
10302   int jx = player->jx, jy = player->jy;
10303   int last_jx = player->last_jx, last_jy = player->last_jy;
10304   int move_stepsize = TILEX / player->move_delay_value;
10305
10306   if (!player->active || !player->MovPos)
10307     return;
10308
10309   if (mode == SCROLL_INIT)
10310   {
10311     player->actual_frame_counter = FrameCounter;
10312     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10313
10314 #if 0
10315     printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10316            FrameCounter,
10317            last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10318            player->block_delay);
10319 #endif
10320
10321 #if USE_NEW_BLOCK_STYLE
10322
10323 #if 0
10324     if (player->block_delay <= 0)
10325       printf("::: ALERT! block_delay == %d\n", player->block_delay);
10326 #endif
10327
10328     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10329         Feld[last_jx][last_jy] == EL_EMPTY)
10330     {
10331       int last_field_block_delay = 0;   /* start with no blocking at all */
10332       int block_delay_adjustment = player->block_delay_adjustment;
10333
10334       /* if player blocks last field, add delay for exactly one move */
10335       if (player->block_last_field)
10336       {
10337         last_field_block_delay += player->move_delay_value;
10338
10339 #if USE_GRAVITY_BUGFIX_NEW
10340         /* when blocking enabled, prevent moving up despite gravity */
10341         if (game.gravity && player->MovDir == MV_UP)
10342           block_delay_adjustment = -1;
10343 #endif
10344       }
10345
10346       /* add block delay adjustment (also possible when not blocking) */
10347       last_field_block_delay += block_delay_adjustment;
10348
10349 #if 0
10350 #if USE_BLOCK_DELAY_BUGFIX
10351       /* when blocking enabled, correct block delay for fast movement */
10352       if (player->block_last_field &&
10353           player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10354         last_field_block_delay =
10355           player->move_delay_value + player->block_delay_adjustment;
10356 #endif
10357 #endif
10358
10359 #if 0
10360 #if USE_GRAVITY_BUGFIX_NEW
10361       /* when blocking enabled, correct block delay for gravity movement */
10362       if (player->block_last_field &&
10363           game.gravity && player->MovDir == MV_UP)
10364         last_field_block_delay = player->move_delay_value - 1;
10365 #endif
10366 #endif
10367
10368       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10369       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10370     }
10371 #else
10372 #if USE_NEW_MOVE_STYLE
10373     if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10374          player->block_last_field) &&
10375         Feld[last_jx][last_jy] == EL_EMPTY)
10376       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10377 #else
10378     if (Feld[last_jx][last_jy] == EL_EMPTY)
10379       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10380 #endif
10381 #endif
10382
10383 #if 0
10384     DrawPlayer(player);
10385 #endif
10386
10387     return;
10388   }
10389   else if (!FrameReached(&player->actual_frame_counter, 1))
10390     return;
10391
10392   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10393   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10394
10395 #if USE_NEW_BLOCK_STYLE
10396 #else
10397   if (!player->block_last_field &&
10398       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10399 #if 1
10400     RemoveField(last_jx, last_jy);
10401 #else
10402     Feld[last_jx][last_jy] = EL_EMPTY;
10403 #endif
10404 #endif
10405
10406   /* before DrawPlayer() to draw correct player graphic for this case */
10407   if (player->MovPos == 0)
10408     CheckGravityMovement(player);
10409
10410 #if 0
10411   DrawPlayer(player);   /* needed here only to cleanup last field */
10412 #endif
10413
10414   if (player->MovPos == 0)      /* player reached destination field */
10415   {
10416 #if 1
10417     if (player->move_delay_reset_counter > 0)
10418     {
10419       player->move_delay_reset_counter--;
10420
10421       if (player->move_delay_reset_counter == 0)
10422       {
10423         /* continue with normal speed after quickly moving through gate */
10424         HALVE_PLAYER_SPEED(player);
10425
10426         /* be able to make the next move without delay */
10427         player->move_delay = 0;
10428       }
10429     }
10430 #else
10431     if (IS_PASSABLE(Feld[last_jx][last_jy]))
10432     {
10433       /* continue with normal speed after quickly moving through gate */
10434       HALVE_PLAYER_SPEED(player);
10435
10436       /* be able to make the next move without delay */
10437       player->move_delay = 0;
10438     }
10439 #endif
10440
10441 #if USE_NEW_BLOCK_STYLE
10442 #else
10443     if (player->block_last_field &&
10444         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10445 #if 1
10446       RemoveField(last_jx, last_jy);
10447 #else
10448       Feld[last_jx][last_jy] = EL_EMPTY;
10449 #endif
10450 #endif
10451
10452     player->last_jx = jx;
10453     player->last_jy = jy;
10454
10455     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10456         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10457         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10458     {
10459       DrawPlayer(player);       /* needed here only to cleanup last field */
10460       RemoveHero(player);
10461
10462       if (local_player->friends_still_needed == 0 ||
10463           IS_SP_ELEMENT(Feld[jx][jy]))
10464         player->LevelSolved = player->GameOver = TRUE;
10465     }
10466
10467 #if 1
10468     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10469     /* this breaks one level: "machine", level 000 */
10470 #if 0
10471     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10472 #endif
10473     {
10474       int move_direction = player->MovDir;
10475 #if 1
10476       int enter_side = MV_DIR_OPPOSITE(move_direction);
10477       int leave_side = move_direction;
10478 #else
10479       static int trigger_sides[4][2] =
10480       {
10481         /* enter side           leave side */
10482         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
10483         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
10484         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
10485         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
10486       };
10487       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10488       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10489 #endif
10490       int old_jx = last_jx;
10491       int old_jy = last_jy;
10492       int old_element = Feld[old_jx][old_jy];
10493       int new_element = Feld[jx][jy];
10494
10495 #if 1
10496       /* !!! TEST ONLY !!! */
10497       if (IS_CUSTOM_ELEMENT(old_element))
10498         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10499                                    CE_LEFT_BY_PLAYER,
10500                                    player->index_bit, leave_side);
10501
10502       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10503                                           CE_PLAYER_LEAVES_X,
10504                                           player->index_bit, leave_side);
10505
10506       if (IS_CUSTOM_ELEMENT(new_element))
10507         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10508                                    player->index_bit, enter_side);
10509
10510       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10511                                           CE_PLAYER_ENTERS_X,
10512                                           player->index_bit, enter_side);
10513 #endif
10514
10515     }
10516 #endif
10517
10518     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10519     {
10520       TestIfHeroTouchesBadThing(jx, jy);
10521       TestIfPlayerTouchesCustomElement(jx, jy);
10522 #if 1
10523 #if 1
10524       /* needed because pushed element has not yet reached its destination,
10525          so it would trigger a change event at its previous field location */
10526       if (!player->is_pushing)
10527 #endif
10528         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10529 #endif
10530
10531       if (!player->active)
10532         RemoveHero(player);
10533     }
10534
10535     if (level.use_step_counter)
10536     {
10537       int i;
10538
10539       TimePlayed++;
10540
10541       if (TimeLeft > 0)
10542       {
10543         TimeLeft--;
10544
10545         if (TimeLeft <= 10 && setup.time_limit)
10546           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10547
10548         DrawGameValue_Time(TimeLeft);
10549
10550         if (!TimeLeft && setup.time_limit)
10551           for (i = 0; i < MAX_PLAYERS; i++)
10552             KillHero(&stored_player[i]);
10553       }
10554       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10555         DrawGameValue_Time(TimePlayed);
10556     }
10557
10558     if (tape.single_step && tape.recording && !tape.pausing &&
10559         !player->programmed_action)
10560       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10561   }
10562 }
10563
10564 void ScrollScreen(struct PlayerInfo *player, int mode)
10565 {
10566   static unsigned long screen_frame_counter = 0;
10567
10568   if (mode == SCROLL_INIT)
10569   {
10570     /* set scrolling step size according to actual player's moving speed */
10571     ScrollStepSize = TILEX / player->move_delay_value;
10572
10573     screen_frame_counter = FrameCounter;
10574     ScreenMovDir = player->MovDir;
10575     ScreenMovPos = player->MovPos;
10576     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10577     return;
10578   }
10579   else if (!FrameReached(&screen_frame_counter, 1))
10580     return;
10581
10582   if (ScreenMovPos)
10583   {
10584     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10585     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10586     redraw_mask |= REDRAW_FIELD;
10587   }
10588   else
10589     ScreenMovDir = MV_NO_MOVING;
10590 }
10591
10592 void TestIfPlayerTouchesCustomElement(int x, int y)
10593 {
10594   static int xy[4][2] =
10595   {
10596     { 0, -1 },
10597     { -1, 0 },
10598     { +1, 0 },
10599     { 0, +1 }
10600   };
10601   static int trigger_sides[4][2] =
10602   {
10603     /* center side       border side */
10604     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10605     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10606     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10607     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10608   };
10609   static int touch_dir[4] =
10610   {
10611     MV_LEFT | MV_RIGHT,
10612     MV_UP   | MV_DOWN,
10613     MV_UP   | MV_DOWN,
10614     MV_LEFT | MV_RIGHT
10615   };
10616   int center_element = Feld[x][y];      /* should always be non-moving! */
10617   int i;
10618
10619   for (i = 0; i < NUM_DIRECTIONS; i++)
10620   {
10621     int xx = x + xy[i][0];
10622     int yy = y + xy[i][1];
10623     int center_side = trigger_sides[i][0];
10624     int border_side = trigger_sides[i][1];
10625     int border_element;
10626
10627     if (!IN_LEV_FIELD(xx, yy))
10628       continue;
10629
10630     if (IS_PLAYER(x, y))
10631     {
10632       struct PlayerInfo *player = PLAYERINFO(x, y);
10633
10634       if (game.engine_version < VERSION_IDENT(3,0,7,0))
10635         border_element = Feld[xx][yy];          /* may be moving! */
10636       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10637         border_element = Feld[xx][yy];
10638       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
10639         border_element = MovingOrBlocked2Element(xx, yy);
10640       else
10641         continue;               /* center and border element do not touch */
10642
10643 #if 1
10644       /* !!! TEST ONLY !!! */
10645       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10646                                  player->index_bit, border_side);
10647       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10648                                           CE_PLAYER_TOUCHES_X,
10649                                           player->index_bit, border_side);
10650 #else
10651       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10652                                           CE_PLAYER_TOUCHES_X,
10653                                           player->index_bit, border_side);
10654       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10655                                  player->index_bit, border_side);
10656 #endif
10657     }
10658     else if (IS_PLAYER(xx, yy))
10659     {
10660       struct PlayerInfo *player = PLAYERINFO(xx, yy);
10661
10662       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10663       {
10664         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10665           continue;             /* center and border element do not touch */
10666       }
10667
10668 #if 1
10669       /* !!! TEST ONLY !!! */
10670       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10671                                  player->index_bit, center_side);
10672       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10673                                           CE_PLAYER_TOUCHES_X,
10674                                           player->index_bit, center_side);
10675 #else
10676       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10677                                           CE_PLAYER_TOUCHES_X,
10678                                           player->index_bit, center_side);
10679       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10680                                  player->index_bit, center_side);
10681 #endif
10682
10683       break;
10684     }
10685   }
10686 }
10687
10688 void TestIfElementTouchesCustomElement(int x, int y)
10689 {
10690   static int xy[4][2] =
10691   {
10692     { 0, -1 },
10693     { -1, 0 },
10694     { +1, 0 },
10695     { 0, +1 }
10696   };
10697   static int trigger_sides[4][2] =
10698   {
10699     /* center side      border side */
10700     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10701     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10702     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10703     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10704   };
10705   static int touch_dir[4] =
10706   {
10707     MV_LEFT | MV_RIGHT,
10708     MV_UP   | MV_DOWN,
10709     MV_UP   | MV_DOWN,
10710     MV_LEFT | MV_RIGHT
10711   };
10712   boolean change_center_element = FALSE;
10713   int center_element_change_page = 0;
10714   int center_element = Feld[x][y];      /* should always be non-moving! */
10715   int border_trigger_element = EL_UNDEFINED;
10716   int i, j;
10717
10718   for (i = 0; i < NUM_DIRECTIONS; i++)
10719   {
10720     int xx = x + xy[i][0];
10721     int yy = y + xy[i][1];
10722     int center_side = trigger_sides[i][0];
10723     int border_side = trigger_sides[i][1];
10724     int border_element;
10725
10726     if (!IN_LEV_FIELD(xx, yy))
10727       continue;
10728
10729     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10730       border_element = Feld[xx][yy];    /* may be moving! */
10731     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10732       border_element = Feld[xx][yy];
10733     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10734       border_element = MovingOrBlocked2Element(xx, yy);
10735     else
10736       continue;                 /* center and border element do not touch */
10737
10738     /* check for change of center element (but change it only once) */
10739     if (IS_CUSTOM_ELEMENT(center_element) &&
10740         HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
10741         !change_center_element)
10742     {
10743       for (j = 0; j < element_info[center_element].num_change_pages; j++)
10744       {
10745         struct ElementChangeInfo *change =
10746           &element_info[center_element].change_page[j];
10747
10748         if (change->can_change &&
10749             change->has_event[CE_TOUCHING_X] &&
10750             change->trigger_side & border_side &&
10751 #if 1
10752             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10753 #else
10754             change->trigger_element == border_element
10755 #endif
10756             )
10757         {
10758           change_center_element = TRUE;
10759           center_element_change_page = j;
10760           border_trigger_element = border_element;
10761
10762           break;
10763         }
10764       }
10765     }
10766
10767     /* check for change of border element */
10768     if (IS_CUSTOM_ELEMENT(border_element) &&
10769         HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
10770     {
10771       for (j = 0; j < element_info[border_element].num_change_pages; j++)
10772       {
10773         struct ElementChangeInfo *change =
10774           &element_info[border_element].change_page[j];
10775
10776         if (change->can_change &&
10777             change->has_event[CE_TOUCHING_X] &&
10778             change->trigger_side & center_side &&
10779 #if 1
10780             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10781 #else
10782             change->trigger_element == center_element
10783 #endif
10784             )
10785         {
10786 #if 0
10787           printf("::: border_element %d, %d\n", x, y);
10788 #endif
10789
10790           CheckElementChangeByPage(xx, yy, border_element, center_element,
10791                                    CE_TOUCHING_X, j);
10792           break;
10793         }
10794       }
10795     }
10796   }
10797
10798   if (change_center_element)
10799   {
10800 #if 0
10801     printf("::: center_element %d, %d\n", x, y);
10802 #endif
10803
10804     CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10805                              CE_TOUCHING_X, center_element_change_page);
10806   }
10807 }
10808
10809 void TestIfElementHitsCustomElement(int x, int y, int direction)
10810 {
10811   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10812   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10813   int hitx = x + dx, hity = y + dy;
10814   int hitting_element = Feld[x][y];
10815   int touched_element;
10816 #if 0
10817   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10818                         !IS_FREE(hitx, hity) &&
10819                         (!IS_MOVING(hitx, hity) ||
10820                          MovDir[hitx][hity] != direction ||
10821                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10822 #endif
10823
10824   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10825     return;
10826
10827 #if 0
10828   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10829     return;
10830 #endif
10831
10832   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10833                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10834
10835 #if !USE_HITTING_SOMETHING_BUGFIX
10836   /* "hitting something" is also true when hitting the playfield border */
10837   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10838                            CE_HITTING_SOMETHING, direction);
10839 #endif
10840
10841   if (IN_LEV_FIELD(hitx, hity))
10842   {
10843     int opposite_direction = MV_DIR_OPPOSITE(direction);
10844     int hitting_side = direction;
10845     int touched_side = opposite_direction;
10846 #if 0
10847     int touched_element = MovingOrBlocked2Element(hitx, hity);
10848 #endif
10849 #if 1
10850     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10851                           MovDir[hitx][hity] != direction ||
10852                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10853
10854     object_hit = TRUE;
10855 #endif
10856
10857     if (object_hit)
10858     {
10859       int i;
10860
10861 #if !USE_HIT_BY_SOMETHING_BUGFIX
10862       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10863                                CE_HIT_BY_SOMETHING, opposite_direction);
10864 #endif
10865
10866       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10867           HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
10868       {
10869         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10870         {
10871           struct ElementChangeInfo *change =
10872             &element_info[hitting_element].change_page[i];
10873
10874           if (change->can_change &&
10875               change->has_event[CE_HITTING_X] &&
10876               change->trigger_side & touched_side &&
10877           
10878 #if 1
10879               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10880 #else
10881               change->trigger_element == touched_element
10882 #endif
10883               )
10884           {
10885             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10886                                      CE_HITTING_X, i);
10887             break;
10888           }
10889         }
10890       }
10891
10892       if (IS_CUSTOM_ELEMENT(touched_element) &&
10893           HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
10894       {
10895         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10896         {
10897           struct ElementChangeInfo *change =
10898             &element_info[touched_element].change_page[i];
10899
10900           if (change->can_change &&
10901               change->has_event[CE_HIT_BY_X] &&
10902               change->trigger_side & hitting_side &&
10903 #if 1
10904               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10905 #else
10906               change->trigger_element == hitting_element
10907 #endif
10908               )
10909           {
10910             CheckElementChangeByPage(hitx, hity, touched_element,
10911                                      hitting_element, CE_HIT_BY_X, i);
10912             break;
10913           }
10914         }
10915       }
10916
10917 #if USE_HIT_BY_SOMETHING_BUGFIX
10918       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10919                                CE_HIT_BY_SOMETHING, opposite_direction);
10920 #endif
10921     }
10922   }
10923
10924 #if USE_HITTING_SOMETHING_BUGFIX
10925   /* "hitting something" is also true when hitting the playfield border */
10926   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10927                            CE_HITTING_SOMETHING, direction);
10928 #endif
10929 }
10930
10931 #if 0
10932 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10933 {
10934   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10935   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10936   int hitx = x + dx, hity = y + dy;
10937   int hitting_element = Feld[x][y];
10938   int touched_element;
10939 #if 0
10940   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10941                         !IS_FREE(hitx, hity) &&
10942                         (!IS_MOVING(hitx, hity) ||
10943                          MovDir[hitx][hity] != direction ||
10944                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10945 #endif
10946
10947   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10948     return;
10949
10950 #if 0
10951   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10952     return;
10953 #endif
10954
10955   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10956                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10957
10958   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10959                            EP_CAN_SMASH_EVERYTHING, direction);
10960
10961   if (IN_LEV_FIELD(hitx, hity))
10962   {
10963     int opposite_direction = MV_DIR_OPPOSITE(direction);
10964     int hitting_side = direction;
10965     int touched_side = opposite_direction;
10966 #if 0
10967     int touched_element = MovingOrBlocked2Element(hitx, hity);
10968 #endif
10969 #if 1
10970     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10971                           MovDir[hitx][hity] != direction ||
10972                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10973
10974     object_hit = TRUE;
10975 #endif
10976
10977     if (object_hit)
10978     {
10979       int i;
10980
10981       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10982                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10983
10984       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10985           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10986       {
10987         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10988         {
10989           struct ElementChangeInfo *change =
10990             &element_info[hitting_element].change_page[i];
10991
10992           if (change->can_change &&
10993               change->has_event[CE_OTHER_IS_SMASHING] &&
10994               change->trigger_side & touched_side &&
10995           
10996 #if 1
10997               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10998 #else
10999               change->trigger_element == touched_element
11000 #endif
11001               )
11002           {
11003             CheckElementChangeByPage(x, y, hitting_element, touched_element,
11004                                      CE_OTHER_IS_SMASHING, i);
11005             break;
11006           }
11007         }
11008       }
11009
11010       if (IS_CUSTOM_ELEMENT(touched_element) &&
11011           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
11012       {
11013         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
11014         {
11015           struct ElementChangeInfo *change =
11016             &element_info[touched_element].change_page[i];
11017
11018           if (change->can_change &&
11019               change->has_event[CE_OTHER_GETS_SMASHED] &&
11020               change->trigger_side & hitting_side &&
11021 #if 1
11022               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11023 #else
11024               change->trigger_element == hitting_element
11025 #endif
11026               )
11027           {
11028             CheckElementChangeByPage(hitx, hity, touched_element,
11029                                      hitting_element, CE_OTHER_GETS_SMASHED,i);
11030             break;
11031           }
11032         }
11033       }
11034     }
11035   }
11036 }
11037 #endif
11038
11039 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11040 {
11041   int i, kill_x = -1, kill_y = -1;
11042   int bad_element = -1;
11043   static int test_xy[4][2] =
11044   {
11045     { 0, -1 },
11046     { -1, 0 },
11047     { +1, 0 },
11048     { 0, +1 }
11049   };
11050   static int test_dir[4] =
11051   {
11052     MV_UP,
11053     MV_LEFT,
11054     MV_RIGHT,
11055     MV_DOWN
11056   };
11057
11058   for (i = 0; i < NUM_DIRECTIONS; i++)
11059   {
11060     int test_x, test_y, test_move_dir, test_element;
11061
11062     test_x = good_x + test_xy[i][0];
11063     test_y = good_y + test_xy[i][1];
11064
11065     if (!IN_LEV_FIELD(test_x, test_y))
11066       continue;
11067
11068     test_move_dir =
11069       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11070
11071 #if 0
11072     test_element = Feld[test_x][test_y];
11073 #else
11074     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11075 #endif
11076
11077     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11078        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11079     */
11080     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11081         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11082     {
11083       kill_x = test_x;
11084       kill_y = test_y;
11085       bad_element = test_element;
11086
11087       break;
11088     }
11089   }
11090
11091   if (kill_x != -1 || kill_y != -1)
11092   {
11093     if (IS_PLAYER(good_x, good_y))
11094     {
11095       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11096
11097 #if 1
11098       if (player->shield_deadly_time_left > 0 &&
11099           !IS_INDESTRUCTIBLE(bad_element))
11100         Bang(kill_x, kill_y);
11101       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11102         KillHero(player);
11103 #else
11104       if (player->shield_deadly_time_left > 0)
11105         Bang(kill_x, kill_y);
11106       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11107         KillHero(player);
11108 #endif
11109     }
11110     else
11111       Bang(good_x, good_y);
11112   }
11113 }
11114
11115 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11116 {
11117   int i, kill_x = -1, kill_y = -1;
11118   int bad_element = Feld[bad_x][bad_y];
11119   static int test_xy[4][2] =
11120   {
11121     { 0, -1 },
11122     { -1, 0 },
11123     { +1, 0 },
11124     { 0, +1 }
11125   };
11126   static int touch_dir[4] =
11127   {
11128     MV_LEFT | MV_RIGHT,
11129     MV_UP   | MV_DOWN,
11130     MV_UP   | MV_DOWN,
11131     MV_LEFT | MV_RIGHT
11132   };
11133   static int test_dir[4] =
11134   {
11135     MV_UP,
11136     MV_LEFT,
11137     MV_RIGHT,
11138     MV_DOWN
11139   };
11140
11141   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11142     return;
11143
11144   for (i = 0; i < NUM_DIRECTIONS; i++)
11145   {
11146     int test_x, test_y, test_move_dir, test_element;
11147
11148     test_x = bad_x + test_xy[i][0];
11149     test_y = bad_y + test_xy[i][1];
11150     if (!IN_LEV_FIELD(test_x, test_y))
11151       continue;
11152
11153     test_move_dir =
11154       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11155
11156     test_element = Feld[test_x][test_y];
11157
11158     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11159        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11160     */
11161     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11162         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11163     {
11164       /* good thing is player or penguin that does not move away */
11165       if (IS_PLAYER(test_x, test_y))
11166       {
11167         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11168
11169         if (bad_element == EL_ROBOT && player->is_moving)
11170           continue;     /* robot does not kill player if he is moving */
11171
11172         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11173         {
11174           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11175             continue;           /* center and border element do not touch */
11176         }
11177
11178         kill_x = test_x;
11179         kill_y = test_y;
11180         break;
11181       }
11182       else if (test_element == EL_PENGUIN)
11183       {
11184         kill_x = test_x;
11185         kill_y = test_y;
11186         break;
11187       }
11188     }
11189   }
11190
11191   if (kill_x != -1 || kill_y != -1)
11192   {
11193     if (IS_PLAYER(kill_x, kill_y))
11194     {
11195       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11196
11197 #if 1
11198       if (player->shield_deadly_time_left > 0 &&
11199           !IS_INDESTRUCTIBLE(bad_element))
11200         Bang(bad_x, bad_y);
11201       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11202         KillHero(player);
11203 #else
11204       if (player->shield_deadly_time_left > 0)
11205         Bang(bad_x, bad_y);
11206       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11207         KillHero(player);
11208 #endif
11209     }
11210     else
11211       Bang(kill_x, kill_y);
11212   }
11213 }
11214
11215 void TestIfHeroTouchesBadThing(int x, int y)
11216 {
11217   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11218 }
11219
11220 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11221 {
11222   TestIfGoodThingHitsBadThing(x, y, move_dir);
11223 }
11224
11225 void TestIfBadThingTouchesHero(int x, int y)
11226 {
11227   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11228 }
11229
11230 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11231 {
11232   TestIfBadThingHitsGoodThing(x, y, move_dir);
11233 }
11234
11235 void TestIfFriendTouchesBadThing(int x, int y)
11236 {
11237   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11238 }
11239
11240 void TestIfBadThingTouchesFriend(int x, int y)
11241 {
11242   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11243 }
11244
11245 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11246 {
11247   int i, kill_x = bad_x, kill_y = bad_y;
11248   static int xy[4][2] =
11249   {
11250     { 0, -1 },
11251     { -1, 0 },
11252     { +1, 0 },
11253     { 0, +1 }
11254   };
11255
11256   for (i = 0; i < NUM_DIRECTIONS; i++)
11257   {
11258     int x, y, element;
11259
11260     x = bad_x + xy[i][0];
11261     y = bad_y + xy[i][1];
11262     if (!IN_LEV_FIELD(x, y))
11263       continue;
11264
11265     element = Feld[x][y];
11266     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11267         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11268     {
11269       kill_x = x;
11270       kill_y = y;
11271       break;
11272     }
11273   }
11274
11275   if (kill_x != bad_x || kill_y != bad_y)
11276     Bang(bad_x, bad_y);
11277 }
11278
11279 void KillHero(struct PlayerInfo *player)
11280 {
11281   int jx = player->jx, jy = player->jy;
11282
11283   if (!player->active)
11284     return;
11285
11286   /* remove accessible field at the player's position */
11287   Feld[jx][jy] = EL_EMPTY;
11288
11289   /* deactivate shield (else Bang()/Explode() would not work right) */
11290   player->shield_normal_time_left = 0;
11291   player->shield_deadly_time_left = 0;
11292
11293   Bang(jx, jy);
11294   BuryHero(player);
11295 }
11296
11297 static void KillHeroUnlessEnemyProtected(int x, int y)
11298 {
11299   if (!PLAYER_ENEMY_PROTECTED(x, y))
11300     KillHero(PLAYERINFO(x, y));
11301 }
11302
11303 static void KillHeroUnlessExplosionProtected(int x, int y)
11304 {
11305   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11306     KillHero(PLAYERINFO(x, y));
11307 }
11308
11309 void BuryHero(struct PlayerInfo *player)
11310 {
11311   int jx = player->jx, jy = player->jy;
11312
11313   if (!player->active)
11314     return;
11315
11316 #if 1
11317   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11318 #else
11319   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11320 #endif
11321   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11322
11323   player->GameOver = TRUE;
11324   RemoveHero(player);
11325 }
11326
11327 void RemoveHero(struct PlayerInfo *player)
11328 {
11329   int jx = player->jx, jy = player->jy;
11330   int i, found = FALSE;
11331
11332   player->present = FALSE;
11333   player->active = FALSE;
11334
11335   if (!ExplodeField[jx][jy])
11336     StorePlayer[jx][jy] = 0;
11337
11338   for (i = 0; i < MAX_PLAYERS; i++)
11339     if (stored_player[i].active)
11340       found = TRUE;
11341
11342   if (!found)
11343     AllPlayersGone = TRUE;
11344
11345   ExitX = ZX = jx;
11346   ExitY = ZY = jy;
11347 }
11348
11349 /*
11350   =============================================================================
11351   checkDiagonalPushing()
11352   -----------------------------------------------------------------------------
11353   check if diagonal input device direction results in pushing of object
11354   (by checking if the alternative direction is walkable, diggable, ...)
11355   =============================================================================
11356 */
11357
11358 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11359                                     int x, int y, int real_dx, int real_dy)
11360 {
11361   int jx, jy, dx, dy, xx, yy;
11362
11363   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11364     return TRUE;
11365
11366   /* diagonal direction: check alternative direction */
11367   jx = player->jx;
11368   jy = player->jy;
11369   dx = x - jx;
11370   dy = y - jy;
11371   xx = jx + (dx == 0 ? real_dx : 0);
11372   yy = jy + (dy == 0 ? real_dy : 0);
11373
11374   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11375 }
11376
11377 /*
11378   =============================================================================
11379   DigField()
11380   -----------------------------------------------------------------------------
11381   x, y:                 field next to player (non-diagonal) to try to dig to
11382   real_dx, real_dy:     direction as read from input device (can be diagonal)
11383   =============================================================================
11384 */
11385
11386 int DigField(struct PlayerInfo *player,
11387              int oldx, int oldy, int x, int y,
11388              int real_dx, int real_dy, int mode)
11389 {
11390 #if 0
11391   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11392 #endif
11393   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11394   boolean player_was_pushing = player->is_pushing;
11395   int jx = oldx, jy = oldy;
11396   int dx = x - jx, dy = y - jy;
11397   int nextx = x + dx, nexty = y + dy;
11398   int move_direction = (dx == -1 ? MV_LEFT :
11399                         dx == +1 ? MV_RIGHT :
11400                         dy == -1 ? MV_UP :
11401                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11402   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11403 #if 1
11404   int dig_side = MV_DIR_OPPOSITE(move_direction);
11405 #else
11406   static int trigger_sides[4] =
11407   {
11408     CH_SIDE_RIGHT,      /* moving left  */
11409     CH_SIDE_LEFT,       /* moving right */
11410     CH_SIDE_BOTTOM,     /* moving up    */
11411     CH_SIDE_TOP,        /* moving down  */
11412   };
11413   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11414 #endif
11415   int old_element = Feld[jx][jy];
11416   int element;
11417
11418   if (is_player)                /* function can also be called by EL_PENGUIN */
11419   {
11420     if (player->MovPos == 0)
11421     {
11422       player->is_digging = FALSE;
11423       player->is_collecting = FALSE;
11424     }
11425
11426     if (player->MovPos == 0)    /* last pushing move finished */
11427       player->is_pushing = FALSE;
11428
11429     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11430     {
11431       player->is_switching = FALSE;
11432 #if USE_NEW_PUSH_DELAY
11433       player->push_delay = -1;
11434 #else
11435       player->push_delay = 0;
11436 #endif
11437
11438       return MF_NO_ACTION;
11439     }
11440   }
11441
11442   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11443     return MF_NO_ACTION;
11444
11445 #if 0
11446
11447 #if 0
11448   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11449 #else
11450   if (IS_TUBE(Feld[jx][jy]) ||
11451       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11452 #endif
11453   {
11454     int i = 0;
11455     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11456     int tube_leave_directions[][2] =
11457     {
11458       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11459       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
11460       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
11461       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
11462       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
11463       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
11464       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
11465       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
11466       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
11467       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
11468       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
11469       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11470     };
11471
11472     while (tube_leave_directions[i][0] != tube_element)
11473     {
11474       i++;
11475       if (tube_leave_directions[i][0] == -1)    /* should not happen */
11476         break;
11477     }
11478
11479     if (!(tube_leave_directions[i][1] & move_direction))
11480       return MF_NO_ACTION;      /* tube has no opening in this direction */
11481   }
11482
11483 #else
11484
11485   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11486     old_element = Back[jx][jy];
11487 #if USE_BACK_WALKABLE_BUGFIX
11488
11489   /* in case of element dropped at player position, check background */
11490   else if (Back[jx][jy] != EL_EMPTY &&
11491            game.engine_version >= VERSION_IDENT(2,2,0,0))
11492     old_element = Back[jx][jy];
11493 #endif
11494
11495 #endif
11496
11497   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11498     return MF_NO_ACTION;        /* field has no opening in this direction */
11499
11500   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11501     return MF_NO_ACTION;        /* field has no opening in this direction */
11502
11503   element = Feld[x][y];
11504
11505   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11506     return MF_NO_ACTION;
11507
11508   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11509       game.engine_version >= VERSION_IDENT(2,2,0,0))
11510     return MF_NO_ACTION;
11511
11512 #if 1
11513   if (game.gravity && is_player && !player->is_auto_moving &&
11514       canFallDown(player) && move_direction != MV_DOWN &&
11515       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11516     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
11517 #endif
11518
11519 #if 0
11520   if (element == EL_EMPTY_SPACE &&
11521       game.gravity && !player->is_auto_moving &&
11522       canFallDown(player) && move_direction != MV_DOWN)
11523     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
11524 #endif
11525
11526   switch (element)
11527   {
11528 #if 0
11529     case EL_SP_PORT_LEFT:
11530     case EL_SP_PORT_RIGHT:
11531     case EL_SP_PORT_UP:
11532     case EL_SP_PORT_DOWN:
11533     case EL_SP_PORT_HORIZONTAL:
11534     case EL_SP_PORT_VERTICAL:
11535     case EL_SP_PORT_ANY:
11536     case EL_SP_GRAVITY_PORT_LEFT:
11537     case EL_SP_GRAVITY_PORT_RIGHT:
11538     case EL_SP_GRAVITY_PORT_UP:
11539     case EL_SP_GRAVITY_PORT_DOWN:
11540 #if 1
11541       if (!canEnterSupaplexPort(x, y, dx, dy))
11542         return MF_NO_ACTION;
11543 #else
11544       if ((dx == -1 &&
11545            element != EL_SP_PORT_LEFT &&
11546            element != EL_SP_GRAVITY_PORT_LEFT &&
11547            element != EL_SP_PORT_HORIZONTAL &&
11548            element != EL_SP_PORT_ANY) ||
11549           (dx == +1 &&
11550            element != EL_SP_PORT_RIGHT &&
11551            element != EL_SP_GRAVITY_PORT_RIGHT &&
11552            element != EL_SP_PORT_HORIZONTAL &&
11553            element != EL_SP_PORT_ANY) ||
11554           (dy == -1 &&
11555            element != EL_SP_PORT_UP &&
11556            element != EL_SP_GRAVITY_PORT_UP &&
11557            element != EL_SP_PORT_VERTICAL &&
11558            element != EL_SP_PORT_ANY) ||
11559           (dy == +1 &&
11560            element != EL_SP_PORT_DOWN &&
11561            element != EL_SP_GRAVITY_PORT_DOWN &&
11562            element != EL_SP_PORT_VERTICAL &&
11563            element != EL_SP_PORT_ANY) ||
11564           !IN_LEV_FIELD(nextx, nexty) ||
11565           !IS_FREE(nextx, nexty))
11566         return MF_NO_ACTION;
11567 #endif
11568
11569       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11570           element == EL_SP_GRAVITY_PORT_RIGHT ||
11571           element == EL_SP_GRAVITY_PORT_UP ||
11572           element == EL_SP_GRAVITY_PORT_DOWN)
11573         game.gravity = !game.gravity;
11574
11575       /* automatically move to the next field with double speed */
11576       player->programmed_action = move_direction;
11577 #if 1
11578       if (player->move_delay_reset_counter == 0)
11579       {
11580         player->move_delay_reset_counter = 2;   /* two double speed steps */
11581
11582         DOUBLE_PLAYER_SPEED(player);
11583       }
11584 #else
11585       player->move_delay_reset_counter = 2;
11586
11587       DOUBLE_PLAYER_SPEED(player);
11588 #endif
11589
11590 #if 0
11591       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11592 #endif
11593
11594       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11595       break;
11596 #endif
11597
11598 #if 0
11599     case EL_TUBE_ANY:
11600     case EL_TUBE_VERTICAL:
11601     case EL_TUBE_HORIZONTAL:
11602     case EL_TUBE_VERTICAL_LEFT:
11603     case EL_TUBE_VERTICAL_RIGHT:
11604     case EL_TUBE_HORIZONTAL_UP:
11605     case EL_TUBE_HORIZONTAL_DOWN:
11606     case EL_TUBE_LEFT_UP:
11607     case EL_TUBE_LEFT_DOWN:
11608     case EL_TUBE_RIGHT_UP:
11609     case EL_TUBE_RIGHT_DOWN:
11610       {
11611         int i = 0;
11612         int tube_enter_directions[][2] =
11613         {
11614           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11615           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
11616           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
11617           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
11618           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
11619           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
11620           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
11621           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
11622           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
11623           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
11624           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
11625           { -1,                         MV_NO_MOVING                         }
11626         };
11627
11628         while (tube_enter_directions[i][0] != element)
11629         {
11630           i++;
11631           if (tube_enter_directions[i][0] == -1)        /* should not happen */
11632             break;
11633         }
11634
11635         if (!(tube_enter_directions[i][1] & move_direction))
11636           return MF_NO_ACTION;  /* tube has no opening in this direction */
11637
11638         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11639       }
11640       break;
11641 #endif
11642
11643     default:
11644
11645 #if 1
11646       if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11647 #else
11648       if (IS_WALKABLE(element))
11649 #endif
11650       {
11651         int sound_element = SND_ELEMENT(element);
11652         int sound_action = ACTION_WALKING;
11653
11654 #if 0
11655         if (!ACCESS_FROM(element, opposite_direction))
11656           return MF_NO_ACTION;  /* field not accessible from this direction */
11657 #endif
11658
11659 #if 0
11660         if (element == EL_EMPTY_SPACE &&
11661             game.gravity && !player->is_auto_moving &&
11662             canFallDown(player) && move_direction != MV_DOWN)
11663           return MF_NO_ACTION;  /* player cannot walk here due to gravity */
11664 #endif
11665
11666         if (IS_RND_GATE(element))
11667         {
11668           if (!player->key[RND_GATE_NR(element)])
11669             return MF_NO_ACTION;
11670         }
11671         else if (IS_RND_GATE_GRAY(element))
11672         {
11673           if (!player->key[RND_GATE_GRAY_NR(element)])
11674             return MF_NO_ACTION;
11675         }
11676         else if (element == EL_EXIT_OPEN ||
11677                  element == EL_SP_EXIT_OPEN ||
11678                  element == EL_SP_EXIT_OPENING)
11679         {
11680           sound_action = ACTION_PASSING;        /* player is passing exit */
11681         }
11682         else if (element == EL_EMPTY)
11683         {
11684           sound_action = ACTION_MOVING;         /* nothing to walk on */
11685         }
11686
11687         /* play sound from background or player, whatever is available */
11688         if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11689           PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11690         else
11691           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11692
11693         break;
11694       }
11695 #if 1
11696       else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11697 #else
11698       else if (IS_PASSABLE(element))
11699 #endif
11700       {
11701 #if 0
11702         if (!canPassField(x, y, move_direction))
11703           return MF_NO_ACTION;
11704 #else
11705
11706 #if 0
11707 #if 1
11708         if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11709             !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11710             (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11711           return MF_NO_ACTION;
11712 #else
11713         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11714           return MF_NO_ACTION;
11715 #endif
11716 #endif
11717
11718 #if 1
11719         if (!ACCESS_FROM(element, opposite_direction))
11720           return MF_NO_ACTION;  /* field not accessible from this direction */
11721 #else
11722         if (IS_CUSTOM_ELEMENT(element) &&
11723             !ACCESS_FROM(element, opposite_direction))
11724           return MF_NO_ACTION;  /* field not accessible from this direction */
11725 #endif
11726
11727 #if 1
11728         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
11729           return MF_NO_ACTION;
11730 #endif
11731
11732 #endif
11733
11734         if (IS_EM_GATE(element))
11735         {
11736           if (!player->key[EM_GATE_NR(element)])
11737             return MF_NO_ACTION;
11738         }
11739         else if (IS_EM_GATE_GRAY(element))
11740         {
11741           if (!player->key[EM_GATE_GRAY_NR(element)])
11742             return MF_NO_ACTION;
11743         }
11744         else if (IS_SP_PORT(element))
11745         {
11746           if (element == EL_SP_GRAVITY_PORT_LEFT ||
11747               element == EL_SP_GRAVITY_PORT_RIGHT ||
11748               element == EL_SP_GRAVITY_PORT_UP ||
11749               element == EL_SP_GRAVITY_PORT_DOWN)
11750             game.gravity = !game.gravity;
11751           else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11752                    element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11753                    element == EL_SP_GRAVITY_ON_PORT_UP ||
11754                    element == EL_SP_GRAVITY_ON_PORT_DOWN)
11755             game.gravity = TRUE;
11756           else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11757                    element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11758                    element == EL_SP_GRAVITY_OFF_PORT_UP ||
11759                    element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11760             game.gravity = FALSE;
11761         }
11762
11763         /* automatically move to the next field with double speed */
11764         player->programmed_action = move_direction;
11765 #if 1
11766         if (player->move_delay_reset_counter == 0)
11767         {
11768           player->move_delay_reset_counter = 2; /* two double speed steps */
11769
11770           DOUBLE_PLAYER_SPEED(player);
11771         }
11772 #else
11773         player->move_delay_reset_counter = 2;
11774
11775         DOUBLE_PLAYER_SPEED(player);
11776 #endif
11777
11778         PlayLevelSoundAction(x, y, ACTION_PASSING);
11779
11780         break;
11781       }
11782       else if (IS_DIGGABLE(element))
11783       {
11784         RemoveField(x, y);
11785
11786         if (mode != DF_SNAP)
11787         {
11788 #if 1
11789           GfxElement[x][y] = GFX_ELEMENT(element);
11790 #else
11791           GfxElement[x][y] =
11792             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11793 #endif
11794           player->is_digging = TRUE;
11795         }
11796
11797         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11798
11799         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11800                                             player->index_bit, dig_side);
11801
11802 #if 1
11803         if (mode == DF_SNAP)
11804           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11805 #endif
11806
11807         break;
11808       }
11809       else if (IS_COLLECTIBLE(element))
11810       {
11811         RemoveField(x, y);
11812
11813         if (is_player && mode != DF_SNAP)
11814         {
11815           GfxElement[x][y] = element;
11816           player->is_collecting = TRUE;
11817         }
11818
11819         if (element == EL_SPEED_PILL)
11820           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11821         else if (element == EL_EXTRA_TIME && level.time > 0)
11822         {
11823           TimeLeft += 10;
11824           DrawGameValue_Time(TimeLeft);
11825         }
11826         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11827         {
11828           player->shield_normal_time_left += 10;
11829           if (element == EL_SHIELD_DEADLY)
11830             player->shield_deadly_time_left += 10;
11831         }
11832         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11833         {
11834           if (player->inventory_size < MAX_INVENTORY_SIZE)
11835             player->inventory_element[player->inventory_size++] = element;
11836
11837           DrawGameValue_Dynamite(local_player->inventory_size);
11838         }
11839         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11840         {
11841           player->dynabomb_count++;
11842           player->dynabombs_left++;
11843         }
11844         else if (element == EL_DYNABOMB_INCREASE_SIZE)
11845         {
11846           player->dynabomb_size++;
11847         }
11848         else if (element == EL_DYNABOMB_INCREASE_POWER)
11849         {
11850           player->dynabomb_xl = TRUE;
11851         }
11852         else if (IS_KEY(element))
11853         {
11854           player->key[KEY_NR(element)] = TRUE;
11855
11856           DrawGameValue_Keys(player->key);
11857
11858           redraw_mask |= REDRAW_DOOR_1;
11859         }
11860         else if (IS_ENVELOPE(element))
11861         {
11862 #if 1
11863           player->show_envelope = element;
11864 #else
11865           ShowEnvelope(element - EL_ENVELOPE_1);
11866 #endif
11867         }
11868         else if (IS_DROPPABLE(element) ||
11869                  IS_THROWABLE(element)) /* can be collected and dropped */
11870         {
11871           int i;
11872
11873           if (element_info[element].collect_count == 0)
11874             player->inventory_infinite_element = element;
11875           else
11876             for (i = 0; i < element_info[element].collect_count; i++)
11877               if (player->inventory_size < MAX_INVENTORY_SIZE)
11878                 player->inventory_element[player->inventory_size++] = element;
11879
11880           DrawGameValue_Dynamite(local_player->inventory_size);
11881         }
11882         else if (element_info[element].collect_count > 0)
11883         {
11884           local_player->gems_still_needed -=
11885             element_info[element].collect_count;
11886           if (local_player->gems_still_needed < 0)
11887             local_player->gems_still_needed = 0;
11888
11889           DrawGameValue_Emeralds(local_player->gems_still_needed);
11890         }
11891
11892         RaiseScoreElement(element);
11893         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11894
11895         if (is_player)
11896           CheckTriggeredElementChangeByPlayer(x, y, element,
11897                                               CE_PLAYER_COLLECTS_X,
11898                                               player->index_bit, dig_side);
11899
11900 #if 1
11901         if (mode == DF_SNAP)
11902           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11903 #endif
11904
11905         break;
11906       }
11907       else if (IS_PUSHABLE(element))
11908       {
11909         if (mode == DF_SNAP && element != EL_BD_ROCK)
11910           return MF_NO_ACTION;
11911
11912         if (CAN_FALL(element) && dy)
11913           return MF_NO_ACTION;
11914
11915         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11916             !(element == EL_SPRING && level.use_spring_bug))
11917           return MF_NO_ACTION;
11918
11919 #if 1
11920         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11921             ((move_direction & MV_VERTICAL &&
11922               ((element_info[element].move_pattern & MV_LEFT &&
11923                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11924                (element_info[element].move_pattern & MV_RIGHT &&
11925                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11926              (move_direction & MV_HORIZONTAL &&
11927               ((element_info[element].move_pattern & MV_UP &&
11928                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11929                (element_info[element].move_pattern & MV_DOWN &&
11930                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11931           return MF_NO_ACTION;
11932 #endif
11933
11934 #if 1
11935         /* do not push elements already moving away faster than player */
11936         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11937             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11938           return MF_NO_ACTION;
11939 #else
11940         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11941           return MF_NO_ACTION;
11942 #endif
11943
11944 #if 1
11945
11946 #if 1
11947         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11948         {
11949           if (player->push_delay_value == -1 || !player_was_pushing)
11950             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11951         }
11952         else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11953         {
11954           if (player->push_delay_value == -1)
11955             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11956         }
11957 #else
11958         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11959         {
11960           if (player->push_delay_value == -1 || !player_was_pushing)
11961             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11962         }
11963 #endif
11964         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11965         {
11966           if (!player->is_pushing)
11967             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11968         }
11969
11970         /*
11971         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11972             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11973              !player_is_pushing))
11974           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11975         */
11976 #else
11977         if (!player->is_pushing &&
11978             game.engine_version >= VERSION_IDENT(2,2,0,7))
11979           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11980 #endif
11981
11982 #if 0
11983         printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11984                player->push_delay, player->push_delay_value,
11985                FrameCounter, game.engine_version,
11986                player_was_pushing, player->is_pushing,
11987                element, element_info[element].token_name,
11988                GET_NEW_PUSH_DELAY(element));
11989 #endif
11990
11991         player->is_pushing = TRUE;
11992
11993         if (!(IN_LEV_FIELD(nextx, nexty) &&
11994               (IS_FREE(nextx, nexty) ||
11995                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11996                 IS_SB_ELEMENT(element)))))
11997           return MF_NO_ACTION;
11998
11999         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12000           return MF_NO_ACTION;
12001
12002 #if USE_NEW_PUSH_DELAY
12003
12004 #if 0
12005         if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
12006           printf("::: ALERT: %d, %d [%d / %d]\n",
12007                  player->push_delay, player->push_delay2,
12008                  FrameCounter, FrameCounter / 50);
12009 #endif
12010
12011         if (player->push_delay == -1)   /* new pushing; restart delay */
12012           player->push_delay = 0;
12013 #else
12014         if (player->push_delay == 0)    /* new pushing; restart delay */
12015           player->push_delay = FrameCounter;
12016 #endif
12017
12018 #if USE_NEW_PUSH_DELAY
12019 #if 0
12020         if ( (player->push_delay > 0) != (!xxx_fr) )
12021           printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
12022                  player->push_delay,
12023                  xxx_pdv2, player->push_delay2, player->push_delay_value,
12024                  FrameCounter, FrameCounter / 50);
12025 #endif
12026
12027 #if 0
12028         if (player->push_delay > 0 &&
12029             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12030             element != EL_SPRING && element != EL_BALLOON)
12031 #else
12032         /* !!! */
12033         if (player->push_delay < player->push_delay_value &&
12034             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12035             element != EL_SPRING && element != EL_BALLOON)
12036 #endif
12037
12038 #else
12039         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
12040             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12041             element != EL_SPRING && element != EL_BALLOON)
12042 #endif
12043         {
12044           /* make sure that there is no move delay before next try to push */
12045 #if USE_NEW_MOVE_DELAY
12046           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12047             player->move_delay = 0;
12048 #else
12049           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12050             player->move_delay = INITIAL_MOVE_DELAY_OFF;
12051 #endif
12052
12053           return MF_NO_ACTION;
12054         }
12055
12056 #if 0
12057         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12058 #endif
12059
12060         if (IS_SB_ELEMENT(element))
12061         {
12062           if (element == EL_SOKOBAN_FIELD_FULL)
12063           {
12064             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12065             local_player->sokobanfields_still_needed++;
12066           }
12067
12068           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12069           {
12070             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12071             local_player->sokobanfields_still_needed--;
12072           }
12073
12074           Feld[x][y] = EL_SOKOBAN_OBJECT;
12075
12076           if (Back[x][y] == Back[nextx][nexty])
12077             PlayLevelSoundAction(x, y, ACTION_PUSHING);
12078           else if (Back[x][y] != 0)
12079             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12080                                         ACTION_EMPTYING);
12081           else
12082             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12083                                         ACTION_FILLING);
12084
12085           if (local_player->sokobanfields_still_needed == 0 &&
12086               game.emulation == EMU_SOKOBAN)
12087           {
12088             player->LevelSolved = player->GameOver = TRUE;
12089             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12090           }
12091         }
12092         else
12093           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12094
12095         InitMovingField(x, y, move_direction);
12096         GfxAction[x][y] = ACTION_PUSHING;
12097
12098         if (mode == DF_SNAP)
12099           ContinueMoving(x, y);
12100         else
12101           MovPos[x][y] = (dx != 0 ? dx : dy);
12102
12103         Pushed[x][y] = TRUE;
12104         Pushed[nextx][nexty] = TRUE;
12105
12106         if (game.engine_version < VERSION_IDENT(2,2,0,7))
12107           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12108         else
12109           player->push_delay_value = -1;        /* get new value later */
12110
12111 #if USE_PUSH_BUGFIX
12112         /* now: check for element change _after_ element has been pushed! */
12113 #if 1
12114         if (game.use_change_when_pushing_bug)
12115 #else
12116         if (game.engine_version < VERSION_IDENT(3,1,0,0))
12117 #endif
12118         {
12119           CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12120                                      player->index_bit, dig_side);
12121           CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12122                                               player->index_bit, dig_side);
12123         }
12124
12125 #else
12126
12127 #if 1
12128         /* check for element change _after_ element has been pushed! */
12129 #else
12130
12131 #if 1
12132         /* !!! TEST ONLY !!! */
12133         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12134                                    player->index_bit, dig_side);
12135         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12136                                             player->index_bit, dig_side);
12137 #else
12138         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12139                                             player->index_bit, dig_side);
12140         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12141                                    player->index_bit, dig_side);
12142 #endif
12143 #endif
12144
12145 #endif
12146
12147         break;
12148       }
12149       else if (IS_SWITCHABLE(element))
12150       {
12151         if (PLAYER_SWITCHING(player, x, y))
12152         {
12153           CheckTriggeredElementChangeByPlayer(x,y, element,
12154                                               CE_PLAYER_PRESSES_X,
12155                                               player->index_bit, dig_side);
12156
12157           return MF_ACTION;
12158         }
12159
12160         player->is_switching = TRUE;
12161         player->switch_x = x;
12162         player->switch_y = y;
12163
12164         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12165
12166         if (element == EL_ROBOT_WHEEL)
12167         {
12168           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12169           ZX = x;
12170           ZY = y;
12171
12172           DrawLevelField(x, y);
12173         }
12174         else if (element == EL_SP_TERMINAL)
12175         {
12176           int xx, yy;
12177
12178           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12179           {
12180             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12181               Bang(xx, yy);
12182             else if (Feld[xx][yy] == EL_SP_TERMINAL)
12183               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12184           }
12185         }
12186         else if (IS_BELT_SWITCH(element))
12187         {
12188           ToggleBeltSwitch(x, y);
12189         }
12190         else if (element == EL_SWITCHGATE_SWITCH_UP ||
12191                  element == EL_SWITCHGATE_SWITCH_DOWN)
12192         {
12193           ToggleSwitchgateSwitch(x, y);
12194         }
12195         else if (element == EL_LIGHT_SWITCH ||
12196                  element == EL_LIGHT_SWITCH_ACTIVE)
12197         {
12198           ToggleLightSwitch(x, y);
12199
12200 #if 0
12201           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12202                          SND_LIGHT_SWITCH_ACTIVATING :
12203                          SND_LIGHT_SWITCH_DEACTIVATING);
12204 #endif
12205         }
12206         else if (element == EL_TIMEGATE_SWITCH)
12207         {
12208           ActivateTimegateSwitch(x, y);
12209         }
12210         else if (element == EL_BALLOON_SWITCH_LEFT ||
12211                  element == EL_BALLOON_SWITCH_RIGHT ||
12212                  element == EL_BALLOON_SWITCH_UP ||
12213                  element == EL_BALLOON_SWITCH_DOWN ||
12214                  element == EL_BALLOON_SWITCH_ANY)
12215         {
12216           if (element == EL_BALLOON_SWITCH_ANY)
12217             game.balloon_dir = move_direction;
12218           else
12219             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
12220                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12221                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
12222                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
12223                                 MV_NO_MOVING);
12224         }
12225         else if (element == EL_LAMP)
12226         {
12227           Feld[x][y] = EL_LAMP_ACTIVE;
12228           local_player->lights_still_needed--;
12229
12230           ResetGfxAnimation(x, y);
12231           DrawLevelField(x, y);
12232         }
12233         else if (element == EL_TIME_ORB_FULL)
12234         {
12235           Feld[x][y] = EL_TIME_ORB_EMPTY;
12236           TimeLeft += 10;
12237           DrawGameValue_Time(TimeLeft);
12238
12239           ResetGfxAnimation(x, y);
12240           DrawLevelField(x, y);
12241
12242 #if 0
12243           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12244 #endif
12245         }
12246
12247         CheckTriggeredElementChangeByPlayer(x, y, element,
12248                                             CE_SWITCH_OF_X,
12249                                             player->index_bit, dig_side);
12250
12251         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12252                                             player->index_bit, dig_side);
12253
12254         return MF_ACTION;
12255       }
12256       else
12257       {
12258         if (!PLAYER_SWITCHING(player, x, y))
12259         {
12260           player->is_switching = TRUE;
12261           player->switch_x = x;
12262           player->switch_y = y;
12263
12264 #if 1
12265           /* !!! TEST ONLY !!! */
12266           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12267                                      player->index_bit, dig_side);
12268           CheckTriggeredElementChangeByPlayer(x, y, element,
12269                                               CE_SWITCH_OF_X,
12270                                               player->index_bit, dig_side);
12271 #else
12272           CheckTriggeredElementChangeByPlayer(x, y, element,
12273                                               CE_SWITCH_OF_X,
12274                                               player->index_bit, dig_side);
12275           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12276                                      player->index_bit, dig_side);
12277 #endif
12278         }
12279
12280 #if 1
12281         /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12282         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12283                                    player->index_bit, dig_side);
12284         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12285                                             player->index_bit, dig_side);
12286 #else
12287         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12288                                             player->index_bit, dig_side);
12289         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12290                                    player->index_bit, dig_side);
12291 #endif
12292       }
12293
12294       return MF_NO_ACTION;
12295   }
12296
12297 #if USE_NEW_PUSH_DELAY
12298   player->push_delay = -1;
12299 #else
12300   player->push_delay = 0;
12301 #endif
12302
12303 #if USE_PENGUIN_COLLECT_BUGFIX
12304   if (is_player)                /* function can also be called by EL_PENGUIN */
12305 #endif
12306   {
12307     if (Feld[x][y] != element)          /* really digged/collected something */
12308       player->is_collecting = !player->is_digging;
12309   }
12310
12311   return MF_MOVING;
12312 }
12313
12314 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12315 {
12316   int jx = player->jx, jy = player->jy;
12317   int x = jx + dx, y = jy + dy;
12318   int snap_direction = (dx == -1 ? MV_LEFT :
12319                         dx == +1 ? MV_RIGHT :
12320                         dy == -1 ? MV_UP :
12321                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
12322
12323 #if 0
12324   if (player->MovPos != 0)
12325     return FALSE;
12326 #else
12327   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12328     return FALSE;
12329 #endif
12330
12331   if (!player->active || !IN_LEV_FIELD(x, y))
12332     return FALSE;
12333
12334   if (dx && dy)
12335     return FALSE;
12336
12337   if (!dx && !dy)
12338   {
12339     if (player->MovPos == 0)
12340       player->is_pushing = FALSE;
12341
12342     player->is_snapping = FALSE;
12343
12344     if (player->MovPos == 0)
12345     {
12346       player->is_moving = FALSE;
12347       player->is_digging = FALSE;
12348       player->is_collecting = FALSE;
12349     }
12350
12351     return FALSE;
12352   }
12353
12354   if (player->is_snapping)
12355     return FALSE;
12356
12357   player->MovDir = snap_direction;
12358
12359 #if 1
12360   if (player->MovPos == 0)
12361 #endif
12362   {
12363     player->is_moving = FALSE;
12364     player->is_digging = FALSE;
12365     player->is_collecting = FALSE;
12366   }
12367
12368   player->is_dropping = FALSE;
12369
12370   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12371     return FALSE;
12372
12373   player->is_snapping = TRUE;
12374
12375 #if 1
12376   if (player->MovPos == 0)
12377 #endif
12378   {
12379     player->is_moving = FALSE;
12380     player->is_digging = FALSE;
12381     player->is_collecting = FALSE;
12382   }
12383
12384 #if 1
12385   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12386     DrawLevelField(player->last_jx, player->last_jy);
12387 #endif
12388
12389   DrawLevelField(x, y);
12390
12391 #if 0
12392   BackToFront();
12393 #endif
12394
12395   return TRUE;
12396 }
12397
12398 boolean DropElement(struct PlayerInfo *player)
12399 {
12400   int old_element, new_element;
12401   int dropx = player->jx, dropy = player->jy;
12402   int drop_direction = player->MovDir;
12403 #if 1
12404   int drop_side = drop_direction;
12405 #else
12406   static int trigger_sides[4] =
12407   {
12408     CH_SIDE_LEFT,       /* dropping left  */
12409     CH_SIDE_RIGHT,      /* dropping right */
12410     CH_SIDE_TOP,        /* dropping up    */
12411     CH_SIDE_BOTTOM,     /* dropping down  */
12412   };
12413   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12414 #endif
12415   int drop_element = (player->inventory_size > 0 ?
12416                       player->inventory_element[player->inventory_size - 1] :
12417                       player->inventory_infinite_element != EL_UNDEFINED ?
12418                       player->inventory_infinite_element :
12419                       player->dynabombs_left > 0 ?
12420                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12421                       EL_UNDEFINED);
12422
12423 #if USE_DROP_BUGFIX
12424   /* do not drop an element on top of another element; when holding drop key
12425      pressed without moving, dropped element must move away before the next
12426      element can be dropped (this is especially important if the next element
12427      is dynamite, which can be placed on background for historical reasons) */
12428   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12429     return MF_ACTION;
12430 #endif
12431
12432   if (IS_THROWABLE(drop_element))
12433   {
12434     dropx += GET_DX_FROM_DIR(drop_direction);
12435     dropy += GET_DY_FROM_DIR(drop_direction);
12436
12437     if (!IN_LEV_FIELD(dropx, dropy))
12438       return FALSE;
12439   }
12440
12441   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12442   new_element = drop_element;           /* default: no change when dropping */
12443
12444   /* check if player is active, not moving and ready to drop */
12445   if (!player->active || player->MovPos || player->drop_delay > 0)
12446     return FALSE;
12447
12448   /* check if player has anything that can be dropped */
12449 #if 1
12450   if (new_element == EL_UNDEFINED)
12451     return FALSE;
12452 #else
12453   if (player->inventory_size == 0 &&
12454       player->inventory_infinite_element == EL_UNDEFINED &&
12455       player->dynabombs_left == 0)
12456     return FALSE;
12457 #endif
12458
12459   /* check if anything can be dropped at the current position */
12460   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12461     return FALSE;
12462
12463   /* collected custom elements can only be dropped on empty fields */
12464 #if 1
12465   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12466     return FALSE;
12467 #else
12468   if (player->inventory_size > 0 &&
12469       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12470       && old_element != EL_EMPTY)
12471     return FALSE;
12472 #endif
12473
12474   if (old_element != EL_EMPTY)
12475     Back[dropx][dropy] = old_element;   /* store old element on this field */
12476
12477   ResetGfxAnimation(dropx, dropy);
12478   ResetRandomAnimationValue(dropx, dropy);
12479
12480   if (player->inventory_size > 0 ||
12481       player->inventory_infinite_element != EL_UNDEFINED)
12482   {
12483     if (player->inventory_size > 0)
12484     {
12485       player->inventory_size--;
12486
12487 #if 0
12488       new_element = player->inventory_element[player->inventory_size];
12489 #endif
12490
12491       DrawGameValue_Dynamite(local_player->inventory_size);
12492
12493       if (new_element == EL_DYNAMITE)
12494         new_element = EL_DYNAMITE_ACTIVE;
12495       else if (new_element == EL_SP_DISK_RED)
12496         new_element = EL_SP_DISK_RED_ACTIVE;
12497     }
12498
12499     Feld[dropx][dropy] = new_element;
12500
12501     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12502       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12503                           el2img(Feld[dropx][dropy]), 0);
12504
12505     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12506
12507 #if 1
12508     /* needed if previous element just changed to "empty" in the last frame */
12509     Changed[dropx][dropy] = FALSE;              /* allow another change */
12510 #endif
12511
12512 #if 1
12513     /* !!! TEST ONLY !!! */
12514     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12515                                player->index_bit, drop_side);
12516     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12517                                         CE_PLAYER_DROPS_X,
12518                                         player->index_bit, drop_side);
12519 #else
12520     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12521                                         CE_PLAYER_DROPS_X,
12522                                         player->index_bit, drop_side);
12523     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12524                                player->index_bit, drop_side);
12525 #endif
12526
12527     TestIfElementTouchesCustomElement(dropx, dropy);
12528   }
12529   else          /* player is dropping a dyna bomb */
12530   {
12531     player->dynabombs_left--;
12532
12533 #if 0
12534     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12535 #endif
12536
12537     Feld[dropx][dropy] = new_element;
12538
12539     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12540       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12541                           el2img(Feld[dropx][dropy]), 0);
12542
12543     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12544   }
12545
12546
12547
12548 #if 1
12549
12550   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12551   {
12552 #if 1
12553     InitField_WithBug1(dropx, dropy, FALSE);
12554 #else
12555     InitField(dropx, dropy, FALSE);
12556     if (CAN_MOVE(Feld[dropx][dropy]))
12557       InitMovDir(dropx, dropy);
12558 #endif
12559   }
12560
12561   new_element = Feld[dropx][dropy];     /* element might have changed */
12562
12563   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12564       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12565   {
12566 #if 0
12567     int move_stepsize = element_info[new_element].move_stepsize;
12568 #endif
12569     int move_direction, nextx, nexty;
12570
12571     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12572       MovDir[dropx][dropy] = drop_direction;
12573
12574     move_direction = MovDir[dropx][dropy];
12575     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12576     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12577
12578 #if 1
12579     Changed[dropx][dropy] = FALSE;              /* allow another change */
12580     CheckCollision[dropx][dropy] = 2;
12581 #else
12582
12583     if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12584     {
12585 #if 0
12586       WasJustMoving[dropx][dropy] = 3;
12587 #else
12588 #if 1
12589       InitMovingField(dropx, dropy, move_direction);
12590       ContinueMoving(dropx, dropy);
12591 #endif
12592 #endif
12593     }
12594 #if 0
12595     /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12596     else
12597     {
12598       Changed[dropx][dropy] = FALSE;    /* allow another change */
12599
12600 #if 1
12601       TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12602 #else
12603       CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12604                                CE_HITTING_SOMETHING, move_direction);
12605 #endif
12606     }
12607 #endif
12608
12609 #endif
12610
12611 #if 0
12612     player->drop_delay = 2 * TILEX / move_stepsize + 1;
12613 #endif
12614   }
12615
12616 #if 0
12617   player->drop_delay = 8 + 8 + 8;
12618 #endif
12619
12620 #if 1
12621   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12622 #endif
12623
12624 #endif
12625
12626   player->is_dropping = TRUE;
12627
12628 #if USE_DROP_BUGFIX
12629   player->drop_x = dropx;
12630   player->drop_y = dropy;
12631 #endif
12632
12633   return TRUE;
12634 }
12635
12636 /* ------------------------------------------------------------------------- */
12637 /* game sound playing functions                                              */
12638 /* ------------------------------------------------------------------------- */
12639
12640 static int *loop_sound_frame = NULL;
12641 static int *loop_sound_volume = NULL;
12642
12643 void InitPlayLevelSound()
12644 {
12645   int num_sounds = getSoundListSize();
12646
12647   checked_free(loop_sound_frame);
12648   checked_free(loop_sound_volume);
12649
12650   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12651   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12652 }
12653
12654 static void PlayLevelSound(int x, int y, int nr)
12655 {
12656   int sx = SCREENX(x), sy = SCREENY(y);
12657   int volume, stereo_position;
12658   int max_distance = 8;
12659   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12660
12661   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12662       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12663     return;
12664
12665   if (!IN_LEV_FIELD(x, y) ||
12666       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12667       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12668     return;
12669
12670   volume = SOUND_MAX_VOLUME;
12671
12672   if (!IN_SCR_FIELD(sx, sy))
12673   {
12674     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12675     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12676
12677     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12678   }
12679
12680   stereo_position = (SOUND_MAX_LEFT +
12681                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12682                      (SCR_FIELDX + 2 * max_distance));
12683
12684   if (IS_LOOP_SOUND(nr))
12685   {
12686     /* This assures that quieter loop sounds do not overwrite louder ones,
12687        while restarting sound volume comparison with each new game frame. */
12688
12689     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12690       return;
12691
12692     loop_sound_volume[nr] = volume;
12693     loop_sound_frame[nr] = FrameCounter;
12694   }
12695
12696   PlaySoundExt(nr, volume, stereo_position, type);
12697 }
12698
12699 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12700 {
12701   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12702                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12703                  y < LEVELY(BY1) ? LEVELY(BY1) :
12704                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12705                  sound_action);
12706 }
12707
12708 static void PlayLevelSoundAction(int x, int y, int action)
12709 {
12710   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12711 }
12712
12713 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12714 {
12715   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12716
12717   if (sound_effect != SND_UNDEFINED)
12718     PlayLevelSound(x, y, sound_effect);
12719 }
12720
12721 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12722                                               int action)
12723 {
12724   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12725
12726   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12727     PlayLevelSound(x, y, sound_effect);
12728 }
12729
12730 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12731 {
12732   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12733
12734   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12735     PlayLevelSound(x, y, sound_effect);
12736 }
12737
12738 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12739 {
12740   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12741
12742   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12743     StopSound(sound_effect);
12744 }
12745
12746 static void PlayLevelMusic()
12747 {
12748   if (levelset.music[level_nr] != MUS_UNDEFINED)
12749     PlayMusic(levelset.music[level_nr]);        /* from config file */
12750   else
12751     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12752 }
12753
12754 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12755 {
12756   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12757
12758 #if 0
12759   if (sample == SAMPLE_bug)
12760     printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
12761 #endif
12762
12763   switch (sample)
12764   {
12765     case SAMPLE_blank:
12766       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12767       break;
12768
12769     case SAMPLE_roll:
12770       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12771       break;
12772
12773     case SAMPLE_stone:
12774       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12775       break;
12776
12777     case SAMPLE_nut:
12778       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12779       break;
12780
12781     case SAMPLE_crack:
12782       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12783       break;
12784
12785     case SAMPLE_bug:
12786       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12787       break;
12788
12789     case SAMPLE_tank:
12790       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12791       break;
12792
12793     case SAMPLE_android_clone:
12794       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12795       break;
12796
12797     case SAMPLE_android_move:
12798       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12799       break;
12800
12801     case SAMPLE_spring:
12802       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12803       break;
12804
12805     case SAMPLE_slurp:
12806       PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
12807       break;
12808
12809     case SAMPLE_eater:
12810       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12811       break;
12812
12813     case SAMPLE_eater_eat:
12814       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12815       break;
12816
12817     case SAMPLE_alien:
12818       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12819       break;
12820
12821     case SAMPLE_collect:
12822       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12823       break;
12824
12825     case SAMPLE_diamond:
12826       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12827       break;
12828
12829     case SAMPLE_squash:
12830       /* !!! CHECK THIS !!! */
12831 #if 1
12832       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12833 #else
12834       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12835 #endif
12836       break;
12837
12838     case SAMPLE_wonderfall:
12839       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12840       break;
12841
12842     case SAMPLE_drip:
12843       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12844       break;
12845
12846     case SAMPLE_push:
12847       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12848       break;
12849
12850     case SAMPLE_dirt:
12851       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12852       break;
12853
12854     case SAMPLE_acid:
12855       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12856       break;
12857
12858     case SAMPLE_ball:
12859       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12860       break;
12861
12862     case SAMPLE_grow:
12863       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12864       break;
12865
12866     case SAMPLE_wonder:
12867       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12868       break;
12869
12870     case SAMPLE_door:
12871       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12872       break;
12873
12874     case SAMPLE_exit_open:
12875       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12876       break;
12877
12878     case SAMPLE_exit_leave:
12879       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12880       break;
12881
12882     case SAMPLE_dynamite:
12883       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12884       break;
12885
12886     case SAMPLE_tick:
12887       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12888       break;
12889
12890     case SAMPLE_press:
12891       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12892       break;
12893
12894     case SAMPLE_wheel:
12895       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12896       break;
12897
12898     case SAMPLE_boom:
12899       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12900       break;
12901
12902     case SAMPLE_die:
12903       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12904       break;
12905
12906     case SAMPLE_time:
12907       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12908       break;
12909
12910     default:
12911       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12912       break;
12913   }
12914 }
12915
12916 void RaiseScore(int value)
12917 {
12918   local_player->score += value;
12919
12920   DrawGameValue_Score(local_player->score);
12921 }
12922
12923 void RaiseScoreElement(int element)
12924 {
12925   switch(element)
12926   {
12927     case EL_EMERALD:
12928     case EL_BD_DIAMOND:
12929     case EL_EMERALD_YELLOW:
12930     case EL_EMERALD_RED:
12931     case EL_EMERALD_PURPLE:
12932     case EL_SP_INFOTRON:
12933       RaiseScore(level.score[SC_EMERALD]);
12934       break;
12935     case EL_DIAMOND:
12936       RaiseScore(level.score[SC_DIAMOND]);
12937       break;
12938     case EL_CRYSTAL:
12939       RaiseScore(level.score[SC_CRYSTAL]);
12940       break;
12941     case EL_PEARL:
12942       RaiseScore(level.score[SC_PEARL]);
12943       break;
12944     case EL_BUG:
12945     case EL_BD_BUTTERFLY:
12946     case EL_SP_ELECTRON:
12947       RaiseScore(level.score[SC_BUG]);
12948       break;
12949     case EL_SPACESHIP:
12950     case EL_BD_FIREFLY:
12951     case EL_SP_SNIKSNAK:
12952       RaiseScore(level.score[SC_SPACESHIP]);
12953       break;
12954     case EL_YAMYAM:
12955     case EL_DARK_YAMYAM:
12956       RaiseScore(level.score[SC_YAMYAM]);
12957       break;
12958     case EL_ROBOT:
12959       RaiseScore(level.score[SC_ROBOT]);
12960       break;
12961     case EL_PACMAN:
12962       RaiseScore(level.score[SC_PACMAN]);
12963       break;
12964     case EL_NUT:
12965       RaiseScore(level.score[SC_NUT]);
12966       break;
12967     case EL_DYNAMITE:
12968     case EL_SP_DISK_RED:
12969     case EL_DYNABOMB_INCREASE_NUMBER:
12970     case EL_DYNABOMB_INCREASE_SIZE:
12971     case EL_DYNABOMB_INCREASE_POWER:
12972       RaiseScore(level.score[SC_DYNAMITE]);
12973       break;
12974     case EL_SHIELD_NORMAL:
12975     case EL_SHIELD_DEADLY:
12976       RaiseScore(level.score[SC_SHIELD]);
12977       break;
12978     case EL_EXTRA_TIME:
12979       RaiseScore(level.score[SC_TIME_BONUS]);
12980       break;
12981     case EL_KEY_1:
12982     case EL_KEY_2:
12983     case EL_KEY_3:
12984     case EL_KEY_4:
12985     case EL_EM_KEY_1:
12986     case EL_EM_KEY_2:
12987     case EL_EM_KEY_3:
12988     case EL_EM_KEY_4:
12989     case EL_EMC_KEY_5:
12990     case EL_EMC_KEY_6:
12991     case EL_EMC_KEY_7:
12992     case EL_EMC_KEY_8:
12993       RaiseScore(level.score[SC_KEY]);
12994       break;
12995     default:
12996       RaiseScore(element_info[element].collect_score);
12997       break;
12998   }
12999 }
13000
13001 void RequestQuitGame(boolean ask_if_really_quit)
13002 {
13003   if (AllPlayersGone ||
13004       !ask_if_really_quit ||
13005       level_editor_test_game ||
13006       Request("Do you really want to quit the game ?",
13007               REQ_ASK | REQ_STAY_CLOSED))
13008   {
13009 #if defined(NETWORK_AVALIABLE)
13010     if (options.network)
13011       SendToServer_StopPlaying();
13012     else
13013 #endif
13014     {
13015       game_status = GAME_MODE_MAIN;
13016       DrawMainMenu();
13017     }
13018   }
13019   else
13020   {
13021
13022 #if 1
13023     if (tape.playing && tape.deactivate_display)
13024       TapeDeactivateDisplayOff(TRUE);
13025 #endif
13026
13027     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13028
13029 #if 1
13030     if (tape.playing && tape.deactivate_display)
13031       TapeDeactivateDisplayOn();
13032 #endif
13033
13034   }
13035 }
13036
13037
13038 /* ---------- new game button stuff ---------------------------------------- */
13039
13040 /* graphic position values for game buttons */
13041 #define GAME_BUTTON_XSIZE       30
13042 #define GAME_BUTTON_YSIZE       30
13043 #define GAME_BUTTON_XPOS        5
13044 #define GAME_BUTTON_YPOS        215
13045 #define SOUND_BUTTON_XPOS       5
13046 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13047
13048 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13049 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13050 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13051 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13052 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13053 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13054
13055 static struct
13056 {
13057   int x, y;
13058   int gadget_id;
13059   char *infotext;
13060 } gamebutton_info[NUM_GAME_BUTTONS] =
13061 {
13062   {
13063     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13064     GAME_CTRL_ID_STOP,
13065     "stop game"
13066   },
13067   {
13068     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13069     GAME_CTRL_ID_PAUSE,
13070     "pause game"
13071   },
13072   {
13073     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13074     GAME_CTRL_ID_PLAY,
13075     "play game"
13076   },
13077   {
13078     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13079     SOUND_CTRL_ID_MUSIC,
13080     "background music on/off"
13081   },
13082   {
13083     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13084     SOUND_CTRL_ID_LOOPS,
13085     "sound loops on/off"
13086   },
13087   {
13088     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13089     SOUND_CTRL_ID_SIMPLE,
13090     "normal sounds on/off"
13091   }
13092 };
13093
13094 void CreateGameButtons()
13095 {
13096   int i;
13097
13098   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13099   {
13100     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13101     struct GadgetInfo *gi;
13102     int button_type;
13103     boolean checked;
13104     unsigned long event_mask;
13105     int gd_xoffset, gd_yoffset;
13106     int gd_x1, gd_x2, gd_y1, gd_y2;
13107     int id = i;
13108
13109     gd_xoffset = gamebutton_info[i].x;
13110     gd_yoffset = gamebutton_info[i].y;
13111     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13112     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13113
13114     if (id == GAME_CTRL_ID_STOP ||
13115         id == GAME_CTRL_ID_PAUSE ||
13116         id == GAME_CTRL_ID_PLAY)
13117     {
13118       button_type = GD_TYPE_NORMAL_BUTTON;
13119       checked = FALSE;
13120       event_mask = GD_EVENT_RELEASED;
13121       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13122       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13123     }
13124     else
13125     {
13126       button_type = GD_TYPE_CHECK_BUTTON;
13127       checked =
13128         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13129          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13130          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13131       event_mask = GD_EVENT_PRESSED;
13132       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13133       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13134     }
13135
13136     gi = CreateGadget(GDI_CUSTOM_ID, id,
13137                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13138                       GDI_X, DX + gd_xoffset,
13139                       GDI_Y, DY + gd_yoffset,
13140                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13141                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13142                       GDI_TYPE, button_type,
13143                       GDI_STATE, GD_BUTTON_UNPRESSED,
13144                       GDI_CHECKED, checked,
13145                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13146                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13147                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13148                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13149                       GDI_EVENT_MASK, event_mask,
13150                       GDI_CALLBACK_ACTION, HandleGameButtons,
13151                       GDI_END);
13152
13153     if (gi == NULL)
13154       Error(ERR_EXIT, "cannot create gadget");
13155
13156     game_gadget[id] = gi;
13157   }
13158 }
13159
13160 void FreeGameButtons()
13161 {
13162   int i;
13163
13164   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13165     FreeGadget(game_gadget[i]);
13166 }
13167
13168 static void MapGameButtons()
13169 {
13170   int i;
13171
13172   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13173     MapGadget(game_gadget[i]);
13174 }
13175
13176 void UnmapGameButtons()
13177 {
13178   int i;
13179
13180   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13181     UnmapGadget(game_gadget[i]);
13182 }
13183
13184 static void HandleGameButtons(struct GadgetInfo *gi)
13185 {
13186   int id = gi->custom_id;
13187
13188   if (game_status != GAME_MODE_PLAYING)
13189     return;
13190
13191   switch (id)
13192   {
13193     case GAME_CTRL_ID_STOP:
13194       RequestQuitGame(TRUE);
13195       break;
13196
13197     case GAME_CTRL_ID_PAUSE:
13198       if (options.network)
13199       {
13200 #if defined(NETWORK_AVALIABLE)
13201         if (tape.pausing)
13202           SendToServer_ContinuePlaying();
13203         else
13204           SendToServer_PausePlaying();
13205 #endif
13206       }
13207       else
13208         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13209       break;
13210
13211     case GAME_CTRL_ID_PLAY:
13212       if (tape.pausing)
13213       {
13214 #if defined(NETWORK_AVALIABLE)
13215         if (options.network)
13216           SendToServer_ContinuePlaying();
13217         else
13218 #endif
13219         {
13220           tape.pausing = FALSE;
13221           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13222         }
13223       }
13224       break;
13225
13226     case SOUND_CTRL_ID_MUSIC:
13227       if (setup.sound_music)
13228       { 
13229         setup.sound_music = FALSE;
13230         FadeMusic();
13231       }
13232       else if (audio.music_available)
13233       { 
13234         setup.sound = setup.sound_music = TRUE;
13235
13236         SetAudioMode(setup.sound);
13237
13238         PlayLevelMusic();
13239       }
13240       break;
13241
13242     case SOUND_CTRL_ID_LOOPS:
13243       if (setup.sound_loops)
13244         setup.sound_loops = FALSE;
13245       else if (audio.loops_available)
13246       {
13247         setup.sound = setup.sound_loops = TRUE;
13248         SetAudioMode(setup.sound);
13249       }
13250       break;
13251
13252     case SOUND_CTRL_ID_SIMPLE:
13253       if (setup.sound_simple)
13254         setup.sound_simple = FALSE;
13255       else if (audio.sound_available)
13256       {
13257         setup.sound = setup.sound_simple = TRUE;
13258         SetAudioMode(setup.sound);
13259       }
13260       break;
13261
13262     default:
13263       break;
13264   }
13265 }