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