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