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