rnd-20060128-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_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 InitDiagonalMovingElement(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_DIAGONAL_SHRINKING,
521     EL_EMPTY,
522     0,
523     NULL,
524     NULL,
525     NULL
526   },
527   {
528     EL_DIAGONAL_GROWING,
529     EL_EMPTY,
530     0,
531     NULL,
532     NULL,
533     InitDiagonalMovingElement
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         /* first check start_pos, then previous/next or (next/previous) pos */
4916         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
4917         int pos = (pos_raw + 8) % 8;
4918         int newx = x + check_xy[pos].dx;
4919         int newy = y + check_xy[pos].dy;
4920         int new_move_dir = check_xy[pos].dir;
4921
4922         if (IS_PLAYER(newx, newy))
4923           return;
4924
4925         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
4926         {
4927           MovDir[x][y] = new_move_dir;
4928
4929           return;
4930         }
4931       }
4932     }
4933   }
4934   else if (move_pattern == MV_TURNING_LEFT ||
4935            move_pattern == MV_TURNING_RIGHT ||
4936            move_pattern == MV_TURNING_LEFT_RIGHT ||
4937            move_pattern == MV_TURNING_RIGHT_LEFT ||
4938            move_pattern == MV_TURNING_RANDOM ||
4939            move_pattern == MV_ALL_DIRECTIONS)
4940   {
4941     boolean can_turn_left =
4942       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4943     boolean can_turn_right =
4944       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4945
4946     if (element_info[element].move_stepsize == 0)       /* "not moving" */
4947       return;
4948
4949     if (move_pattern == MV_TURNING_LEFT)
4950       MovDir[x][y] = left_dir;
4951     else if (move_pattern == MV_TURNING_RIGHT)
4952       MovDir[x][y] = right_dir;
4953     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4954       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4955     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4956       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4957     else if (move_pattern == MV_TURNING_RANDOM)
4958       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4959                       can_turn_right && !can_turn_left ? right_dir :
4960                       RND(2) ? left_dir : right_dir);
4961     else if (can_turn_left && can_turn_right)
4962       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4963     else if (can_turn_left)
4964       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4965     else if (can_turn_right)
4966       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4967     else
4968       MovDir[x][y] = back_dir;
4969
4970     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4971   }
4972   else if (move_pattern == MV_HORIZONTAL ||
4973            move_pattern == MV_VERTICAL)
4974   {
4975     if (move_pattern & old_move_dir)
4976       MovDir[x][y] = back_dir;
4977     else if (move_pattern == MV_HORIZONTAL)
4978       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4979     else if (move_pattern == MV_VERTICAL)
4980       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4981
4982     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4983   }
4984   else if (move_pattern & MV_ANY_DIRECTION)
4985   {
4986     MovDir[x][y] = move_pattern;
4987     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4988   }
4989   else if (move_pattern & MV_WIND_DIRECTION)
4990   {
4991     MovDir[x][y] = game.wind_direction;
4992     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4993   }
4994   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4995   {
4996     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4997       MovDir[x][y] = left_dir;
4998     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4999       MovDir[x][y] = right_dir;
5000
5001     if (MovDir[x][y] != old_move_dir)
5002       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5003   }
5004   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5005   {
5006     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5007       MovDir[x][y] = right_dir;
5008     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5009       MovDir[x][y] = left_dir;
5010
5011     if (MovDir[x][y] != old_move_dir)
5012       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5013   }
5014   else if (move_pattern == MV_TOWARDS_PLAYER ||
5015            move_pattern == MV_AWAY_FROM_PLAYER)
5016   {
5017     int attr_x = -1, attr_y = -1;
5018     int newx, newy;
5019     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5020
5021     if (AllPlayersGone)
5022     {
5023       attr_x = ExitX;
5024       attr_y = ExitY;
5025     }
5026     else
5027     {
5028       int i;
5029
5030       for (i = 0; i < MAX_PLAYERS; i++)
5031       {
5032         struct PlayerInfo *player = &stored_player[i];
5033         int jx = player->jx, jy = player->jy;
5034
5035         if (!player->active)
5036           continue;
5037
5038         if (attr_x == -1 ||
5039             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5040         {
5041           attr_x = jx;
5042           attr_y = jy;
5043         }
5044       }
5045     }
5046
5047     MovDir[x][y] = MV_NONE;
5048     if (attr_x < x)
5049       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5050     else if (attr_x > x)
5051       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5052     if (attr_y < y)
5053       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5054     else if (attr_y > y)
5055       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5056
5057     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5058
5059     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5060     {
5061       boolean first_horiz = RND(2);
5062       int new_move_dir = MovDir[x][y];
5063
5064       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5065       {
5066         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5067         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5068
5069         return;
5070       }
5071
5072       MovDir[x][y] =
5073         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5074       Moving2Blocked(x, y, &newx, &newy);
5075
5076       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5077         return;
5078
5079       MovDir[x][y] =
5080         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5081       Moving2Blocked(x, y, &newx, &newy);
5082
5083       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5084         return;
5085
5086       MovDir[x][y] = old_move_dir;
5087     }
5088   }
5089   else if (move_pattern == MV_WHEN_PUSHED ||
5090            move_pattern == MV_WHEN_DROPPED)
5091   {
5092     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5093       MovDir[x][y] = MV_NONE;
5094
5095     MovDelay[x][y] = 0;
5096   }
5097   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5098   {
5099     static int test_xy[7][2] =
5100     {
5101       { 0, -1 },
5102       { -1, 0 },
5103       { +1, 0 },
5104       { 0, +1 },
5105       { 0, -1 },
5106       { -1, 0 },
5107       { +1, 0 },
5108     };
5109     static int test_dir[7] =
5110     {
5111       MV_UP,
5112       MV_LEFT,
5113       MV_RIGHT,
5114       MV_DOWN,
5115       MV_UP,
5116       MV_LEFT,
5117       MV_RIGHT,
5118     };
5119     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5120     int move_preference = -1000000;     /* start with very low preference */
5121     int new_move_dir = MV_NONE;
5122     int start_test = RND(4);
5123     int i;
5124
5125     for (i = 0; i < NUM_DIRECTIONS; i++)
5126     {
5127       int move_dir = test_dir[start_test + i];
5128       int move_dir_preference;
5129
5130       xx = x + test_xy[start_test + i][0];
5131       yy = y + test_xy[start_test + i][1];
5132
5133       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5134           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5135       {
5136         new_move_dir = move_dir;
5137
5138         break;
5139       }
5140
5141       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5142         continue;
5143
5144       move_dir_preference = -1 * RunnerVisit[xx][yy];
5145       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5146         move_dir_preference = PlayerVisit[xx][yy];
5147
5148       if (move_dir_preference > move_preference)
5149       {
5150         /* prefer field that has not been visited for the longest time */
5151         move_preference = move_dir_preference;
5152         new_move_dir = move_dir;
5153       }
5154       else if (move_dir_preference == move_preference &&
5155                move_dir == old_move_dir)
5156       {
5157         /* prefer last direction when all directions are preferred equally */
5158         move_preference = move_dir_preference;
5159         new_move_dir = move_dir;
5160       }
5161     }
5162
5163     MovDir[x][y] = new_move_dir;
5164     if (old_move_dir != new_move_dir)
5165       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5166   }
5167 }
5168
5169 static void TurnRound(int x, int y)
5170 {
5171   int direction = MovDir[x][y];
5172
5173   TurnRoundExt(x, y);
5174
5175   GfxDir[x][y] = MovDir[x][y];
5176
5177   if (direction != MovDir[x][y])
5178     GfxFrame[x][y] = 0;
5179
5180   if (MovDelay[x][y])
5181     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5182 }
5183
5184 static boolean JustBeingPushed(int x, int y)
5185 {
5186   int i;
5187
5188   for (i = 0; i < MAX_PLAYERS; i++)
5189   {
5190     struct PlayerInfo *player = &stored_player[i];
5191
5192     if (player->active && player->is_pushing && player->MovPos)
5193     {
5194       int next_jx = player->jx + (player->jx - player->last_jx);
5195       int next_jy = player->jy + (player->jy - player->last_jy);
5196
5197       if (x == next_jx && y == next_jy)
5198         return TRUE;
5199     }
5200   }
5201
5202   return FALSE;
5203 }
5204
5205 void StartMoving(int x, int y)
5206 {
5207   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5208   int element = Feld[x][y];
5209
5210   if (Stop[x][y])
5211     return;
5212
5213   if (MovDelay[x][y] == 0)
5214     GfxAction[x][y] = ACTION_DEFAULT;
5215
5216   if (CAN_FALL(element) && y < lev_fieldy - 1)
5217   {
5218     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5219         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5220       if (JustBeingPushed(x, y))
5221         return;
5222
5223     if (element == EL_QUICKSAND_FULL)
5224     {
5225       if (IS_FREE(x, y + 1))
5226       {
5227         InitMovingField(x, y, MV_DOWN);
5228         started_moving = TRUE;
5229
5230         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5231         Store[x][y] = EL_ROCK;
5232
5233         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5234       }
5235       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5236       {
5237         if (!MovDelay[x][y])
5238           MovDelay[x][y] = TILEY + 1;
5239
5240         if (MovDelay[x][y])
5241         {
5242           MovDelay[x][y]--;
5243           if (MovDelay[x][y])
5244             return;
5245         }
5246
5247         Feld[x][y] = EL_QUICKSAND_EMPTY;
5248         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5249         Store[x][y + 1] = Store[x][y];
5250         Store[x][y] = 0;
5251
5252         PlayLevelSoundAction(x, y, ACTION_FILLING);
5253       }
5254     }
5255     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5256              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5257     {
5258       InitMovingField(x, y, MV_DOWN);
5259       started_moving = TRUE;
5260
5261       Feld[x][y] = EL_QUICKSAND_FILLING;
5262       Store[x][y] = element;
5263
5264       PlayLevelSoundAction(x, y, ACTION_FILLING);
5265     }
5266     else if (element == EL_MAGIC_WALL_FULL)
5267     {
5268       if (IS_FREE(x, y + 1))
5269       {
5270         InitMovingField(x, y, MV_DOWN);
5271         started_moving = TRUE;
5272
5273         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5274         Store[x][y] = EL_CHANGED(Store[x][y]);
5275       }
5276       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5277       {
5278         if (!MovDelay[x][y])
5279           MovDelay[x][y] = TILEY/4 + 1;
5280
5281         if (MovDelay[x][y])
5282         {
5283           MovDelay[x][y]--;
5284           if (MovDelay[x][y])
5285             return;
5286         }
5287
5288         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5289         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5290         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5291         Store[x][y] = 0;
5292       }
5293     }
5294     else if (element == EL_BD_MAGIC_WALL_FULL)
5295     {
5296       if (IS_FREE(x, y + 1))
5297       {
5298         InitMovingField(x, y, MV_DOWN);
5299         started_moving = TRUE;
5300
5301         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5302         Store[x][y] = EL_CHANGED2(Store[x][y]);
5303       }
5304       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5305       {
5306         if (!MovDelay[x][y])
5307           MovDelay[x][y] = TILEY/4 + 1;
5308
5309         if (MovDelay[x][y])
5310         {
5311           MovDelay[x][y]--;
5312           if (MovDelay[x][y])
5313             return;
5314         }
5315
5316         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5317         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5318         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5319         Store[x][y] = 0;
5320       }
5321     }
5322     else if (CAN_PASS_MAGIC_WALL(element) &&
5323              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5324               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5325     {
5326       InitMovingField(x, y, MV_DOWN);
5327       started_moving = TRUE;
5328
5329       Feld[x][y] =
5330         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5331          EL_BD_MAGIC_WALL_FILLING);
5332       Store[x][y] = element;
5333     }
5334     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5335     {
5336       SplashAcid(x, y + 1);
5337
5338       InitMovingField(x, y, MV_DOWN);
5339       started_moving = TRUE;
5340
5341       Store[x][y] = EL_ACID;
5342     }
5343     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5344               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5345
5346              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5347               CAN_FALL(element) && WasJustFalling[x][y] &&
5348               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5349
5350              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5351               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5352               (Feld[x][y + 1] == EL_BLOCKED)))
5353     {
5354       /* this is needed for a special case not covered by calling "Impact()"
5355          from "ContinueMoving()": if an element moves to a tile directly below
5356          another element which was just falling on that tile (which was empty
5357          in the previous frame), the falling element above would just stop
5358          instead of smashing the element below (in previous version, the above
5359          element was just checked for "moving" instead of "falling", resulting
5360          in incorrect smashes caused by horizontal movement of the above
5361          element; also, the case of the player being the element to smash was
5362          simply not covered here... :-/ ) */
5363
5364       CheckCollision[x][y] = 0;
5365
5366       Impact(x, y);
5367     }
5368     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5369     {
5370       if (MovDir[x][y] == MV_NONE)
5371       {
5372         InitMovingField(x, y, MV_DOWN);
5373         started_moving = TRUE;
5374       }
5375     }
5376     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5377     {
5378       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5379         MovDir[x][y] = MV_DOWN;
5380
5381       InitMovingField(x, y, MV_DOWN);
5382       started_moving = TRUE;
5383     }
5384     else if (element == EL_AMOEBA_DROP)
5385     {
5386       Feld[x][y] = EL_AMOEBA_GROWING;
5387       Store[x][y] = EL_AMOEBA_WET;
5388     }
5389     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5390               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5391              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5392              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5393     {
5394       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
5395                                 (IS_FREE(x - 1, y + 1) ||
5396                                  Feld[x - 1][y + 1] == EL_ACID));
5397       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5398                                 (IS_FREE(x + 1, y + 1) ||
5399                                  Feld[x + 1][y + 1] == EL_ACID));
5400       boolean can_fall_any  = (can_fall_left || can_fall_right);
5401       boolean can_fall_both = (can_fall_left && can_fall_right);
5402       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5403
5404 #if USE_NEW_ALL_SLIPPERY
5405       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5406       {
5407         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5408           can_fall_right = FALSE;
5409         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5410           can_fall_left = FALSE;
5411         else if (slippery_type == SLIPPERY_ONLY_LEFT)
5412           can_fall_right = FALSE;
5413         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5414           can_fall_left = FALSE;
5415
5416         can_fall_any  = (can_fall_left || can_fall_right);
5417         can_fall_both = FALSE;
5418       }
5419 #else
5420       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5421       {
5422         if (slippery_type == SLIPPERY_ONLY_LEFT)
5423           can_fall_right = FALSE;
5424         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5425           can_fall_left = FALSE;
5426         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5427           can_fall_right = FALSE;
5428         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5429           can_fall_left = FALSE;
5430
5431         can_fall_any  = (can_fall_left || can_fall_right);
5432         can_fall_both = (can_fall_left && can_fall_right);
5433       }
5434 #endif
5435
5436 #if USE_NEW_ALL_SLIPPERY
5437 #else
5438 #if USE_NEW_SP_SLIPPERY
5439       /* !!! better use the same properties as for custom elements here !!! */
5440       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5441                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5442       {
5443         can_fall_right = FALSE;         /* slip down on left side */
5444         can_fall_both = FALSE;
5445       }
5446 #endif
5447 #endif
5448
5449 #if USE_NEW_ALL_SLIPPERY
5450       if (can_fall_both)
5451       {
5452         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5453           can_fall_right = FALSE;       /* slip down on left side */
5454         else
5455           can_fall_left = !(can_fall_right = RND(2));
5456
5457         can_fall_both = FALSE;
5458       }
5459 #else
5460       if (can_fall_both)
5461       {
5462         if (game.emulation == EMU_BOULDERDASH ||
5463             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5464           can_fall_right = FALSE;       /* slip down on left side */
5465         else
5466           can_fall_left = !(can_fall_right = RND(2));
5467
5468         can_fall_both = FALSE;
5469       }
5470 #endif
5471
5472       if (can_fall_any)
5473       {
5474         /* if not determined otherwise, prefer left side for slipping down */
5475         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5476         started_moving = TRUE;
5477       }
5478     }
5479 #if 0
5480     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5481 #else
5482     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5483 #endif
5484     {
5485       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
5486       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5487       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5488       int belt_dir = game.belt_dir[belt_nr];
5489
5490       if ((belt_dir == MV_LEFT  && left_is_free) ||
5491           (belt_dir == MV_RIGHT && right_is_free))
5492       {
5493         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5494
5495         InitMovingField(x, y, belt_dir);
5496         started_moving = TRUE;
5497
5498         Pushed[x][y] = TRUE;
5499         Pushed[nextx][y] = TRUE;
5500
5501         GfxAction[x][y] = ACTION_DEFAULT;
5502       }
5503       else
5504       {
5505         MovDir[x][y] = 0;       /* if element was moving, stop it */
5506       }
5507     }
5508   }
5509
5510   /* not "else if" because of elements that can fall and move (EL_SPRING) */
5511 #if 0
5512   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5513 #else
5514   if (CAN_MOVE(element) && !started_moving)
5515 #endif
5516   {
5517     int move_pattern = element_info[element].move_pattern;
5518     int newx, newy;
5519
5520 #if 0
5521 #if DEBUG
5522     if (MovDir[x][y] == MV_NONE)
5523     {
5524       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5525              x, y, element, element_info[element].token_name);
5526       printf("StartMoving(): This should never happen!\n");
5527     }
5528 #endif
5529 #endif
5530
5531     Moving2Blocked(x, y, &newx, &newy);
5532
5533     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5534       return;
5535
5536     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5537         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5538     {
5539       WasJustMoving[x][y] = 0;
5540       CheckCollision[x][y] = 0;
5541
5542       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5543
5544       if (Feld[x][y] != element)        /* element has changed */
5545         return;
5546     }
5547
5548     if (!MovDelay[x][y])        /* start new movement phase */
5549     {
5550       /* all objects that can change their move direction after each step
5551          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5552
5553       if (element != EL_YAMYAM &&
5554           element != EL_DARK_YAMYAM &&
5555           element != EL_PACMAN &&
5556           !(move_pattern & MV_ANY_DIRECTION) &&
5557           move_pattern != MV_TURNING_LEFT &&
5558           move_pattern != MV_TURNING_RIGHT &&
5559           move_pattern != MV_TURNING_LEFT_RIGHT &&
5560           move_pattern != MV_TURNING_RIGHT_LEFT &&
5561           move_pattern != MV_TURNING_RANDOM)
5562       {
5563         TurnRound(x, y);
5564
5565         if (MovDelay[x][y] && (element == EL_BUG ||
5566                                element == EL_SPACESHIP ||
5567                                element == EL_SP_SNIKSNAK ||
5568                                element == EL_SP_ELECTRON ||
5569                                element == EL_MOLE))
5570           DrawLevelField(x, y);
5571       }
5572     }
5573
5574     if (MovDelay[x][y])         /* wait some time before next movement */
5575     {
5576       MovDelay[x][y]--;
5577
5578       if (element == EL_ROBOT ||
5579           element == EL_YAMYAM ||
5580           element == EL_DARK_YAMYAM)
5581       {
5582         DrawLevelElementAnimationIfNeeded(x, y, element);
5583         PlayLevelSoundAction(x, y, ACTION_WAITING);
5584       }
5585       else if (element == EL_SP_ELECTRON)
5586         DrawLevelElementAnimationIfNeeded(x, y, element);
5587       else if (element == EL_DRAGON)
5588       {
5589         int i;
5590         int dir = MovDir[x][y];
5591         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5592         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5593         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
5594                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
5595                        dir == MV_UP     ? IMG_FLAMES_1_UP :
5596                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5597         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5598
5599         GfxAction[x][y] = ACTION_ATTACKING;
5600
5601         if (IS_PLAYER(x, y))
5602           DrawPlayerField(x, y);
5603         else
5604           DrawLevelField(x, y);
5605
5606         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5607
5608         for (i = 1; i <= 3; i++)
5609         {
5610           int xx = x + i * dx;
5611           int yy = y + i * dy;
5612           int sx = SCREENX(xx);
5613           int sy = SCREENY(yy);
5614           int flame_graphic = graphic + (i - 1);
5615
5616           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5617             break;
5618
5619           if (MovDelay[x][y])
5620           {
5621             int flamed = MovingOrBlocked2Element(xx, yy);
5622
5623             /* !!! */
5624 #if 0
5625             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5626               Bang(xx, yy);
5627             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5628               RemoveMovingField(xx, yy);
5629             else
5630               RemoveField(xx, yy);
5631 #else
5632             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5633               Bang(xx, yy);
5634             else
5635               RemoveMovingField(xx, yy);
5636 #endif
5637
5638             ChangeDelay[xx][yy] = 0;
5639
5640             Feld[xx][yy] = EL_FLAMES;
5641
5642             if (IN_SCR_FIELD(sx, sy))
5643             {
5644               DrawLevelFieldCrumbledSand(xx, yy);
5645               DrawGraphic(sx, sy, flame_graphic, frame);
5646             }
5647           }
5648           else
5649           {
5650             if (Feld[xx][yy] == EL_FLAMES)
5651               Feld[xx][yy] = EL_EMPTY;
5652             DrawLevelField(xx, yy);
5653           }
5654         }
5655       }
5656
5657       if (MovDelay[x][y])       /* element still has to wait some time */
5658       {
5659         PlayLevelSoundAction(x, y, ACTION_WAITING);
5660
5661         return;
5662       }
5663     }
5664
5665     /* now make next step */
5666
5667     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5668
5669     if (DONT_COLLIDE_WITH(element) &&
5670         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5671         !PLAYER_ENEMY_PROTECTED(newx, newy))
5672     {
5673       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5674
5675       return;
5676     }
5677
5678     else if (CAN_MOVE_INTO_ACID(element) &&
5679              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5680              (MovDir[x][y] == MV_DOWN ||
5681               game.engine_version >= VERSION_IDENT(3,1,0,0)))
5682     {
5683       SplashAcid(newx, newy);
5684       Store[x][y] = EL_ACID;
5685     }
5686     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5687     {
5688       if (Feld[newx][newy] == EL_EXIT_OPEN)
5689       {
5690         RemoveField(x, y);
5691         DrawLevelField(x, y);
5692
5693         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5694         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5695           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5696
5697         local_player->friends_still_needed--;
5698         if (!local_player->friends_still_needed &&
5699             !local_player->GameOver && AllPlayersGone)
5700           local_player->LevelSolved = local_player->GameOver = TRUE;
5701
5702         return;
5703       }
5704       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5705       {
5706         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5707           DrawLevelField(newx, newy);
5708         else
5709           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5710       }
5711       else if (!IS_FREE(newx, newy))
5712       {
5713         GfxAction[x][y] = ACTION_WAITING;
5714
5715         if (IS_PLAYER(x, y))
5716           DrawPlayerField(x, y);
5717         else
5718           DrawLevelField(x, y);
5719
5720         return;
5721       }
5722     }
5723     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5724     {
5725       if (IS_FOOD_PIG(Feld[newx][newy]))
5726       {
5727         if (IS_MOVING(newx, newy))
5728           RemoveMovingField(newx, newy);
5729         else
5730         {
5731           Feld[newx][newy] = EL_EMPTY;
5732           DrawLevelField(newx, newy);
5733         }
5734
5735         PlayLevelSound(x, y, SND_PIG_DIGGING);
5736       }
5737       else if (!IS_FREE(newx, newy))
5738       {
5739         if (IS_PLAYER(x, y))
5740           DrawPlayerField(x, y);
5741         else
5742           DrawLevelField(x, y);
5743
5744         return;
5745       }
5746     }
5747     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5748     {
5749       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL &&
5750           ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5751       {
5752         int diagonal_move_dir = MovDir[x][y];
5753         int change_delay = 8;
5754         int graphic;
5755
5756         /* android is moving diagonally */
5757
5758         CreateField(x, y, EL_DIAGONAL_SHRINKING);
5759
5760         GfxElement[x][y] = EL_EMC_ANDROID;
5761         GfxAction[x][y] = ACTION_SHRINKING;
5762         GfxDir[x][y] = diagonal_move_dir;
5763         ChangeDelay[x][y] = change_delay;
5764
5765         graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5766                                  GfxDir[x][y]);
5767
5768         DrawLevelGraphicAnimation(x, y, graphic);
5769         PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5770
5771 #if 0
5772         CheckTriggeredElementChangeBySide(x, y, element,
5773                                           CE_MOVE_OF_X, new_move_dir);
5774
5775         TestIfElementTouchesCustomElement(x, y); /* empty or new element */
5776
5777         TestIfElementHitsCustomElement(newx, newy, new_move_dir);
5778         TestIfElementTouchesCustomElement(newx, newy);
5779 #endif
5780
5781         CreateField(newx, newy, EL_DIAGONAL_GROWING);
5782
5783         Store[newx][newy] = EL_EMC_ANDROID;
5784         GfxElement[newx][newy] = EL_EMC_ANDROID;
5785         GfxAction[newx][newy] = ACTION_GROWING;
5786         GfxDir[newx][newy] = diagonal_move_dir;
5787         ChangeDelay[newx][newy] = change_delay;
5788
5789         graphic = el_act_dir2img(GfxElement[newx][newy], GfxAction[newx][newy],
5790                                  GfxDir[newx][newy]);
5791
5792         DrawLevelGraphicAnimation(newx, newy, graphic);
5793         PlayLevelSoundAction(newx, newy, ACTION_GROWING);
5794
5795         return;
5796       }
5797       else if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5798       {
5799         Feld[newx][newy] = EL_EMPTY;
5800         DrawLevelField(newx, newy);
5801
5802         PlayLevelSoundAction(x, y, ACTION_DIGGING);
5803       }
5804       else if (!IS_FREE(newx, newy))
5805       {
5806 #if 0
5807         if (IS_PLAYER(x, y))
5808           DrawPlayerField(x, y);
5809         else
5810           DrawLevelField(x, y);
5811 #endif
5812
5813         return;
5814       }
5815     }
5816     else if (IS_CUSTOM_ELEMENT(element) &&
5817              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5818     {
5819       int new_element = Feld[newx][newy];
5820
5821       if (!IS_FREE(newx, newy))
5822       {
5823         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5824                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5825                       ACTION_BREAKING);
5826
5827         /* no element can dig solid indestructible elements */
5828         if (IS_INDESTRUCTIBLE(new_element) &&
5829             !IS_DIGGABLE(new_element) &&
5830             !IS_COLLECTIBLE(new_element))
5831           return;
5832
5833         if (AmoebaNr[newx][newy] &&
5834             (new_element == EL_AMOEBA_FULL ||
5835              new_element == EL_BD_AMOEBA ||
5836              new_element == EL_AMOEBA_GROWING))
5837         {
5838           AmoebaCnt[AmoebaNr[newx][newy]]--;
5839           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5840         }
5841
5842         if (IS_MOVING(newx, newy))
5843           RemoveMovingField(newx, newy);
5844         else
5845         {
5846           RemoveField(newx, newy);
5847           DrawLevelField(newx, newy);
5848         }
5849
5850         /* if digged element was about to explode, prevent the explosion */
5851         ExplodeField[newx][newy] = EX_TYPE_NONE;
5852
5853         PlayLevelSoundAction(x, y, action);
5854       }
5855
5856       Store[newx][newy] = EL_EMPTY;
5857 #if 1
5858       /* this makes it possible to leave the removed element again */
5859       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5860         Store[newx][newy] = new_element;
5861 #else
5862       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5863       {
5864         int move_leave_element = element_info[element].move_leave_element;
5865
5866         /* this makes it possible to leave the removed element again */
5867         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5868                              new_element : move_leave_element);
5869       }
5870 #endif
5871
5872       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5873       {
5874         RunnerVisit[x][y] = FrameCounter;
5875         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5876       }
5877     }
5878     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5879     {
5880       if (!IS_FREE(newx, newy))
5881       {
5882         if (IS_PLAYER(x, y))
5883           DrawPlayerField(x, y);
5884         else
5885           DrawLevelField(x, y);
5886
5887         return;
5888       }
5889       else
5890       {
5891         boolean wanna_flame = !RND(10);
5892         int dx = newx - x, dy = newy - y;
5893         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5894         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5895         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5896                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5897         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5898                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5899
5900         if ((wanna_flame ||
5901              IS_CLASSIC_ENEMY(element1) ||
5902              IS_CLASSIC_ENEMY(element2)) &&
5903             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5904             element1 != EL_FLAMES && element2 != EL_FLAMES)
5905         {
5906           ResetGfxAnimation(x, y);
5907           GfxAction[x][y] = ACTION_ATTACKING;
5908
5909           if (IS_PLAYER(x, y))
5910             DrawPlayerField(x, y);
5911           else
5912             DrawLevelField(x, y);
5913
5914           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5915
5916           MovDelay[x][y] = 50;
5917
5918           /* !!! */
5919 #if 0
5920           RemoveField(newx, newy);
5921 #endif
5922           Feld[newx][newy] = EL_FLAMES;
5923           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5924           {
5925 #if 0
5926             RemoveField(newx1, newy1);
5927 #endif
5928             Feld[newx1][newy1] = EL_FLAMES;
5929           }
5930           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5931           {
5932 #if 0
5933             RemoveField(newx2, newy2);
5934 #endif
5935             Feld[newx2][newy2] = EL_FLAMES;
5936           }
5937
5938           return;
5939         }
5940       }
5941     }
5942     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5943              Feld[newx][newy] == EL_DIAMOND)
5944     {
5945       if (IS_MOVING(newx, newy))
5946         RemoveMovingField(newx, newy);
5947       else
5948       {
5949         Feld[newx][newy] = EL_EMPTY;
5950         DrawLevelField(newx, newy);
5951       }
5952
5953       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5954     }
5955     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5956              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5957     {
5958       if (AmoebaNr[newx][newy])
5959       {
5960         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5961         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5962             Feld[newx][newy] == EL_BD_AMOEBA)
5963           AmoebaCnt[AmoebaNr[newx][newy]]--;
5964       }
5965
5966 #if 0
5967       /* !!! test !!! */
5968       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5969       {
5970         RemoveMovingField(newx, newy);
5971       }
5972 #else
5973       if (IS_MOVING(newx, newy))
5974       {
5975         RemoveMovingField(newx, newy);
5976       }
5977 #endif
5978       else
5979       {
5980         Feld[newx][newy] = EL_EMPTY;
5981         DrawLevelField(newx, newy);
5982       }
5983
5984       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5985     }
5986     else if ((element == EL_PACMAN || element == EL_MOLE)
5987              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5988     {
5989       if (AmoebaNr[newx][newy])
5990       {
5991         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5992         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5993             Feld[newx][newy] == EL_BD_AMOEBA)
5994           AmoebaCnt[AmoebaNr[newx][newy]]--;
5995       }
5996
5997       if (element == EL_MOLE)
5998       {
5999         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6000         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6001
6002         ResetGfxAnimation(x, y);
6003         GfxAction[x][y] = ACTION_DIGGING;
6004         DrawLevelField(x, y);
6005
6006         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6007
6008         return;                         /* wait for shrinking amoeba */
6009       }
6010       else      /* element == EL_PACMAN */
6011       {
6012         Feld[newx][newy] = EL_EMPTY;
6013         DrawLevelField(newx, newy);
6014         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6015       }
6016     }
6017     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6018              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6019               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6020     {
6021       /* wait for shrinking amoeba to completely disappear */
6022       return;
6023     }
6024     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6025     {
6026       /* object was running against a wall */
6027
6028       TurnRound(x, y);
6029
6030 #if 0
6031       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6032       if (move_pattern & MV_ANY_DIRECTION &&
6033           move_pattern == MovDir[x][y])
6034       {
6035         int blocking_element =
6036           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6037
6038         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6039                                  MovDir[x][y]);
6040
6041         element = Feld[x][y];   /* element might have changed */
6042       }
6043 #endif
6044
6045       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6046         DrawLevelElementAnimation(x, y, element);
6047
6048       if (DONT_TOUCH(element))
6049         TestIfBadThingTouchesPlayer(x, y);
6050
6051       return;
6052     }
6053
6054     InitMovingField(x, y, MovDir[x][y]);
6055
6056     PlayLevelSoundAction(x, y, ACTION_MOVING);
6057   }
6058
6059   if (MovDir[x][y])
6060     ContinueMoving(x, y);
6061 }
6062
6063 void ContinueMoving(int x, int y)
6064 {
6065   int element = Feld[x][y];
6066   struct ElementInfo *ei = &element_info[element];
6067   int direction = MovDir[x][y];
6068   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6069   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6070   int newx = x + dx, newy = y + dy;
6071   int stored = Store[x][y];
6072   int stored_new = Store[newx][newy];
6073   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6074   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6075   boolean last_line = (newy == lev_fieldy - 1);
6076
6077   MovPos[x][y] += getElementMoveStepsize(x, y);
6078
6079   if (pushed_by_player) /* special case: moving object pushed by player */
6080     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6081
6082   if (ABS(MovPos[x][y]) < TILEX)
6083   {
6084     DrawLevelField(x, y);
6085
6086     return;     /* element is still moving */
6087   }
6088
6089   /* element reached destination field */
6090
6091   Feld[x][y] = EL_EMPTY;
6092   Feld[newx][newy] = element;
6093   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6094
6095   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6096   {
6097     element = Feld[newx][newy] = EL_ACID;
6098   }
6099   else if (element == EL_MOLE)
6100   {
6101     Feld[x][y] = EL_SAND;
6102
6103     DrawLevelFieldCrumbledSandNeighbours(x, y);
6104   }
6105   else if (element == EL_QUICKSAND_FILLING)
6106   {
6107     element = Feld[newx][newy] = get_next_element(element);
6108     Store[newx][newy] = Store[x][y];
6109   }
6110   else if (element == EL_QUICKSAND_EMPTYING)
6111   {
6112     Feld[x][y] = get_next_element(element);
6113     element = Feld[newx][newy] = Store[x][y];
6114   }
6115   else if (element == EL_MAGIC_WALL_FILLING)
6116   {
6117     element = Feld[newx][newy] = get_next_element(element);
6118     if (!game.magic_wall_active)
6119       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6120     Store[newx][newy] = Store[x][y];
6121   }
6122   else if (element == EL_MAGIC_WALL_EMPTYING)
6123   {
6124     Feld[x][y] = get_next_element(element);
6125     if (!game.magic_wall_active)
6126       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6127     element = Feld[newx][newy] = Store[x][y];
6128
6129 #if USE_NEW_CUSTOM_VALUE
6130     InitField(newx, newy, FALSE);
6131 #endif
6132   }
6133   else if (element == EL_BD_MAGIC_WALL_FILLING)
6134   {
6135     element = Feld[newx][newy] = get_next_element(element);
6136     if (!game.magic_wall_active)
6137       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6138     Store[newx][newy] = Store[x][y];
6139   }
6140   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6141   {
6142     Feld[x][y] = get_next_element(element);
6143     if (!game.magic_wall_active)
6144       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6145     element = Feld[newx][newy] = Store[x][y];
6146
6147 #if USE_NEW_CUSTOM_VALUE
6148     InitField(newx, newy, FALSE);
6149 #endif
6150   }
6151   else if (element == EL_AMOEBA_DROPPING)
6152   {
6153     Feld[x][y] = get_next_element(element);
6154     element = Feld[newx][newy] = Store[x][y];
6155   }
6156   else if (element == EL_SOKOBAN_OBJECT)
6157   {
6158     if (Back[x][y])
6159       Feld[x][y] = Back[x][y];
6160
6161     if (Back[newx][newy])
6162       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6163
6164     Back[x][y] = Back[newx][newy] = 0;
6165   }
6166
6167   Store[x][y] = EL_EMPTY;
6168   MovPos[x][y] = 0;
6169   MovDir[x][y] = 0;
6170   MovDelay[x][y] = 0;
6171
6172   MovDelay[newx][newy] = 0;
6173
6174 #if 1
6175   if (CAN_CHANGE_OR_HAS_ACTION(element))
6176 #else
6177   if (CAN_CHANGE(element))
6178 #endif
6179   {
6180     /* copy element change control values to new field */
6181     ChangeDelay[newx][newy] = ChangeDelay[x][y];
6182     ChangePage[newx][newy]  = ChangePage[x][y];
6183     ChangeCount[newx][newy] = ChangeCount[x][y];
6184     ChangeEvent[newx][newy] = ChangeEvent[x][y];
6185
6186 #if 0
6187 #if USE_NEW_CUSTOM_VALUE
6188     CustomValue[newx][newy] = CustomValue[x][y];
6189 #endif
6190 #endif
6191   }
6192
6193 #if 1
6194 #if USE_NEW_CUSTOM_VALUE
6195     CustomValue[newx][newy] = CustomValue[x][y];
6196 #endif
6197 #endif
6198
6199   ChangeDelay[x][y] = 0;
6200   ChangePage[x][y] = -1;
6201   ChangeCount[x][y] = 0;
6202   ChangeEvent[x][y] = -1;
6203
6204 #if USE_NEW_CUSTOM_VALUE
6205   CustomValue[x][y] = 0;
6206 #endif
6207
6208   /* copy animation control values to new field */
6209   GfxFrame[newx][newy]  = GfxFrame[x][y];
6210   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
6211   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
6212   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
6213
6214   Pushed[x][y] = Pushed[newx][newy] = FALSE;
6215
6216   /* some elements can leave other elements behind after moving */
6217   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6218       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6219       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6220   {
6221     int move_leave_element = ei->move_leave_element;
6222
6223 #if 1
6224     /* this makes it possible to leave the removed element again */
6225     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6226       move_leave_element = stored;
6227 #else
6228     /* this makes it possible to leave the removed element again */
6229     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6230         ei->move_leave_element == EL_TRIGGER_ELEMENT)
6231       move_leave_element = stored;
6232 #endif
6233
6234     Feld[x][y] = move_leave_element;
6235
6236     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6237       MovDir[x][y] = direction;
6238
6239     InitField(x, y, FALSE);
6240
6241     if (GFX_CRUMBLED(Feld[x][y]))
6242       DrawLevelFieldCrumbledSandNeighbours(x, y);
6243
6244     if (ELEM_IS_PLAYER(move_leave_element))
6245       RelocatePlayer(x, y, move_leave_element);
6246   }
6247
6248   /* do this after checking for left-behind element */
6249   ResetGfxAnimation(x, y);      /* reset animation values for old field */
6250
6251   if (!CAN_MOVE(element) ||
6252       (CAN_FALL(element) && direction == MV_DOWN &&
6253        (element == EL_SPRING ||
6254         element_info[element].move_pattern == MV_WHEN_PUSHED ||
6255         element_info[element].move_pattern == MV_WHEN_DROPPED)))
6256     GfxDir[x][y] = MovDir[newx][newy] = 0;
6257
6258   DrawLevelField(x, y);
6259   DrawLevelField(newx, newy);
6260
6261   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
6262
6263   /* prevent pushed element from moving on in pushed direction */
6264   if (pushed_by_player && CAN_MOVE(element) &&
6265       element_info[element].move_pattern & MV_ANY_DIRECTION &&
6266       !(element_info[element].move_pattern & direction))
6267     TurnRound(newx, newy);
6268
6269   /* prevent elements on conveyor belt from moving on in last direction */
6270   if (pushed_by_conveyor && CAN_FALL(element) &&
6271       direction & MV_HORIZONTAL)
6272     MovDir[newx][newy] = 0;
6273
6274   if (!pushed_by_player)
6275   {
6276     int nextx = newx + dx, nexty = newy + dy;
6277     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6278
6279     WasJustMoving[newx][newy] = 3;
6280
6281     if (CAN_FALL(element) && direction == MV_DOWN)
6282       WasJustFalling[newx][newy] = 3;
6283
6284     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6285       CheckCollision[newx][newy] = 2;
6286   }
6287
6288   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
6289   {
6290     TestIfBadThingTouchesPlayer(newx, newy);
6291     TestIfBadThingTouchesFriend(newx, newy);
6292
6293     if (!IS_CUSTOM_ELEMENT(element))
6294       TestIfBadThingTouchesOtherBadThing(newx, newy);
6295   }
6296   else if (element == EL_PENGUIN)
6297     TestIfFriendTouchesBadThing(newx, newy);
6298
6299   /* give the player one last chance (one more frame) to move away */
6300   if (CAN_FALL(element) && direction == MV_DOWN &&
6301       (last_line || (!IS_FREE(x, newy + 1) &&
6302                      (!IS_PLAYER(x, newy + 1) ||
6303                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
6304     Impact(x, newy);
6305
6306   if (pushed_by_player && !game.use_change_when_pushing_bug)
6307   {
6308     int push_side = MV_DIR_OPPOSITE(direction);
6309     struct PlayerInfo *player = PLAYERINFO(x, y);
6310
6311     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6312                                player->index_bit, push_side);
6313     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6314                                         player->index_bit, push_side);
6315   }
6316
6317   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
6318     MovDelay[newx][newy] = 1;
6319
6320   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6321
6322   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
6323
6324   TestIfElementHitsCustomElement(newx, newy, direction);
6325   TestIfPlayerTouchesCustomElement(newx, newy);
6326   TestIfElementTouchesCustomElement(newx, newy);
6327
6328 #if 1
6329   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6330       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6331     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6332                                       MV_DIR_OPPOSITE(direction));
6333 #endif
6334 }
6335
6336 int AmoebeNachbarNr(int ax, int ay)
6337 {
6338   int i;
6339   int element = Feld[ax][ay];
6340   int group_nr = 0;
6341   static int xy[4][2] =
6342   {
6343     { 0, -1 },
6344     { -1, 0 },
6345     { +1, 0 },
6346     { 0, +1 }
6347   };
6348
6349   for (i = 0; i < NUM_DIRECTIONS; i++)
6350   {
6351     int x = ax + xy[i][0];
6352     int y = ay + xy[i][1];
6353
6354     if (!IN_LEV_FIELD(x, y))
6355       continue;
6356
6357     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6358       group_nr = AmoebaNr[x][y];
6359   }
6360
6361   return group_nr;
6362 }
6363
6364 void AmoebenVereinigen(int ax, int ay)
6365 {
6366   int i, x, y, xx, yy;
6367   int new_group_nr = AmoebaNr[ax][ay];
6368   static int xy[4][2] =
6369   {
6370     { 0, -1 },
6371     { -1, 0 },
6372     { +1, 0 },
6373     { 0, +1 }
6374   };
6375
6376   if (new_group_nr == 0)
6377     return;
6378
6379   for (i = 0; i < NUM_DIRECTIONS; i++)
6380   {
6381     x = ax + xy[i][0];
6382     y = ay + xy[i][1];
6383
6384     if (!IN_LEV_FIELD(x, y))
6385       continue;
6386
6387     if ((Feld[x][y] == EL_AMOEBA_FULL ||
6388          Feld[x][y] == EL_BD_AMOEBA ||
6389          Feld[x][y] == EL_AMOEBA_DEAD) &&
6390         AmoebaNr[x][y] != new_group_nr)
6391     {
6392       int old_group_nr = AmoebaNr[x][y];
6393
6394       if (old_group_nr == 0)
6395         return;
6396
6397       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6398       AmoebaCnt[old_group_nr] = 0;
6399       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6400       AmoebaCnt2[old_group_nr] = 0;
6401
6402 #if 1
6403       SCAN_PLAYFIELD(xx, yy)
6404 #else
6405       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6406 #endif
6407       {
6408         if (AmoebaNr[xx][yy] == old_group_nr)
6409           AmoebaNr[xx][yy] = new_group_nr;
6410       }
6411     }
6412   }
6413 }
6414
6415 void AmoebeUmwandeln(int ax, int ay)
6416 {
6417   int i, x, y;
6418
6419   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6420   {
6421     int group_nr = AmoebaNr[ax][ay];
6422
6423 #ifdef DEBUG
6424     if (group_nr == 0)
6425     {
6426       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6427       printf("AmoebeUmwandeln(): This should never happen!\n");
6428       return;
6429     }
6430 #endif
6431
6432 #if 1
6433     SCAN_PLAYFIELD(x, y)
6434 #else
6435     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6436 #endif
6437     {
6438       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6439       {
6440         AmoebaNr[x][y] = 0;
6441         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6442       }
6443     }
6444
6445     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6446                             SND_AMOEBA_TURNING_TO_GEM :
6447                             SND_AMOEBA_TURNING_TO_ROCK));
6448     Bang(ax, ay);
6449   }
6450   else
6451   {
6452     static int xy[4][2] =
6453     {
6454       { 0, -1 },
6455       { -1, 0 },
6456       { +1, 0 },
6457       { 0, +1 }
6458     };
6459
6460     for (i = 0; i < NUM_DIRECTIONS; i++)
6461     {
6462       x = ax + xy[i][0];
6463       y = ay + xy[i][1];
6464
6465       if (!IN_LEV_FIELD(x, y))
6466         continue;
6467
6468       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6469       {
6470         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6471                               SND_AMOEBA_TURNING_TO_GEM :
6472                               SND_AMOEBA_TURNING_TO_ROCK));
6473         Bang(x, y);
6474       }
6475     }
6476   }
6477 }
6478
6479 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6480 {
6481   int x, y;
6482   int group_nr = AmoebaNr[ax][ay];
6483   boolean done = FALSE;
6484
6485 #ifdef DEBUG
6486   if (group_nr == 0)
6487   {
6488     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6489     printf("AmoebeUmwandelnBD(): This should never happen!\n");
6490     return;
6491   }
6492 #endif
6493
6494 #if 1
6495   SCAN_PLAYFIELD(x, y)
6496 #else
6497   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6498 #endif
6499   {
6500     if (AmoebaNr[x][y] == group_nr &&
6501         (Feld[x][y] == EL_AMOEBA_DEAD ||
6502          Feld[x][y] == EL_BD_AMOEBA ||
6503          Feld[x][y] == EL_AMOEBA_GROWING))
6504     {
6505       AmoebaNr[x][y] = 0;
6506       Feld[x][y] = new_element;
6507       InitField(x, y, FALSE);
6508       DrawLevelField(x, y);
6509       done = TRUE;
6510     }
6511   }
6512
6513   if (done)
6514     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6515                             SND_BD_AMOEBA_TURNING_TO_ROCK :
6516                             SND_BD_AMOEBA_TURNING_TO_GEM));
6517 }
6518
6519 void AmoebeWaechst(int x, int y)
6520 {
6521   static unsigned long sound_delay = 0;
6522   static unsigned long sound_delay_value = 0;
6523
6524   if (!MovDelay[x][y])          /* start new growing cycle */
6525   {
6526     MovDelay[x][y] = 7;
6527
6528     if (DelayReached(&sound_delay, sound_delay_value))
6529     {
6530       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6531       sound_delay_value = 30;
6532     }
6533   }
6534
6535   if (MovDelay[x][y])           /* wait some time before growing bigger */
6536   {
6537     MovDelay[x][y]--;
6538     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6539     {
6540       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6541                                            6 - MovDelay[x][y]);
6542
6543       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6544     }
6545
6546     if (!MovDelay[x][y])
6547     {
6548       Feld[x][y] = Store[x][y];
6549       Store[x][y] = 0;
6550       DrawLevelField(x, y);
6551     }
6552   }
6553 }
6554
6555 void AmoebaDisappearing(int x, int y)
6556 {
6557   static unsigned long sound_delay = 0;
6558   static unsigned long sound_delay_value = 0;
6559
6560   if (!MovDelay[x][y])          /* start new shrinking cycle */
6561   {
6562     MovDelay[x][y] = 7;
6563
6564     if (DelayReached(&sound_delay, sound_delay_value))
6565       sound_delay_value = 30;
6566   }
6567
6568   if (MovDelay[x][y])           /* wait some time before shrinking */
6569   {
6570     MovDelay[x][y]--;
6571     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6572     {
6573       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6574                                            6 - MovDelay[x][y]);
6575
6576       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6577     }
6578
6579     if (!MovDelay[x][y])
6580     {
6581       Feld[x][y] = EL_EMPTY;
6582       DrawLevelField(x, y);
6583
6584       /* don't let mole enter this field in this cycle;
6585          (give priority to objects falling to this field from above) */
6586       Stop[x][y] = TRUE;
6587     }
6588   }
6589 }
6590
6591 void AmoebeAbleger(int ax, int ay)
6592 {
6593   int i;
6594   int element = Feld[ax][ay];
6595   int graphic = el2img(element);
6596   int newax = ax, neway = ay;
6597   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6598   static int xy[4][2] =
6599   {
6600     { 0, -1 },
6601     { -1, 0 },
6602     { +1, 0 },
6603     { 0, +1 }
6604   };
6605
6606   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6607   {
6608     Feld[ax][ay] = EL_AMOEBA_DEAD;
6609     DrawLevelField(ax, ay);
6610     return;
6611   }
6612
6613   if (IS_ANIMATED(graphic))
6614     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6615
6616   if (!MovDelay[ax][ay])        /* start making new amoeba field */
6617     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6618
6619   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
6620   {
6621     MovDelay[ax][ay]--;
6622     if (MovDelay[ax][ay])
6623       return;
6624   }
6625
6626   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6627   {
6628     int start = RND(4);
6629     int x = ax + xy[start][0];
6630     int y = ay + xy[start][1];
6631
6632     if (!IN_LEV_FIELD(x, y))
6633       return;
6634
6635     if (IS_FREE(x, y) ||
6636         CAN_GROW_INTO(Feld[x][y]) ||
6637         Feld[x][y] == EL_QUICKSAND_EMPTY)
6638     {
6639       newax = x;
6640       neway = y;
6641     }
6642
6643     if (newax == ax && neway == ay)
6644       return;
6645   }
6646   else                          /* normal or "filled" (BD style) amoeba */
6647   {
6648     int start = RND(4);
6649     boolean waiting_for_player = FALSE;
6650
6651     for (i = 0; i < NUM_DIRECTIONS; i++)
6652     {
6653       int j = (start + i) % 4;
6654       int x = ax + xy[j][0];
6655       int y = ay + xy[j][1];
6656
6657       if (!IN_LEV_FIELD(x, y))
6658         continue;
6659
6660       if (IS_FREE(x, y) ||
6661           CAN_GROW_INTO(Feld[x][y]) ||
6662           Feld[x][y] == EL_QUICKSAND_EMPTY)
6663       {
6664         newax = x;
6665         neway = y;
6666         break;
6667       }
6668       else if (IS_PLAYER(x, y))
6669         waiting_for_player = TRUE;
6670     }
6671
6672     if (newax == ax && neway == ay)             /* amoeba cannot grow */
6673     {
6674       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6675       {
6676         Feld[ax][ay] = EL_AMOEBA_DEAD;
6677         DrawLevelField(ax, ay);
6678         AmoebaCnt[AmoebaNr[ax][ay]]--;
6679
6680         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
6681         {
6682           if (element == EL_AMOEBA_FULL)
6683             AmoebeUmwandeln(ax, ay);
6684           else if (element == EL_BD_AMOEBA)
6685             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6686         }
6687       }
6688       return;
6689     }
6690     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6691     {
6692       /* amoeba gets larger by growing in some direction */
6693
6694       int new_group_nr = AmoebaNr[ax][ay];
6695
6696 #ifdef DEBUG
6697   if (new_group_nr == 0)
6698   {
6699     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6700     printf("AmoebeAbleger(): This should never happen!\n");
6701     return;
6702   }
6703 #endif
6704
6705       AmoebaNr[newax][neway] = new_group_nr;
6706       AmoebaCnt[new_group_nr]++;
6707       AmoebaCnt2[new_group_nr]++;
6708
6709       /* if amoeba touches other amoeba(s) after growing, unify them */
6710       AmoebenVereinigen(newax, neway);
6711
6712       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6713       {
6714         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6715         return;
6716       }
6717     }
6718   }
6719
6720   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6721       (neway == lev_fieldy - 1 && newax != ax))
6722   {
6723     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6724     Store[newax][neway] = element;
6725   }
6726   else if (neway == ay || element == EL_EMC_DRIPPER)
6727   {
6728     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6729
6730     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6731   }
6732   else
6733   {
6734     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6735     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6736     Store[ax][ay] = EL_AMOEBA_DROP;
6737     ContinueMoving(ax, ay);
6738     return;
6739   }
6740
6741   DrawLevelField(newax, neway);
6742 }
6743
6744 void Life(int ax, int ay)
6745 {
6746   int x1, y1, x2, y2;
6747 #if 0
6748   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6749 #endif
6750   int life_time = 40;
6751   int element = Feld[ax][ay];
6752   int graphic = el2img(element);
6753   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6754                          level.biomaze);
6755   boolean changed = FALSE;
6756
6757   if (IS_ANIMATED(graphic))
6758     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6759
6760   if (Stop[ax][ay])
6761     return;
6762
6763   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6764     MovDelay[ax][ay] = life_time;
6765
6766   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6767   {
6768     MovDelay[ax][ay]--;
6769     if (MovDelay[ax][ay])
6770       return;
6771   }
6772
6773   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6774   {
6775     int xx = ax+x1, yy = ay+y1;
6776     int nachbarn = 0;
6777
6778     if (!IN_LEV_FIELD(xx, yy))
6779       continue;
6780
6781     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6782     {
6783       int x = xx+x2, y = yy+y2;
6784
6785       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6786         continue;
6787
6788       if (((Feld[x][y] == element ||
6789             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6790            !Stop[x][y]) ||
6791           (IS_FREE(x, y) && Stop[x][y]))
6792         nachbarn++;
6793     }
6794
6795     if (xx == ax && yy == ay)           /* field in the middle */
6796     {
6797       if (nachbarn < life_parameter[0] ||
6798           nachbarn > life_parameter[1])
6799       {
6800         Feld[xx][yy] = EL_EMPTY;
6801         if (!Stop[xx][yy])
6802           DrawLevelField(xx, yy);
6803         Stop[xx][yy] = TRUE;
6804         changed = TRUE;
6805       }
6806     }
6807     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6808     {                                   /* free border field */
6809       if (nachbarn >= life_parameter[2] &&
6810           nachbarn <= life_parameter[3])
6811       {
6812         Feld[xx][yy] = element;
6813         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6814         if (!Stop[xx][yy])
6815           DrawLevelField(xx, yy);
6816         Stop[xx][yy] = TRUE;
6817         changed = TRUE;
6818       }
6819     }
6820   }
6821
6822   if (changed)
6823     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6824                    SND_GAME_OF_LIFE_GROWING);
6825 }
6826
6827 static void InitRobotWheel(int x, int y)
6828 {
6829   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6830 }
6831
6832 static void RunRobotWheel(int x, int y)
6833 {
6834   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6835 }
6836
6837 static void StopRobotWheel(int x, int y)
6838 {
6839   if (ZX == x && ZY == y)
6840     ZX = ZY = -1;
6841 }
6842
6843 static void InitTimegateWheel(int x, int y)
6844 {
6845   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6846 }
6847
6848 static void RunTimegateWheel(int x, int y)
6849 {
6850   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6851 }
6852
6853 static void InitMagicBallDelay(int x, int y)
6854 {
6855 #if 1
6856   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
6857 #else
6858   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
6859 #endif
6860 }
6861
6862 static void ActivateMagicBall(int bx, int by)
6863 {
6864   int x, y;
6865
6866   if (level.ball_random)
6867   {
6868     int pos_border = RND(8);    /* select one of the eight border elements */
6869     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
6870     int xx = pos_content % 3;
6871     int yy = pos_content / 3;
6872
6873     x = bx - 1 + xx;
6874     y = by - 1 + yy;
6875
6876     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6877       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6878   }
6879   else
6880   {
6881     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
6882     {
6883       int xx = x - bx + 1;
6884       int yy = y - by + 1;
6885
6886       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
6887         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
6888     }
6889   }
6890
6891   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
6892 }
6893
6894 static void InitDiagonalMovingElement(int x, int y)
6895 {
6896 #if 0
6897   MovDelay[x][y] = level.android_move_time;
6898 #endif
6899 }
6900
6901 void CheckExit(int x, int y)
6902 {
6903   if (local_player->gems_still_needed > 0 ||
6904       local_player->sokobanfields_still_needed > 0 ||
6905       local_player->lights_still_needed > 0)
6906   {
6907     int element = Feld[x][y];
6908     int graphic = el2img(element);
6909
6910     if (IS_ANIMATED(graphic))
6911       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6912
6913     return;
6914   }
6915
6916   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6917     return;
6918
6919   Feld[x][y] = EL_EXIT_OPENING;
6920
6921   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6922 }
6923
6924 void CheckExitSP(int x, int y)
6925 {
6926   if (local_player->gems_still_needed > 0)
6927   {
6928     int element = Feld[x][y];
6929     int graphic = el2img(element);
6930
6931     if (IS_ANIMATED(graphic))
6932       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6933
6934     return;
6935   }
6936
6937   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6938     return;
6939
6940   Feld[x][y] = EL_SP_EXIT_OPENING;
6941
6942   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6943 }
6944
6945 static void CloseAllOpenTimegates()
6946 {
6947   int x, y;
6948
6949 #if 1
6950   SCAN_PLAYFIELD(x, y)
6951 #else
6952   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6953 #endif
6954   {
6955     int element = Feld[x][y];
6956
6957     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6958     {
6959       Feld[x][y] = EL_TIMEGATE_CLOSING;
6960
6961       PlayLevelSoundAction(x, y, ACTION_CLOSING);
6962     }
6963   }
6964 }
6965
6966 void EdelsteinFunkeln(int x, int y)
6967 {
6968   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6969     return;
6970
6971   if (Feld[x][y] == EL_BD_DIAMOND)
6972     return;
6973
6974   if (MovDelay[x][y] == 0)      /* next animation frame */
6975     MovDelay[x][y] = 11 * !SimpleRND(500);
6976
6977   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6978   {
6979     MovDelay[x][y]--;
6980
6981     if (setup.direct_draw && MovDelay[x][y])
6982       SetDrawtoField(DRAW_BUFFERED);
6983
6984     DrawLevelElementAnimation(x, y, Feld[x][y]);
6985
6986     if (MovDelay[x][y] != 0)
6987     {
6988       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6989                                            10 - MovDelay[x][y]);
6990
6991       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6992
6993       if (setup.direct_draw)
6994       {
6995         int dest_x, dest_y;
6996
6997         dest_x = FX + SCREENX(x) * TILEX;
6998         dest_y = FY + SCREENY(y) * TILEY;
6999
7000         BlitBitmap(drawto_field, window,
7001                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7002         SetDrawtoField(DRAW_DIRECT);
7003       }
7004     }
7005   }
7006 }
7007
7008 void MauerWaechst(int x, int y)
7009 {
7010   int delay = 6;
7011
7012   if (!MovDelay[x][y])          /* next animation frame */
7013     MovDelay[x][y] = 3 * delay;
7014
7015   if (MovDelay[x][y])           /* wait some time before next frame */
7016   {
7017     MovDelay[x][y]--;
7018
7019     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7020     {
7021       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7022       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7023
7024       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7025     }
7026
7027     if (!MovDelay[x][y])
7028     {
7029       if (MovDir[x][y] == MV_LEFT)
7030       {
7031         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7032           DrawLevelField(x - 1, y);
7033       }
7034       else if (MovDir[x][y] == MV_RIGHT)
7035       {
7036         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7037           DrawLevelField(x + 1, y);
7038       }
7039       else if (MovDir[x][y] == MV_UP)
7040       {
7041         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7042           DrawLevelField(x, y - 1);
7043       }
7044       else
7045       {
7046         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7047           DrawLevelField(x, y + 1);
7048       }
7049
7050       Feld[x][y] = Store[x][y];
7051       Store[x][y] = 0;
7052       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7053       DrawLevelField(x, y);
7054     }
7055   }
7056 }
7057
7058 void MauerAbleger(int ax, int ay)
7059 {
7060   int element = Feld[ax][ay];
7061   int graphic = el2img(element);
7062   boolean oben_frei = FALSE, unten_frei = FALSE;
7063   boolean links_frei = FALSE, rechts_frei = FALSE;
7064   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7065   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7066   boolean new_wall = FALSE;
7067
7068   if (IS_ANIMATED(graphic))
7069     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7070
7071   if (!MovDelay[ax][ay])        /* start building new wall */
7072     MovDelay[ax][ay] = 6;
7073
7074   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7075   {
7076     MovDelay[ax][ay]--;
7077     if (MovDelay[ax][ay])
7078       return;
7079   }
7080
7081   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7082     oben_frei = TRUE;
7083   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7084     unten_frei = TRUE;
7085   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7086     links_frei = TRUE;
7087   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7088     rechts_frei = TRUE;
7089
7090   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7091       element == EL_EXPANDABLE_WALL_ANY)
7092   {
7093     if (oben_frei)
7094     {
7095       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7096       Store[ax][ay-1] = element;
7097       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7098       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7099         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7100                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7101       new_wall = TRUE;
7102     }
7103     if (unten_frei)
7104     {
7105       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7106       Store[ax][ay+1] = element;
7107       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7108       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7109         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7110                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7111       new_wall = TRUE;
7112     }
7113   }
7114
7115   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7116       element == EL_EXPANDABLE_WALL_ANY ||
7117       element == EL_EXPANDABLE_WALL)
7118   {
7119     if (links_frei)
7120     {
7121       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7122       Store[ax-1][ay] = element;
7123       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7124       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7125         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7126                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7127       new_wall = TRUE;
7128     }
7129
7130     if (rechts_frei)
7131     {
7132       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7133       Store[ax+1][ay] = element;
7134       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7135       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7136         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7137                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7138       new_wall = TRUE;
7139     }
7140   }
7141
7142   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7143     DrawLevelField(ax, ay);
7144
7145   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7146     oben_massiv = TRUE;
7147   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7148     unten_massiv = TRUE;
7149   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7150     links_massiv = TRUE;
7151   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7152     rechts_massiv = TRUE;
7153
7154   if (((oben_massiv && unten_massiv) ||
7155        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7156        element == EL_EXPANDABLE_WALL) &&
7157       ((links_massiv && rechts_massiv) ||
7158        element == EL_EXPANDABLE_WALL_VERTICAL))
7159     Feld[ax][ay] = EL_WALL;
7160
7161   if (new_wall)
7162     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7163 }
7164
7165 void CheckForDragon(int x, int y)
7166 {
7167   int i, j;
7168   boolean dragon_found = FALSE;
7169   static int xy[4][2] =
7170   {
7171     { 0, -1 },
7172     { -1, 0 },
7173     { +1, 0 },
7174     { 0, +1 }
7175   };
7176
7177   for (i = 0; i < NUM_DIRECTIONS; i++)
7178   {
7179     for (j = 0; j < 4; j++)
7180     {
7181       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7182
7183       if (IN_LEV_FIELD(xx, yy) &&
7184           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7185       {
7186         if (Feld[xx][yy] == EL_DRAGON)
7187           dragon_found = TRUE;
7188       }
7189       else
7190         break;
7191     }
7192   }
7193
7194   if (!dragon_found)
7195   {
7196     for (i = 0; i < NUM_DIRECTIONS; i++)
7197     {
7198       for (j = 0; j < 3; j++)
7199       {
7200         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7201   
7202         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7203         {
7204           Feld[xx][yy] = EL_EMPTY;
7205           DrawLevelField(xx, yy);
7206         }
7207         else
7208           break;
7209       }
7210     }
7211   }
7212 }
7213
7214 static void InitBuggyBase(int x, int y)
7215 {
7216   int element = Feld[x][y];
7217   int activating_delay = FRAMES_PER_SECOND / 4;
7218
7219   ChangeDelay[x][y] =
7220     (element == EL_SP_BUGGY_BASE ?
7221      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7222      element == EL_SP_BUGGY_BASE_ACTIVATING ?
7223      activating_delay :
7224      element == EL_SP_BUGGY_BASE_ACTIVE ?
7225      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7226 }
7227
7228 static void WarnBuggyBase(int x, int y)
7229 {
7230   int i;
7231   static int xy[4][2] =
7232   {
7233     { 0, -1 },
7234     { -1, 0 },
7235     { +1, 0 },
7236     { 0, +1 }
7237   };
7238
7239   for (i = 0; i < NUM_DIRECTIONS; i++)
7240   {
7241     int xx = x + xy[i][0], yy = y + xy[i][1];
7242
7243     if (IS_PLAYER(xx, yy))
7244     {
7245       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7246
7247       break;
7248     }
7249   }
7250 }
7251
7252 static void InitTrap(int x, int y)
7253 {
7254   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7255 }
7256
7257 static void ActivateTrap(int x, int y)
7258 {
7259   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7260 }
7261
7262 static void ChangeActiveTrap(int x, int y)
7263 {
7264   int graphic = IMG_TRAP_ACTIVE;
7265
7266   /* if new animation frame was drawn, correct crumbled sand border */
7267   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7268     DrawLevelFieldCrumbledSand(x, y);
7269 }
7270
7271 static int getSpecialActionElement(int element, int number, int base_element)
7272 {
7273   return (element != EL_EMPTY ? element :
7274           number != -1 ? base_element + number - 1 :
7275           EL_EMPTY);
7276 }
7277
7278 static int getModifiedActionNumber(int value_old, int operator, int operand,
7279                                    int value_min, int value_max)
7280 {
7281   int value_new = (operator == CA_MODE_SET      ? operand :
7282                    operator == CA_MODE_ADD      ? value_old + operand :
7283                    operator == CA_MODE_SUBTRACT ? value_old - operand :
7284                    operator == CA_MODE_MULTIPLY ? value_old * operand :
7285                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
7286                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
7287                    value_old);
7288
7289   return (value_new < value_min ? value_min :
7290           value_new > value_max ? value_max :
7291           value_new);
7292 }
7293
7294 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7295 {
7296   struct ElementInfo *ei = &element_info[element];
7297   struct ElementChangeInfo *change = &ei->change_page[page];
7298   int action_type = change->action_type;
7299   int action_mode = change->action_mode;
7300   int action_arg = change->action_arg;
7301   int i;
7302
7303   if (!change->has_action)
7304     return;
7305
7306   /* ---------- determine action paramater values -------------------------- */
7307
7308   int level_time_value =
7309     (level.time > 0 ? TimeLeft :
7310      TimePlayed);
7311
7312   int action_arg_element =
7313     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
7314      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7315      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
7316      EL_EMPTY);
7317
7318   int action_arg_direction =
7319     (action_arg >= CA_ARG_DIRECTION_LEFT &&
7320      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7321      action_arg == CA_ARG_DIRECTION_TRIGGER ?
7322      change->actual_trigger_side :
7323      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7324      MV_DIR_OPPOSITE(change->actual_trigger_side) :
7325      MV_NONE);
7326
7327   int action_arg_number_min =
7328     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7329      CA_ARG_MIN);
7330
7331   int action_arg_number_max =
7332     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7333      action_type == CA_SET_LEVEL_GEMS ? 999 :
7334      action_type == CA_SET_LEVEL_TIME ? 9999 :
7335      action_type == CA_SET_LEVEL_SCORE ? 99999 :
7336      action_type == CA_SET_CE_SCORE ? 9999 :
7337      action_type == CA_SET_CE_VALUE ? 9999 :
7338      CA_ARG_MAX);
7339
7340   int action_arg_number_reset =
7341     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7342      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7343      action_type == CA_SET_LEVEL_TIME ? level.time :
7344      action_type == CA_SET_LEVEL_SCORE ? 0 :
7345      action_type == CA_SET_CE_SCORE ? 0 :
7346 #if 1
7347      action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7348 #else
7349      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7350 #endif
7351      0);
7352
7353   int action_arg_number =
7354     (action_arg <= CA_ARG_MAX ? action_arg :
7355      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7356      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7357      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7358      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7359      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7360      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7361      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7362 #if USE_NEW_CUSTOM_VALUE
7363      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7364 #else
7365      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7366 #endif
7367      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7368      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7369      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7370      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7371      action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7372      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
7373      -1);
7374
7375   int action_arg_number_old =
7376     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7377      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7378      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7379      action_type == CA_SET_CE_SCORE ? ei->collect_score :
7380      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7381      0);
7382
7383   int action_arg_number_new =
7384     getModifiedActionNumber(action_arg_number_old,
7385                             action_mode, action_arg_number,
7386                             action_arg_number_min, action_arg_number_max);
7387
7388   int trigger_player_bits =
7389     (change->actual_trigger_player >= EL_PLAYER_1 &&
7390      change->actual_trigger_player <= EL_PLAYER_4 ?
7391      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7392      PLAYER_BITS_ANY);
7393
7394   int action_arg_player_bits =
7395     (action_arg >= CA_ARG_PLAYER_1 &&
7396      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7397      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7398      PLAYER_BITS_ANY);
7399
7400   /* ---------- execute action  -------------------------------------------- */
7401
7402   switch(action_type)
7403   {
7404     case CA_NO_ACTION:
7405     {
7406       return;
7407     }
7408
7409     /* ---------- level actions  ------------------------------------------- */
7410
7411     case CA_RESTART_LEVEL:
7412     {
7413       game.restart_level = TRUE;
7414
7415       break;
7416     }
7417
7418     case CA_SHOW_ENVELOPE:
7419     {
7420       int element = getSpecialActionElement(action_arg_element,
7421                                             action_arg_number, EL_ENVELOPE_1);
7422
7423       if (IS_ENVELOPE(element))
7424         local_player->show_envelope = element;
7425
7426       break;
7427     }
7428
7429     case CA_SET_LEVEL_TIME:
7430     {
7431       if (level.time > 0)       /* only modify limited time value */
7432       {
7433         TimeLeft = action_arg_number_new;
7434
7435         DrawGameValue_Time(TimeLeft);
7436
7437         if (!TimeLeft && setup.time_limit)
7438           for (i = 0; i < MAX_PLAYERS; i++)
7439             KillPlayer(&stored_player[i]);
7440       }
7441
7442       break;
7443     }
7444
7445     case CA_SET_LEVEL_SCORE:
7446     {
7447       local_player->score = action_arg_number_new;
7448
7449       DrawGameValue_Score(local_player->score);
7450
7451       break;
7452     }
7453
7454     case CA_SET_LEVEL_GEMS:
7455     {
7456       local_player->gems_still_needed = action_arg_number_new;
7457
7458       DrawGameValue_Emeralds(local_player->gems_still_needed);
7459
7460       break;
7461     }
7462
7463     case CA_SET_LEVEL_GRAVITY:
7464     {
7465       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
7466                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
7467                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7468                       game.gravity);
7469       break;
7470     }
7471
7472     case CA_SET_LEVEL_WIND:
7473     {
7474       game.wind_direction = action_arg_direction;
7475
7476       break;
7477     }
7478
7479     /* ---------- player actions  ------------------------------------------ */
7480
7481     case CA_MOVE_PLAYER:
7482     {
7483       /* automatically move to the next field in specified direction */
7484       for (i = 0; i < MAX_PLAYERS; i++)
7485         if (trigger_player_bits & (1 << i))
7486           stored_player[i].programmed_action = action_arg_direction;
7487
7488       break;
7489     }
7490
7491     case CA_EXIT_PLAYER:
7492     {
7493       for (i = 0; i < MAX_PLAYERS; i++)
7494         if (action_arg_player_bits & (1 << i))
7495           stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7496
7497       break;
7498     }
7499
7500     case CA_KILL_PLAYER:
7501     {
7502       for (i = 0; i < MAX_PLAYERS; i++)
7503         if (action_arg_player_bits & (1 << i))
7504           KillPlayer(&stored_player[i]);
7505
7506       break;
7507     }
7508
7509     case CA_SET_PLAYER_KEYS:
7510     {
7511       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7512       int element = getSpecialActionElement(action_arg_element,
7513                                             action_arg_number, EL_KEY_1);
7514
7515       if (IS_KEY(element))
7516       {
7517         for (i = 0; i < MAX_PLAYERS; i++)
7518         {
7519           if (trigger_player_bits & (1 << i))
7520           {
7521             stored_player[i].key[KEY_NR(element)] = key_state;
7522
7523             DrawGameValue_Keys(stored_player[i].key);
7524
7525             redraw_mask |= REDRAW_DOOR_1;
7526           }
7527         }
7528       }
7529
7530       break;
7531     }
7532
7533     case CA_SET_PLAYER_SPEED:
7534     {
7535       for (i = 0; i < MAX_PLAYERS; i++)
7536       {
7537         if (trigger_player_bits & (1 << i))
7538         {
7539           int move_stepsize = TILEX / stored_player[i].move_delay_value;
7540
7541           if (action_arg == CA_ARG_SPEED_FASTER &&
7542               stored_player[i].cannot_move)
7543           {
7544             action_arg_number = STEPSIZE_VERY_SLOW;
7545           }
7546           else if (action_arg == CA_ARG_SPEED_SLOWER ||
7547                    action_arg == CA_ARG_SPEED_FASTER)
7548           {
7549             action_arg_number = 2;
7550             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7551                            CA_MODE_MULTIPLY);
7552           }
7553
7554           move_stepsize =
7555             getModifiedActionNumber(move_stepsize,
7556                                     action_mode,
7557                                     action_arg_number,
7558                                     action_arg_number_min,
7559                                     action_arg_number_max);
7560
7561 #if 1
7562           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7563 #else
7564           /* make sure that value is power of 2 */
7565           move_stepsize = (1 << log_2(move_stepsize));
7566
7567           /* do no immediately change -- the player might just be moving */
7568           stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7569
7570           stored_player[i].cannot_move =
7571             (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7572 #endif
7573         }
7574       }
7575
7576       break;
7577     }
7578
7579     case CA_SET_PLAYER_SHIELD:
7580     {
7581       for (i = 0; i < MAX_PLAYERS; i++)
7582       {
7583         if (trigger_player_bits & (1 << i))
7584         {
7585           if (action_arg == CA_ARG_SHIELD_OFF)
7586           {
7587             stored_player[i].shield_normal_time_left = 0;
7588             stored_player[i].shield_deadly_time_left = 0;
7589           }
7590           else if (action_arg == CA_ARG_SHIELD_NORMAL)
7591           {
7592             stored_player[i].shield_normal_time_left = 999999;
7593           }
7594           else if (action_arg == CA_ARG_SHIELD_DEADLY)
7595           {
7596             stored_player[i].shield_normal_time_left = 999999;
7597             stored_player[i].shield_deadly_time_left = 999999;
7598           }
7599         }
7600       }
7601
7602       break;
7603     }
7604
7605     case CA_SET_PLAYER_ARTWORK:
7606     {
7607       for (i = 0; i < MAX_PLAYERS; i++)
7608       {
7609         if (trigger_player_bits & (1 << i))
7610         {
7611           int artwork_element = action_arg_element;
7612
7613           if (action_arg == CA_ARG_ELEMENT_RESET)
7614             artwork_element =
7615               (level.use_artwork_element[i] ? level.artwork_element[i] :
7616                stored_player[i].element_nr);
7617
7618           stored_player[i].artwork_element = artwork_element;
7619
7620           SetPlayerWaiting(&stored_player[i], FALSE);
7621
7622           /* set number of special actions for bored and sleeping animation */
7623           stored_player[i].num_special_action_bored =
7624             get_num_special_action(artwork_element,
7625                                    ACTION_BORING_1, ACTION_BORING_LAST);
7626           stored_player[i].num_special_action_sleeping =
7627             get_num_special_action(artwork_element,
7628                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7629         }
7630       }
7631
7632       break;
7633     }
7634
7635     /* ---------- CE actions  ---------------------------------------------- */
7636
7637     case CA_SET_CE_SCORE:
7638     {
7639       ei->collect_score = action_arg_number_new;
7640
7641       break;
7642     }
7643
7644     case CA_SET_CE_VALUE:
7645     {
7646 #if USE_NEW_CUSTOM_VALUE
7647       int last_custom_value = CustomValue[x][y];
7648
7649       CustomValue[x][y] = action_arg_number_new;
7650
7651 #if 0
7652       printf("::: Count == %d\n", CustomValue[x][y]);
7653 #endif
7654
7655       if (CustomValue[x][y] == 0 && last_custom_value > 0)
7656       {
7657 #if 0
7658         printf("::: CE_VALUE_GETS_ZERO\n");
7659 #endif
7660
7661         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7662         CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7663       }
7664 #endif
7665
7666       break;
7667     }
7668
7669     /* ---------- engine actions  ------------------------------------------ */
7670
7671     case CA_SET_ENGINE_SCAN_MODE:
7672     {
7673       InitPlayfieldScanMode(action_arg);
7674
7675       break;
7676     }
7677
7678     default:
7679       break;
7680   }
7681 }
7682
7683 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7684 {
7685   int previous_move_direction = MovDir[x][y];
7686 #if USE_NEW_CUSTOM_VALUE
7687   int last_ce_value = CustomValue[x][y];
7688 #endif
7689   boolean add_player = (ELEM_IS_PLAYER(element) &&
7690                         IS_WALKABLE(Feld[x][y]));
7691
7692   /* check if element under player changes from accessible to unaccessible
7693      (needed for special case of dropping element which then changes) */
7694   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7695       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7696   {
7697     Bang(x, y);
7698
7699     return;
7700   }
7701
7702   if (!add_player)
7703   {
7704     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7705       RemoveMovingField(x, y);
7706     else
7707       RemoveField(x, y);
7708
7709     Feld[x][y] = element;
7710
7711     ResetGfxAnimation(x, y);
7712     ResetRandomAnimationValue(x, y);
7713
7714     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7715       MovDir[x][y] = previous_move_direction;
7716
7717 #if USE_NEW_CUSTOM_VALUE
7718     if (element_info[Feld[x][y]].use_last_ce_value)
7719       CustomValue[x][y] = last_ce_value;
7720 #endif
7721
7722     InitField_WithBug1(x, y, FALSE);
7723
7724     DrawLevelField(x, y);
7725
7726     if (GFX_CRUMBLED(Feld[x][y]))
7727       DrawLevelFieldCrumbledSandNeighbours(x, y);
7728   }
7729
7730   /* "ChangeCount" not set yet to allow "entered by player" change one time */
7731   if (ELEM_IS_PLAYER(element))
7732     RelocatePlayer(x, y, element);
7733
7734   if (is_change)
7735     ChangeCount[x][y]++;        /* count number of changes in the same frame */
7736
7737   TestIfBadThingTouchesPlayer(x, y);
7738   TestIfPlayerTouchesCustomElement(x, y);
7739   TestIfElementTouchesCustomElement(x, y);
7740 }
7741
7742 static void CreateField(int x, int y, int element)
7743 {
7744   CreateFieldExt(x, y, element, FALSE);
7745 }
7746
7747 static void CreateElementFromChange(int x, int y, int element)
7748 {
7749   CreateFieldExt(x, y, element, TRUE);
7750 }
7751
7752 static boolean ChangeElement(int x, int y, int element, int page)
7753 {
7754   struct ElementChangeInfo *change = &element_info[element].change_page[page];
7755   int target_element;
7756   int old_element = Feld[x][y];
7757
7758   /* always use default change event to prevent running into a loop */
7759   if (ChangeEvent[x][y] == -1)
7760     ChangeEvent[x][y] = CE_DELAY;
7761
7762   if (ChangeEvent[x][y] == CE_DELAY)
7763   {
7764     /* reset actual trigger element, trigger player and action element */
7765     change->actual_trigger_element = EL_EMPTY;
7766     change->actual_trigger_player = EL_PLAYER_1;
7767     change->actual_trigger_side = CH_SIDE_NONE;
7768     change->actual_trigger_ce_value = 0;
7769   }
7770
7771   /* do not change elements more than a specified maximum number of changes */
7772   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7773     return FALSE;
7774
7775   ChangeCount[x][y]++;          /* count number of changes in the same frame */
7776
7777   if (change->explode)
7778   {
7779     Bang(x, y);
7780
7781     return TRUE;
7782   }
7783
7784   if (change->use_target_content)
7785   {
7786     boolean complete_replace = TRUE;
7787     boolean can_replace[3][3];
7788     int xx, yy;
7789
7790     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7791     {
7792       boolean is_empty;
7793       boolean is_walkable;
7794       boolean is_diggable;
7795       boolean is_collectible;
7796       boolean is_removable;
7797       boolean is_destructible;
7798       int ex = x + xx - 1;
7799       int ey = y + yy - 1;
7800       int content_element = change->target_content.e[xx][yy];
7801       int e;
7802
7803       can_replace[xx][yy] = TRUE;
7804
7805       if (ex == x && ey == y)   /* do not check changing element itself */
7806         continue;
7807
7808       if (content_element == EL_EMPTY_SPACE)
7809       {
7810         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
7811
7812         continue;
7813       }
7814
7815       if (!IN_LEV_FIELD(ex, ey))
7816       {
7817         can_replace[xx][yy] = FALSE;
7818         complete_replace = FALSE;
7819
7820         continue;
7821       }
7822
7823       e = Feld[ex][ey];
7824
7825       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7826         e = MovingOrBlocked2Element(ex, ey);
7827
7828       is_empty = (IS_FREE(ex, ey) ||
7829                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7830
7831       is_walkable     = (is_empty || IS_WALKABLE(e));
7832       is_diggable     = (is_empty || IS_DIGGABLE(e));
7833       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
7834       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7835       is_removable    = (is_diggable || is_collectible);
7836
7837       can_replace[xx][yy] =
7838         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
7839           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
7840           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
7841           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
7842           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
7843           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7844          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7845
7846       if (!can_replace[xx][yy])
7847         complete_replace = FALSE;
7848     }
7849
7850     if (!change->only_if_complete || complete_replace)
7851     {
7852       boolean something_has_changed = FALSE;
7853
7854       if (change->only_if_complete && change->use_random_replace &&
7855           RND(100) < change->random_percentage)
7856         return FALSE;
7857
7858       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7859       {
7860         int ex = x + xx - 1;
7861         int ey = y + yy - 1;
7862         int content_element;
7863
7864         if (can_replace[xx][yy] && (!change->use_random_replace ||
7865                                     RND(100) < change->random_percentage))
7866         {
7867           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7868             RemoveMovingField(ex, ey);
7869
7870           ChangeEvent[ex][ey] = ChangeEvent[x][y];
7871
7872           content_element = change->target_content.e[xx][yy];
7873           target_element = GET_TARGET_ELEMENT(content_element, change);
7874
7875           CreateElementFromChange(ex, ey, target_element);
7876
7877           something_has_changed = TRUE;
7878
7879           /* for symmetry reasons, freeze newly created border elements */
7880           if (ex != x || ey != y)
7881             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
7882         }
7883       }
7884
7885       if (something_has_changed)
7886       {
7887         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7888         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7889       }
7890     }
7891   }
7892   else
7893   {
7894     target_element = GET_TARGET_ELEMENT(change->target_element, change);
7895
7896     if (element == EL_DIAGONAL_GROWING)
7897       target_element = Store[x][y];
7898
7899     CreateElementFromChange(x, y, target_element);
7900
7901     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7902     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7903   }
7904
7905   /* this uses direct change before indirect change */
7906   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7907
7908   return TRUE;
7909 }
7910
7911 #if USE_NEW_DELAYED_ACTION
7912
7913 static void HandleElementChange(int x, int y, int page)
7914 {
7915   int element = MovingOrBlocked2Element(x, y);
7916   struct ElementInfo *ei = &element_info[element];
7917   struct ElementChangeInfo *change = &ei->change_page[page];
7918
7919 #ifdef DEBUG
7920   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7921       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7922   {
7923     printf("\n\n");
7924     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7925            x, y, element, element_info[element].token_name);
7926     printf("HandleElementChange(): This should never happen!\n");
7927     printf("\n\n");
7928   }
7929 #endif
7930
7931   /* this can happen with classic bombs on walkable, changing elements */
7932   if (!CAN_CHANGE_OR_HAS_ACTION(element))
7933   {
7934 #if 0
7935     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7936       ChangeDelay[x][y] = 0;
7937 #endif
7938
7939     return;
7940   }
7941
7942   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7943   {
7944     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7945
7946     if (change->can_change)
7947     {
7948       ResetGfxAnimation(x, y);
7949       ResetRandomAnimationValue(x, y);
7950
7951       if (change->pre_change_function)
7952         change->pre_change_function(x, y);
7953     }
7954   }
7955
7956   ChangeDelay[x][y]--;
7957
7958   if (ChangeDelay[x][y] != 0)           /* continue element change */
7959   {
7960     if (change->can_change)
7961     {
7962       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7963
7964       if (IS_ANIMATED(graphic))
7965         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7966
7967       if (change->change_function)
7968         change->change_function(x, y);
7969     }
7970   }
7971   else                                  /* finish element change */
7972   {
7973     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7974     {
7975       page = ChangePage[x][y];
7976       ChangePage[x][y] = -1;
7977
7978       change = &ei->change_page[page];
7979     }
7980
7981     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7982     {
7983       ChangeDelay[x][y] = 1;            /* try change after next move step */
7984       ChangePage[x][y] = page;          /* remember page to use for change */
7985
7986       return;
7987     }
7988
7989     if (change->can_change)
7990     {
7991       if (ChangeElement(x, y, element, page))
7992       {
7993         if (change->post_change_function)
7994           change->post_change_function(x, y);
7995       }
7996     }
7997
7998     if (change->has_action)
7999       ExecuteCustomElementAction(x, y, element, page);
8000   }
8001 }
8002
8003 #else
8004
8005 static void HandleElementChange(int x, int y, int page)
8006 {
8007   int element = MovingOrBlocked2Element(x, y);
8008   struct ElementInfo *ei = &element_info[element];
8009   struct ElementChangeInfo *change = &ei->change_page[page];
8010
8011 #ifdef DEBUG
8012   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8013   {
8014     printf("\n\n");
8015     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8016            x, y, element, element_info[element].token_name);
8017     printf("HandleElementChange(): This should never happen!\n");
8018     printf("\n\n");
8019   }
8020 #endif
8021
8022   /* this can happen with classic bombs on walkable, changing elements */
8023   if (!CAN_CHANGE(element))
8024   {
8025 #if 0
8026     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8027       ChangeDelay[x][y] = 0;
8028 #endif
8029
8030     return;
8031   }
8032
8033   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8034   {
8035     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8036
8037     ResetGfxAnimation(x, y);
8038     ResetRandomAnimationValue(x, y);
8039
8040     if (change->pre_change_function)
8041       change->pre_change_function(x, y);
8042   }
8043
8044   ChangeDelay[x][y]--;
8045
8046   if (ChangeDelay[x][y] != 0)           /* continue element change */
8047   {
8048     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8049
8050     if (IS_ANIMATED(graphic))
8051       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8052
8053     if (change->change_function)
8054       change->change_function(x, y);
8055   }
8056   else                                  /* finish element change */
8057   {
8058     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8059     {
8060       page = ChangePage[x][y];
8061       ChangePage[x][y] = -1;
8062
8063       change = &ei->change_page[page];
8064     }
8065
8066     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8067     {
8068       ChangeDelay[x][y] = 1;            /* try change after next move step */
8069       ChangePage[x][y] = page;          /* remember page to use for change */
8070
8071       return;
8072     }
8073
8074     if (ChangeElement(x, y, element, page))
8075     {
8076       if (change->post_change_function)
8077         change->post_change_function(x, y);
8078     }
8079   }
8080 }
8081
8082 #endif
8083
8084 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8085                                               int trigger_element,
8086                                               int trigger_event,
8087                                               int trigger_player,
8088                                               int trigger_side,
8089                                               int trigger_page)
8090 {
8091   boolean change_done_any = FALSE;
8092   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8093   int i;
8094
8095   if (!(trigger_events[trigger_element][trigger_event]))
8096     return FALSE;
8097
8098   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8099   {
8100     int element = EL_CUSTOM_START + i;
8101     boolean change_done = FALSE;
8102     int p;
8103
8104     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8105         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8106       continue;
8107
8108     for (p = 0; p < element_info[element].num_change_pages; p++)
8109     {
8110       struct ElementChangeInfo *change = &element_info[element].change_page[p];
8111
8112       if (change->can_change_or_has_action &&
8113           change->has_event[trigger_event] &&
8114           change->trigger_side & trigger_side &&
8115           change->trigger_player & trigger_player &&
8116           change->trigger_page & trigger_page_bits &&
8117           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8118       {
8119         change->actual_trigger_element = trigger_element;
8120         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8121         change->actual_trigger_side = trigger_side;
8122         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8123
8124         if ((change->can_change && !change_done) || change->has_action)
8125         {
8126           int x, y;
8127
8128 #if 1
8129           SCAN_PLAYFIELD(x, y)
8130 #else
8131           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8132 #endif
8133           {
8134             if (Feld[x][y] == element)
8135             {
8136               if (change->can_change && !change_done)
8137               {
8138                 ChangeDelay[x][y] = 1;
8139                 ChangeEvent[x][y] = trigger_event;
8140
8141                 HandleElementChange(x, y, p);
8142               }
8143 #if USE_NEW_DELAYED_ACTION
8144               else if (change->has_action)
8145               {
8146                 ExecuteCustomElementAction(x, y, element, p);
8147                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8148               }
8149 #else
8150               if (change->has_action)
8151               {
8152                 ExecuteCustomElementAction(x, y, element, p);
8153                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8154               }
8155 #endif
8156             }
8157           }
8158
8159           if (change->can_change)
8160           {
8161             change_done = TRUE;
8162             change_done_any = TRUE;
8163           }
8164         }
8165       }
8166     }
8167   }
8168
8169   return change_done_any;
8170 }
8171
8172 static boolean CheckElementChangeExt(int x, int y,
8173                                      int element,
8174                                      int trigger_element,
8175                                      int trigger_event,
8176                                      int trigger_player,
8177                                      int trigger_side)
8178 {
8179   boolean change_done = FALSE;
8180   int p;
8181
8182   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8183       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8184     return FALSE;
8185
8186   if (Feld[x][y] == EL_BLOCKED)
8187   {
8188     Blocked2Moving(x, y, &x, &y);
8189     element = Feld[x][y];
8190   }
8191
8192   if (Feld[x][y] != element)    /* check if element has already changed */
8193     return FALSE;
8194
8195   for (p = 0; p < element_info[element].num_change_pages; p++)
8196   {
8197     struct ElementChangeInfo *change = &element_info[element].change_page[p];
8198
8199     boolean check_trigger_element =
8200       (trigger_event == CE_TOUCHING_X ||
8201        trigger_event == CE_HITTING_X ||
8202        trigger_event == CE_HIT_BY_X);
8203
8204     if (change->can_change_or_has_action &&
8205         change->has_event[trigger_event] &&
8206         change->trigger_side & trigger_side &&
8207         change->trigger_player & trigger_player &&
8208         (!check_trigger_element ||
8209          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8210     {
8211       change->actual_trigger_element = trigger_element;
8212       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8213       change->actual_trigger_side = trigger_side;
8214       change->actual_trigger_ce_value = CustomValue[x][y];
8215
8216       /* special case: trigger element not at (x,y) position for some events */
8217       if (check_trigger_element)
8218       {
8219         static struct
8220         {
8221           int dx, dy;
8222         } move_xy[] =
8223           {
8224             {  0,  0 },
8225             { -1,  0 },
8226             { +1,  0 },
8227             {  0,  0 },
8228             {  0, -1 },
8229             {  0,  0 }, { 0, 0 }, { 0, 0 },
8230             {  0, +1 }
8231           };
8232
8233         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8234         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8235
8236         change->actual_trigger_ce_value = CustomValue[xx][yy];
8237       }
8238
8239       if (change->can_change && !change_done)
8240       {
8241         ChangeDelay[x][y] = 1;
8242         ChangeEvent[x][y] = trigger_event;
8243
8244         HandleElementChange(x, y, p);
8245
8246         change_done = TRUE;
8247       }
8248 #if USE_NEW_DELAYED_ACTION
8249       else if (change->has_action)
8250       {
8251         ExecuteCustomElementAction(x, y, element, p);
8252         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8253       }
8254 #else
8255       if (change->has_action)
8256       {
8257         ExecuteCustomElementAction(x, y, element, p);
8258         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8259       }
8260 #endif
8261     }
8262   }
8263
8264   return change_done;
8265 }
8266
8267 static void PlayPlayerSound(struct PlayerInfo *player)
8268 {
8269   int jx = player->jx, jy = player->jy;
8270   int sound_element = player->artwork_element;
8271   int last_action = player->last_action_waiting;
8272   int action = player->action_waiting;
8273
8274   if (player->is_waiting)
8275   {
8276     if (action != last_action)
8277       PlayLevelSoundElementAction(jx, jy, sound_element, action);
8278     else
8279       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8280   }
8281   else
8282   {
8283     if (action != last_action)
8284       StopSound(element_info[sound_element].sound[last_action]);
8285
8286     if (last_action == ACTION_SLEEPING)
8287       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8288   }
8289 }
8290
8291 static void PlayAllPlayersSound()
8292 {
8293   int i;
8294
8295   for (i = 0; i < MAX_PLAYERS; i++)
8296     if (stored_player[i].active)
8297       PlayPlayerSound(&stored_player[i]);
8298 }
8299
8300 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8301 {
8302   boolean last_waiting = player->is_waiting;
8303   int move_dir = player->MovDir;
8304
8305   player->last_action_waiting = player->action_waiting;
8306
8307   if (is_waiting)
8308   {
8309     if (!last_waiting)          /* not waiting -> waiting */
8310     {
8311       player->is_waiting = TRUE;
8312
8313       player->frame_counter_bored =
8314         FrameCounter +
8315         game.player_boring_delay_fixed +
8316         SimpleRND(game.player_boring_delay_random);
8317       player->frame_counter_sleeping =
8318         FrameCounter +
8319         game.player_sleeping_delay_fixed +
8320         SimpleRND(game.player_sleeping_delay_random);
8321
8322       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8323     }
8324
8325     if (game.player_sleeping_delay_fixed +
8326         game.player_sleeping_delay_random > 0 &&
8327         player->anim_delay_counter == 0 &&
8328         player->post_delay_counter == 0 &&
8329         FrameCounter >= player->frame_counter_sleeping)
8330       player->is_sleeping = TRUE;
8331     else if (game.player_boring_delay_fixed +
8332              game.player_boring_delay_random > 0 &&
8333              FrameCounter >= player->frame_counter_bored)
8334       player->is_bored = TRUE;
8335
8336     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8337                               player->is_bored ? ACTION_BORING :
8338                               ACTION_WAITING);
8339
8340     if (player->is_sleeping)
8341     {
8342       if (player->num_special_action_sleeping > 0)
8343       {
8344         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8345         {
8346           int last_special_action = player->special_action_sleeping;
8347           int num_special_action = player->num_special_action_sleeping;
8348           int special_action =
8349             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8350              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8351              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8352              last_special_action + 1 : ACTION_SLEEPING);
8353           int special_graphic =
8354             el_act_dir2img(player->artwork_element, special_action, move_dir);
8355
8356           player->anim_delay_counter =
8357             graphic_info[special_graphic].anim_delay_fixed +
8358             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8359           player->post_delay_counter =
8360             graphic_info[special_graphic].post_delay_fixed +
8361             SimpleRND(graphic_info[special_graphic].post_delay_random);
8362
8363           player->special_action_sleeping = special_action;
8364         }
8365
8366         if (player->anim_delay_counter > 0)
8367         {
8368           player->action_waiting = player->special_action_sleeping;
8369           player->anim_delay_counter--;
8370         }
8371         else if (player->post_delay_counter > 0)
8372         {
8373           player->post_delay_counter--;
8374         }
8375       }
8376     }
8377     else if (player->is_bored)
8378     {
8379       if (player->num_special_action_bored > 0)
8380       {
8381         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8382         {
8383           int special_action =
8384             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8385           int special_graphic =
8386             el_act_dir2img(player->artwork_element, special_action, move_dir);
8387
8388           player->anim_delay_counter =
8389             graphic_info[special_graphic].anim_delay_fixed +
8390             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8391           player->post_delay_counter =
8392             graphic_info[special_graphic].post_delay_fixed +
8393             SimpleRND(graphic_info[special_graphic].post_delay_random);
8394
8395           player->special_action_bored = special_action;
8396         }
8397
8398         if (player->anim_delay_counter > 0)
8399         {
8400           player->action_waiting = player->special_action_bored;
8401           player->anim_delay_counter--;
8402         }
8403         else if (player->post_delay_counter > 0)
8404         {
8405           player->post_delay_counter--;
8406         }
8407       }
8408     }
8409   }
8410   else if (last_waiting)        /* waiting -> not waiting */
8411   {
8412     player->is_waiting = FALSE;
8413     player->is_bored = FALSE;
8414     player->is_sleeping = FALSE;
8415
8416     player->frame_counter_bored = -1;
8417     player->frame_counter_sleeping = -1;
8418
8419     player->anim_delay_counter = 0;
8420     player->post_delay_counter = 0;
8421
8422     player->action_waiting = ACTION_DEFAULT;
8423
8424     player->special_action_bored = ACTION_DEFAULT;
8425     player->special_action_sleeping = ACTION_DEFAULT;
8426   }
8427 }
8428
8429 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8430 {
8431   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8432   int left      = player_action & JOY_LEFT;
8433   int right     = player_action & JOY_RIGHT;
8434   int up        = player_action & JOY_UP;
8435   int down      = player_action & JOY_DOWN;
8436   int button1   = player_action & JOY_BUTTON_1;
8437   int button2   = player_action & JOY_BUTTON_2;
8438   int dx        = (left ? -1    : right ? 1     : 0);
8439   int dy        = (up   ? -1    : down  ? 1     : 0);
8440
8441   if (!player->active || tape.pausing)
8442     return 0;
8443
8444   if (player_action)
8445   {
8446     if (button1)
8447       snapped = SnapField(player, dx, dy);
8448     else
8449     {
8450       if (button2)
8451         dropped = DropElement(player);
8452
8453       moved = MovePlayer(player, dx, dy);
8454     }
8455
8456     if (tape.single_step && tape.recording && !tape.pausing)
8457     {
8458       if (button1 || (dropped && !moved))
8459       {
8460         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8461         SnapField(player, 0, 0);                /* stop snapping */
8462       }
8463     }
8464
8465     SetPlayerWaiting(player, FALSE);
8466
8467     return player_action;
8468   }
8469   else
8470   {
8471     /* no actions for this player (no input at player's configured device) */
8472
8473     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8474     SnapField(player, 0, 0);
8475     CheckGravityMovementWhenNotMoving(player);
8476
8477     if (player->MovPos == 0)
8478       SetPlayerWaiting(player, TRUE);
8479
8480     if (player->MovPos == 0)    /* needed for tape.playing */
8481       player->is_moving = FALSE;
8482
8483     player->is_dropping = FALSE;
8484
8485     return 0;
8486   }
8487 }
8488
8489 void AdvanceFrameAndPlayerCounters(int player_nr)
8490 {
8491   int i;
8492
8493   /* advance frame counters (global frame counter and time frame counter) */
8494   FrameCounter++;
8495   TimeFrames++;
8496
8497   /* advance player counters (counters for move delay, move animation etc.) */
8498   for (i = 0; i < MAX_PLAYERS; i++)
8499   {
8500     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8501     int move_delay_value = stored_player[i].move_delay_value;
8502     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8503
8504     if (!advance_player_counters)       /* not all players may be affected */
8505       continue;
8506
8507 #if USE_NEW_PLAYER_ANIM
8508     if (move_frames == 0)       /* less than one move per game frame */
8509     {
8510       int stepsize = TILEX / move_delay_value;
8511       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8512       int count = (stored_player[i].is_moving ?
8513                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8514
8515       if (count % delay == 0)
8516         move_frames = 1;
8517     }
8518 #endif
8519
8520     stored_player[i].Frame += move_frames;
8521
8522     if (stored_player[i].MovPos != 0)
8523       stored_player[i].StepFrame += move_frames;
8524
8525     if (stored_player[i].move_delay > 0)
8526       stored_player[i].move_delay--;
8527
8528     /* due to bugs in previous versions, counter must count up, not down */
8529     if (stored_player[i].push_delay != -1)
8530       stored_player[i].push_delay++;
8531
8532     if (stored_player[i].drop_delay > 0)
8533       stored_player[i].drop_delay--;
8534   }
8535 }
8536
8537 void GameActions()
8538 {
8539   static unsigned long game_frame_delay = 0;
8540   unsigned long game_frame_delay_value;
8541   int magic_wall_x = 0, magic_wall_y = 0;
8542   int i, x, y, element, graphic;
8543   byte *recorded_player_action;
8544   byte summarized_player_action = 0;
8545   byte tape_action[MAX_PLAYERS];
8546
8547   if (game_status != GAME_MODE_PLAYING)
8548     return;
8549
8550   game_frame_delay_value =
8551     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8552
8553   if (tape.playing && tape.warp_forward && !tape.pausing)
8554     game_frame_delay_value = 0;
8555
8556   /* ---------- main game synchronization point ---------- */
8557
8558   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8559
8560   InitPlayfieldScanModeVars();
8561
8562 #if USE_ONE_MORE_CHANGE_PER_FRAME
8563   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8564   {
8565     SCAN_PLAYFIELD(x, y)
8566     {
8567       ChangeCount[x][y] = 0;
8568       ChangeEvent[x][y] = -1;
8569     }
8570   }
8571 #endif
8572
8573   if (network_playing && !network_player_action_received)
8574   {
8575     /* try to get network player actions in time */
8576
8577 #if defined(NETWORK_AVALIABLE)
8578     /* last chance to get network player actions without main loop delay */
8579     HandleNetworking();
8580 #endif
8581
8582     /* game was quit by network peer */
8583     if (game_status != GAME_MODE_PLAYING)
8584       return;
8585
8586     if (!network_player_action_received)
8587       return;           /* failed to get network player actions in time */
8588   }
8589
8590   if (tape.pausing)
8591     return;
8592
8593   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8594
8595 #if 1
8596   /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8597   if (recorded_player_action == NULL && tape.pausing)
8598     return;
8599 #endif
8600
8601   for (i = 0; i < MAX_PLAYERS; i++)
8602   {
8603     summarized_player_action |= stored_player[i].action;
8604
8605     if (!network_playing)
8606       stored_player[i].effective_action = stored_player[i].action;
8607   }
8608
8609 #if defined(NETWORK_AVALIABLE)
8610   if (network_playing)
8611     SendToServer_MovePlayer(summarized_player_action);
8612 #endif
8613
8614   if (!options.network && !setup.team_mode)
8615     local_player->effective_action = summarized_player_action;
8616
8617   if (recorded_player_action != NULL)
8618     for (i = 0; i < MAX_PLAYERS; i++)
8619       stored_player[i].effective_action = recorded_player_action[i];
8620
8621   for (i = 0; i < MAX_PLAYERS; i++)
8622   {
8623     tape_action[i] = stored_player[i].effective_action;
8624
8625     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8626       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8627   }
8628
8629   /* only save actions from input devices, but not programmed actions */
8630   if (tape.recording)
8631     TapeRecordAction(tape_action);
8632
8633   for (i = 0; i < MAX_PLAYERS; i++)
8634   {
8635     int actual_player_action = stored_player[i].effective_action;
8636
8637 #if 1
8638     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8639        - rnd_equinox_tetrachloride 048
8640        - rnd_equinox_tetrachloride_ii 096
8641        - rnd_emanuel_schmieg 002
8642        - doctor_sloan_ww 001, 020
8643     */
8644     if (stored_player[i].MovPos == 0)
8645       CheckGravityMovement(&stored_player[i]);
8646 #endif
8647
8648     /* overwrite programmed action with tape action */
8649     if (stored_player[i].programmed_action)
8650       actual_player_action = stored_player[i].programmed_action;
8651
8652 #if 1
8653     PlayerActions(&stored_player[i], actual_player_action);
8654 #else
8655     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8656
8657     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8658       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8659 #endif
8660
8661     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8662   }
8663
8664   network_player_action_received = FALSE;
8665
8666   ScrollScreen(NULL, SCROLL_GO_ON);
8667
8668   /* for backwards compatibility, the following code emulates a fixed bug that
8669      occured when pushing elements (causing elements that just made their last
8670      pushing step to already (if possible) make their first falling step in the
8671      same game frame, which is bad); this code is also needed to use the famous
8672      "spring push bug" which is used in older levels and might be wanted to be
8673      used also in newer levels, but in this case the buggy pushing code is only
8674      affecting the "spring" element and no other elements */
8675
8676   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8677   {
8678     for (i = 0; i < MAX_PLAYERS; i++)
8679     {
8680       struct PlayerInfo *player = &stored_player[i];
8681       int x = player->jx;
8682       int y = player->jy;
8683
8684       if (player->active && player->is_pushing && player->is_moving &&
8685           IS_MOVING(x, y) &&
8686           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8687            Feld[x][y] == EL_SPRING))
8688       {
8689         ContinueMoving(x, y);
8690
8691         /* continue moving after pushing (this is actually a bug) */
8692         if (!IS_MOVING(x, y))
8693         {
8694           Stop[x][y] = FALSE;
8695         }
8696       }
8697     }
8698   }
8699
8700 #if 1
8701   SCAN_PLAYFIELD(x, y)
8702 #else
8703   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8704 #endif
8705   {
8706     ChangeCount[x][y] = 0;
8707     ChangeEvent[x][y] = -1;
8708
8709     /* this must be handled before main playfield loop */
8710     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8711     {
8712       MovDelay[x][y]--;
8713       if (MovDelay[x][y] <= 0)
8714         RemoveField(x, y);
8715     }
8716
8717 #if USE_NEW_SNAP_DELAY
8718     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8719     {
8720       MovDelay[x][y]--;
8721       if (MovDelay[x][y] <= 0)
8722       {
8723         RemoveField(x, y);
8724         DrawLevelField(x, y);
8725
8726         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
8727       }
8728     }
8729 #endif
8730
8731 #if DEBUG
8732     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8733     {
8734       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8735       printf("GameActions(): This should never happen!\n");
8736
8737       ChangePage[x][y] = -1;
8738     }
8739 #endif
8740
8741     Stop[x][y] = FALSE;
8742     if (WasJustMoving[x][y] > 0)
8743       WasJustMoving[x][y]--;
8744     if (WasJustFalling[x][y] > 0)
8745       WasJustFalling[x][y]--;
8746     if (CheckCollision[x][y] > 0)
8747       CheckCollision[x][y]--;
8748
8749     GfxFrame[x][y]++;
8750
8751     /* reset finished pushing action (not done in ContinueMoving() to allow
8752        continuous pushing animation for elements with zero push delay) */
8753     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8754     {
8755       ResetGfxAnimation(x, y);
8756       DrawLevelField(x, y);
8757     }
8758
8759 #if DEBUG
8760     if (IS_BLOCKED(x, y))
8761     {
8762       int oldx, oldy;
8763
8764       Blocked2Moving(x, y, &oldx, &oldy);
8765       if (!IS_MOVING(oldx, oldy))
8766       {
8767         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8768         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8769         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8770         printf("GameActions(): This should never happen!\n");
8771       }
8772     }
8773 #endif
8774   }
8775
8776 #if 1
8777   SCAN_PLAYFIELD(x, y)
8778 #else
8779   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8780 #endif
8781   {
8782     element = Feld[x][y];
8783     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8784
8785 #if 0
8786     printf("::: %d,%d\n", x, y);
8787
8788     if (element == EL_ROCK)
8789       printf("::: Yo man! Rocks can fall!\n");
8790 #endif
8791
8792     if (graphic_info[graphic].anim_global_sync)
8793       GfxFrame[x][y] = FrameCounter;
8794     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
8795     {
8796       int old_gfx_frame = GfxFrame[x][y];
8797
8798       GfxFrame[x][y] = CustomValue[x][y];
8799
8800 #if 1
8801       if (GfxFrame[x][y] != old_gfx_frame)
8802 #endif
8803         DrawLevelGraphicAnimation(x, y, graphic);
8804     }
8805     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
8806     {
8807       int old_gfx_frame = GfxFrame[x][y];
8808
8809       GfxFrame[x][y] = element_info[element].collect_score;
8810
8811 #if 1
8812       if (GfxFrame[x][y] != old_gfx_frame)
8813 #endif
8814         DrawLevelGraphicAnimation(x, y, graphic);
8815     }
8816
8817     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8818         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8819       ResetRandomAnimationValue(x, y);
8820
8821     SetRandomAnimationValue(x, y);
8822
8823     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8824
8825     if (IS_INACTIVE(element))
8826     {
8827       if (IS_ANIMATED(graphic))
8828         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8829
8830       continue;
8831     }
8832
8833     /* this may take place after moving, so 'element' may have changed */
8834     if (IS_CHANGING(x, y) &&
8835         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8836     {
8837       int page = element_info[element].event_page_nr[CE_DELAY];
8838 #if 0
8839       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8840 #else
8841
8842 #if 0
8843       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8844 #endif
8845
8846 #if 0
8847       if (element == EL_CUSTOM_255)
8848         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8849 #endif
8850
8851 #if 1
8852       HandleElementChange(x, y, page);
8853 #else
8854       if (CAN_CHANGE(element))
8855         HandleElementChange(x, y, page);
8856
8857       if (HAS_ACTION(element))
8858         ExecuteCustomElementAction(x, y, element, page);
8859 #endif
8860
8861 #endif
8862
8863       element = Feld[x][y];
8864       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8865     }
8866
8867     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8868     {
8869       StartMoving(x, y);
8870
8871       element = Feld[x][y];
8872       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8873
8874       if (IS_ANIMATED(graphic) &&
8875           !IS_MOVING(x, y) &&
8876           !Stop[x][y])
8877         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8878
8879       if (IS_GEM(element) || element == EL_SP_INFOTRON)
8880         EdelsteinFunkeln(x, y);
8881     }
8882     else if ((element == EL_ACID ||
8883               element == EL_EXIT_OPEN ||
8884               element == EL_SP_EXIT_OPEN ||
8885               element == EL_SP_TERMINAL ||
8886               element == EL_SP_TERMINAL_ACTIVE ||
8887               element == EL_EXTRA_TIME ||
8888               element == EL_SHIELD_NORMAL ||
8889               element == EL_SHIELD_DEADLY) &&
8890              IS_ANIMATED(graphic))
8891       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8892     else if (IS_MOVING(x, y))
8893       ContinueMoving(x, y);
8894     else if (IS_ACTIVE_BOMB(element))
8895       CheckDynamite(x, y);
8896     else if (element == EL_AMOEBA_GROWING)
8897       AmoebeWaechst(x, y);
8898     else if (element == EL_AMOEBA_SHRINKING)
8899       AmoebaDisappearing(x, y);
8900
8901 #if !USE_NEW_AMOEBA_CODE
8902     else if (IS_AMOEBALIVE(element))
8903       AmoebeAbleger(x, y);
8904 #endif
8905
8906     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8907       Life(x, y);
8908     else if (element == EL_EXIT_CLOSED)
8909       CheckExit(x, y);
8910     else if (element == EL_SP_EXIT_CLOSED)
8911       CheckExitSP(x, y);
8912     else if (element == EL_EXPANDABLE_WALL_GROWING)
8913       MauerWaechst(x, y);
8914     else if (element == EL_EXPANDABLE_WALL ||
8915              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8916              element == EL_EXPANDABLE_WALL_VERTICAL ||
8917              element == EL_EXPANDABLE_WALL_ANY)
8918       MauerAbleger(x, y);
8919     else if (element == EL_FLAMES)
8920       CheckForDragon(x, y);
8921     else if (element == EL_EXPLOSION)
8922       ; /* drawing of correct explosion animation is handled separately */
8923     else if (element == EL_ELEMENT_SNAPPING ||
8924              element == EL_DIAGONAL_SHRINKING ||
8925              element == EL_DIAGONAL_GROWING)
8926     {
8927 #if 1
8928       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8929
8930       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8931 #endif
8932     }
8933     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8934       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8935
8936 #if 0
8937     if (element == EL_CUSTOM_255 ||
8938         element == EL_CUSTOM_256)
8939       DrawLevelGraphicAnimation(x, y, graphic);
8940 #endif
8941
8942     if (IS_BELT_ACTIVE(element))
8943       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8944
8945     if (game.magic_wall_active)
8946     {
8947       int jx = local_player->jx, jy = local_player->jy;
8948
8949       /* play the element sound at the position nearest to the player */
8950       if ((element == EL_MAGIC_WALL_FULL ||
8951            element == EL_MAGIC_WALL_ACTIVE ||
8952            element == EL_MAGIC_WALL_EMPTYING ||
8953            element == EL_BD_MAGIC_WALL_FULL ||
8954            element == EL_BD_MAGIC_WALL_ACTIVE ||
8955            element == EL_BD_MAGIC_WALL_EMPTYING) &&
8956           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8957       {
8958         magic_wall_x = x;
8959         magic_wall_y = y;
8960       }
8961     }
8962   }
8963
8964 #if USE_NEW_AMOEBA_CODE
8965   /* new experimental amoeba growth stuff */
8966   if (!(FrameCounter % 8))
8967   {
8968     static unsigned long random = 1684108901;
8969
8970     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8971     {
8972       x = RND(lev_fieldx);
8973       y = RND(lev_fieldy);
8974       element = Feld[x][y];
8975
8976       if (!IS_PLAYER(x,y) &&
8977           (element == EL_EMPTY ||
8978            CAN_GROW_INTO(element) ||
8979            element == EL_QUICKSAND_EMPTY ||
8980            element == EL_ACID_SPLASH_LEFT ||
8981            element == EL_ACID_SPLASH_RIGHT))
8982       {
8983         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8984             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8985             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8986             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8987           Feld[x][y] = EL_AMOEBA_DROP;
8988       }
8989
8990       random = random * 129 + 1;
8991     }
8992   }
8993 #endif
8994
8995 #if 0
8996   if (game.explosions_delayed)
8997 #endif
8998   {
8999     game.explosions_delayed = FALSE;
9000
9001 #if 1
9002     SCAN_PLAYFIELD(x, y)
9003 #else
9004     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9005 #endif
9006     {
9007       element = Feld[x][y];
9008
9009       if (ExplodeField[x][y])
9010         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9011       else if (element == EL_EXPLOSION)
9012         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9013
9014       ExplodeField[x][y] = EX_TYPE_NONE;
9015     }
9016
9017     game.explosions_delayed = TRUE;
9018   }
9019
9020   if (game.magic_wall_active)
9021   {
9022     if (!(game.magic_wall_time_left % 4))
9023     {
9024       int element = Feld[magic_wall_x][magic_wall_y];
9025
9026       if (element == EL_BD_MAGIC_WALL_FULL ||
9027           element == EL_BD_MAGIC_WALL_ACTIVE ||
9028           element == EL_BD_MAGIC_WALL_EMPTYING)
9029         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9030       else
9031         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9032     }
9033
9034     if (game.magic_wall_time_left > 0)
9035     {
9036       game.magic_wall_time_left--;
9037       if (!game.magic_wall_time_left)
9038       {
9039 #if 1
9040         SCAN_PLAYFIELD(x, y)
9041 #else
9042         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9043 #endif
9044         {
9045           element = Feld[x][y];
9046
9047           if (element == EL_MAGIC_WALL_ACTIVE ||
9048               element == EL_MAGIC_WALL_FULL)
9049           {
9050             Feld[x][y] = EL_MAGIC_WALL_DEAD;
9051             DrawLevelField(x, y);
9052           }
9053           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9054                    element == EL_BD_MAGIC_WALL_FULL)
9055           {
9056             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9057             DrawLevelField(x, y);
9058           }
9059         }
9060
9061         game.magic_wall_active = FALSE;
9062       }
9063     }
9064   }
9065
9066   if (game.light_time_left > 0)
9067   {
9068     game.light_time_left--;
9069
9070     if (game.light_time_left == 0)
9071       RedrawAllLightSwitchesAndInvisibleElements();
9072   }
9073
9074   if (game.timegate_time_left > 0)
9075   {
9076     game.timegate_time_left--;
9077
9078     if (game.timegate_time_left == 0)
9079       CloseAllOpenTimegates();
9080   }
9081
9082   if (game.lenses_time_left > 0)
9083   {
9084     game.lenses_time_left--;
9085
9086     if (game.lenses_time_left == 0)
9087       RedrawAllInvisibleElementsForLenses();
9088   }
9089
9090   if (game.magnify_time_left > 0)
9091   {
9092     game.magnify_time_left--;
9093
9094     if (game.magnify_time_left == 0)
9095       RedrawAllInvisibleElementsForMagnifier();
9096   }
9097
9098   for (i = 0; i < MAX_PLAYERS; i++)
9099   {
9100     struct PlayerInfo *player = &stored_player[i];
9101
9102     if (SHIELD_ON(player))
9103     {
9104       if (player->shield_deadly_time_left)
9105         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9106       else if (player->shield_normal_time_left)
9107         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9108     }
9109   }
9110
9111   if (TimeFrames >= FRAMES_PER_SECOND)
9112   {
9113     TimeFrames = 0;
9114     TapeTime++;
9115
9116     for (i = 0; i < MAX_PLAYERS; i++)
9117     {
9118       struct PlayerInfo *player = &stored_player[i];
9119
9120       if (SHIELD_ON(player))
9121       {
9122         player->shield_normal_time_left--;
9123
9124         if (player->shield_deadly_time_left > 0)
9125           player->shield_deadly_time_left--;
9126       }
9127     }
9128
9129     if (!level.use_step_counter)
9130     {
9131       TimePlayed++;
9132
9133       if (TimeLeft > 0)
9134       {
9135         TimeLeft--;
9136
9137         if (TimeLeft <= 10 && setup.time_limit)
9138           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9139
9140         DrawGameValue_Time(TimeLeft);
9141
9142         if (!TimeLeft && setup.time_limit)
9143           for (i = 0; i < MAX_PLAYERS; i++)
9144             KillPlayer(&stored_player[i]);
9145       }
9146       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9147         DrawGameValue_Time(TimePlayed);
9148     }
9149
9150     if (tape.recording || tape.playing)
9151       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9152   }
9153
9154   DrawAllPlayers();
9155   PlayAllPlayersSound();
9156
9157   if (options.debug)                    /* calculate frames per second */
9158   {
9159     static unsigned long fps_counter = 0;
9160     static int fps_frames = 0;
9161     unsigned long fps_delay_ms = Counter() - fps_counter;
9162
9163     fps_frames++;
9164
9165     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
9166     {
9167       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9168
9169       fps_frames = 0;
9170       fps_counter = Counter();
9171     }
9172
9173     redraw_mask |= REDRAW_FPS;
9174   }
9175
9176   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9177
9178   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9179   {
9180     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9181
9182     local_player->show_envelope = 0;
9183   }
9184
9185   /* use random number generator in every frame to make it less predictable */
9186   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9187     RND(1);
9188 }
9189
9190 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9191 {
9192   int min_x = x, min_y = y, max_x = x, max_y = y;
9193   int i;
9194
9195   for (i = 0; i < MAX_PLAYERS; i++)
9196   {
9197     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9198
9199     if (!stored_player[i].active || &stored_player[i] == player)
9200       continue;
9201
9202     min_x = MIN(min_x, jx);
9203     min_y = MIN(min_y, jy);
9204     max_x = MAX(max_x, jx);
9205     max_y = MAX(max_y, jy);
9206   }
9207
9208   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9209 }
9210
9211 static boolean AllPlayersInVisibleScreen()
9212 {
9213   int i;
9214
9215   for (i = 0; i < MAX_PLAYERS; i++)
9216   {
9217     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9218
9219     if (!stored_player[i].active)
9220       continue;
9221
9222     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9223       return FALSE;
9224   }
9225
9226   return TRUE;
9227 }
9228
9229 void ScrollLevel(int dx, int dy)
9230 {
9231   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9232   int x, y;
9233
9234   BlitBitmap(drawto_field, drawto_field,
9235              FX + TILEX * (dx == -1) - softscroll_offset,
9236              FY + TILEY * (dy == -1) - softscroll_offset,
9237              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9238              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9239              FX + TILEX * (dx == 1) - softscroll_offset,
9240              FY + TILEY * (dy == 1) - softscroll_offset);
9241
9242   if (dx)
9243   {
9244     x = (dx == 1 ? BX1 : BX2);
9245     for (y = BY1; y <= BY2; y++)
9246       DrawScreenField(x, y);
9247   }
9248
9249   if (dy)
9250   {
9251     y = (dy == 1 ? BY1 : BY2);
9252     for (x = BX1; x <= BX2; x++)
9253       DrawScreenField(x, y);
9254   }
9255
9256   redraw_mask |= REDRAW_FIELD;
9257 }
9258
9259 static boolean canFallDown(struct PlayerInfo *player)
9260 {
9261   int jx = player->jx, jy = player->jy;
9262
9263   return (IN_LEV_FIELD(jx, jy + 1) &&
9264           (IS_FREE(jx, jy + 1) ||
9265            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9266           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9267           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9268 }
9269
9270 static boolean canPassField(int x, int y, int move_dir)
9271 {
9272   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9273   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9274   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9275   int nextx = x + dx;
9276   int nexty = y + dy;
9277   int element = Feld[x][y];
9278
9279   return (IS_PASSABLE_FROM(element, opposite_dir) &&
9280           !CAN_MOVE(element) &&
9281           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9282           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9283           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9284 }
9285
9286 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9287 {
9288   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9289   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9290   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9291   int newx = x + dx;
9292   int newy = y + dy;
9293
9294   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9295           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9296           (IS_DIGGABLE(Feld[newx][newy]) ||
9297            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9298            canPassField(newx, newy, move_dir)));
9299 }
9300
9301 static void CheckGravityMovement(struct PlayerInfo *player)
9302 {
9303   if (game.gravity && !player->programmed_action)
9304   {
9305     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9306     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9307     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9308     int jx = player->jx, jy = player->jy;
9309     boolean player_is_moving_to_valid_field =
9310       (!player_is_snapping &&
9311        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9312         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9313     boolean player_can_fall_down = canFallDown(player);
9314
9315     if (player_can_fall_down &&
9316         !player_is_moving_to_valid_field)
9317       player->programmed_action = MV_DOWN;
9318   }
9319 }
9320
9321 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9322 {
9323   return CheckGravityMovement(player);
9324
9325   if (game.gravity && !player->programmed_action)
9326   {
9327     int jx = player->jx, jy = player->jy;
9328     boolean field_under_player_is_free =
9329       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9330     boolean player_is_standing_on_valid_field =
9331       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9332        (IS_WALKABLE(Feld[jx][jy]) &&
9333         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9334
9335     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9336       player->programmed_action = MV_DOWN;
9337   }
9338 }
9339
9340 /*
9341   MovePlayerOneStep()
9342   -----------------------------------------------------------------------------
9343   dx, dy:               direction (non-diagonal) to try to move the player to
9344   real_dx, real_dy:     direction as read from input device (can be diagonal)
9345 */
9346
9347 boolean MovePlayerOneStep(struct PlayerInfo *player,
9348                           int dx, int dy, int real_dx, int real_dy)
9349 {
9350   int jx = player->jx, jy = player->jy;
9351   int new_jx = jx + dx, new_jy = jy + dy;
9352 #if !USE_FIXED_DONT_RUN_INTO
9353   int element;
9354 #endif
9355   int can_move;
9356   boolean player_can_move = !player->cannot_move;
9357
9358   if (!player->active || (!dx && !dy))
9359     return MP_NO_ACTION;
9360
9361   player->MovDir = (dx < 0 ? MV_LEFT :
9362                     dx > 0 ? MV_RIGHT :
9363                     dy < 0 ? MV_UP :
9364                     dy > 0 ? MV_DOWN :  MV_NONE);
9365
9366   if (!IN_LEV_FIELD(new_jx, new_jy))
9367     return MP_NO_ACTION;
9368
9369   if (!player_can_move)
9370   {
9371 #if 1
9372     if (player->MovPos == 0)
9373     {
9374       player->is_moving = FALSE;
9375       player->is_digging = FALSE;
9376       player->is_collecting = FALSE;
9377       player->is_snapping = FALSE;
9378       player->is_pushing = FALSE;
9379     }
9380 #else
9381     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9382     SnapField(player, 0, 0);
9383 #endif
9384
9385 #if 0
9386     return MP_NO_ACTION;
9387 #endif
9388   }
9389
9390   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9391     return MP_NO_ACTION;
9392
9393 #if !USE_FIXED_DONT_RUN_INTO
9394   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9395
9396   /* (moved to DigField()) */
9397   if (player_can_move && DONT_RUN_INTO(element))
9398   {
9399     if (element == EL_ACID && dx == 0 && dy == 1)
9400     {
9401       SplashAcid(new_jx, new_jy);
9402       Feld[jx][jy] = EL_PLAYER_1;
9403       InitMovingField(jx, jy, MV_DOWN);
9404       Store[jx][jy] = EL_ACID;
9405       ContinueMoving(jx, jy);
9406       BuryPlayer(player);
9407     }
9408     else
9409       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9410
9411     return MP_MOVING;
9412   }
9413 #endif
9414
9415   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9416 #if 0
9417 #if USE_FIXED_DONT_RUN_INTO
9418   if (can_move == MP_DONT_RUN_INTO)
9419     return MP_MOVING;
9420 #endif
9421 #endif
9422   if (can_move != MP_MOVING)
9423     return can_move;
9424
9425 #if USE_FIXED_DONT_RUN_INTO
9426 #endif
9427
9428   /* check if DigField() has caused relocation of the player */
9429   if (player->jx != jx || player->jy != jy)
9430     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9431
9432   StorePlayer[jx][jy] = 0;
9433   player->last_jx = jx;
9434   player->last_jy = jy;
9435   player->jx = new_jx;
9436   player->jy = new_jy;
9437   StorePlayer[new_jx][new_jy] = player->element_nr;
9438
9439   if (player->move_delay_value_next != -1)
9440   {
9441     player->move_delay_value = player->move_delay_value_next;
9442     player->move_delay_value_next = -1;
9443   }
9444
9445   player->MovPos =
9446     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9447
9448   player->step_counter++;
9449
9450   PlayerVisit[jx][jy] = FrameCounter;
9451
9452   ScrollPlayer(player, SCROLL_INIT);
9453
9454   return MP_MOVING;
9455 }
9456
9457 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9458 {
9459   int jx = player->jx, jy = player->jy;
9460   int old_jx = jx, old_jy = jy;
9461   int moved = MP_NO_ACTION;
9462
9463   if (!player->active)
9464     return FALSE;
9465
9466   if (!dx && !dy)
9467   {
9468     if (player->MovPos == 0)
9469     {
9470       player->is_moving = FALSE;
9471       player->is_digging = FALSE;
9472       player->is_collecting = FALSE;
9473       player->is_snapping = FALSE;
9474       player->is_pushing = FALSE;
9475     }
9476
9477     return FALSE;
9478   }
9479
9480   if (player->move_delay > 0)
9481     return FALSE;
9482
9483   player->move_delay = -1;              /* set to "uninitialized" value */
9484
9485   /* store if player is automatically moved to next field */
9486   player->is_auto_moving = (player->programmed_action != MV_NONE);
9487
9488   /* remove the last programmed player action */
9489   player->programmed_action = 0;
9490
9491   if (player->MovPos)
9492   {
9493     /* should only happen if pre-1.2 tape recordings are played */
9494     /* this is only for backward compatibility */
9495
9496     int original_move_delay_value = player->move_delay_value;
9497
9498 #if DEBUG
9499     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9500            tape.counter);
9501 #endif
9502
9503     /* scroll remaining steps with finest movement resolution */
9504     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9505
9506     while (player->MovPos)
9507     {
9508       ScrollPlayer(player, SCROLL_GO_ON);
9509       ScrollScreen(NULL, SCROLL_GO_ON);
9510
9511       AdvanceFrameAndPlayerCounters(player->index_nr);
9512
9513       DrawAllPlayers();
9514       BackToFront();
9515     }
9516
9517     player->move_delay_value = original_move_delay_value;
9518   }
9519
9520   if (player->last_move_dir & MV_HORIZONTAL)
9521   {
9522     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9523       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9524   }
9525   else
9526   {
9527     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9528       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9529   }
9530
9531   jx = player->jx;
9532   jy = player->jy;
9533
9534   if (moved & MP_MOVING && !ScreenMovPos &&
9535       (player == local_player || !options.network))
9536   {
9537     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9538     int offset = (setup.scroll_delay ? 3 : 0);
9539
9540     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9541     {
9542       /* actual player has left the screen -- scroll in that direction */
9543       if (jx != old_jx)         /* player has moved horizontally */
9544         scroll_x += (jx - old_jx);
9545       else                      /* player has moved vertically */
9546         scroll_y += (jy - old_jy);
9547     }
9548     else
9549     {
9550       if (jx != old_jx)         /* player has moved horizontally */
9551       {
9552         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
9553             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9554           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9555
9556         /* don't scroll over playfield boundaries */
9557         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9558           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9559
9560         /* don't scroll more than one field at a time */
9561         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9562
9563         /* don't scroll against the player's moving direction */
9564         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
9565             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9566           scroll_x = old_scroll_x;
9567       }
9568       else                      /* player has moved vertically */
9569       {
9570         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
9571             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9572           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9573
9574         /* don't scroll over playfield boundaries */
9575         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9576           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9577
9578         /* don't scroll more than one field at a time */
9579         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9580
9581         /* don't scroll against the player's moving direction */
9582         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
9583             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9584           scroll_y = old_scroll_y;
9585       }
9586     }
9587
9588     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9589     {
9590       if (!options.network && !AllPlayersInVisibleScreen())
9591       {
9592         scroll_x = old_scroll_x;
9593         scroll_y = old_scroll_y;
9594       }
9595       else
9596       {
9597         ScrollScreen(player, SCROLL_INIT);
9598         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9599       }
9600     }
9601   }
9602
9603   player->StepFrame = 0;
9604
9605   if (moved & MP_MOVING)
9606   {
9607     if (old_jx != jx && old_jy == jy)
9608       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9609     else if (old_jx == jx && old_jy != jy)
9610       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9611
9612     DrawLevelField(jx, jy);     /* for "crumbled sand" */
9613
9614     player->last_move_dir = player->MovDir;
9615     player->is_moving = TRUE;
9616     player->is_snapping = FALSE;
9617     player->is_switching = FALSE;
9618     player->is_dropping = FALSE;
9619   }
9620   else
9621   {
9622     CheckGravityMovementWhenNotMoving(player);
9623
9624     player->is_moving = FALSE;
9625
9626     /* at this point, the player is allowed to move, but cannot move right now
9627        (e.g. because of something blocking the way) -- ensure that the player
9628        is also allowed to move in the next frame (in old versions before 3.1.1,
9629        the player was forced to wait again for eight frames before next try) */
9630
9631     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9632       player->move_delay = 0;   /* allow direct movement in the next frame */
9633   }
9634
9635   if (player->move_delay == -1)         /* not yet initialized by DigField() */
9636     player->move_delay = player->move_delay_value;
9637
9638   if (game.engine_version < VERSION_IDENT(3,0,7,0))
9639   {
9640     TestIfPlayerTouchesBadThing(jx, jy);
9641     TestIfPlayerTouchesCustomElement(jx, jy);
9642   }
9643
9644   if (!player->active)
9645     RemovePlayer(player);
9646
9647   return moved;
9648 }
9649
9650 void ScrollPlayer(struct PlayerInfo *player, int mode)
9651 {
9652   int jx = player->jx, jy = player->jy;
9653   int last_jx = player->last_jx, last_jy = player->last_jy;
9654   int move_stepsize = TILEX / player->move_delay_value;
9655
9656 #if USE_NEW_PLAYER_SPEED
9657   if (!player->active)
9658     return;
9659
9660   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
9661     return;
9662 #else
9663   if (!player->active || player->MovPos == 0)
9664     return;
9665 #endif
9666
9667   if (mode == SCROLL_INIT)
9668   {
9669     player->actual_frame_counter = FrameCounter;
9670     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9671
9672     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9673         Feld[last_jx][last_jy] == EL_EMPTY)
9674     {
9675       int last_field_block_delay = 0;   /* start with no blocking at all */
9676       int block_delay_adjustment = player->block_delay_adjustment;
9677
9678       /* if player blocks last field, add delay for exactly one move */
9679       if (player->block_last_field)
9680       {
9681         last_field_block_delay += player->move_delay_value;
9682
9683         /* when blocking enabled, prevent moving up despite gravity */
9684         if (game.gravity && player->MovDir == MV_UP)
9685           block_delay_adjustment = -1;
9686       }
9687
9688       /* add block delay adjustment (also possible when not blocking) */
9689       last_field_block_delay += block_delay_adjustment;
9690
9691       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9692       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9693     }
9694
9695 #if USE_NEW_PLAYER_SPEED
9696     if (player->MovPos != 0)    /* player has not yet reached destination */
9697       return;
9698 #else
9699     return;
9700 #endif
9701   }
9702   else if (!FrameReached(&player->actual_frame_counter, 1))
9703     return;
9704
9705 #if 0
9706   printf("::: player->MovPos: %d -> %d\n",
9707          player->MovPos,
9708          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9709 #endif
9710
9711 #if USE_NEW_PLAYER_SPEED
9712   if (player->MovPos != 0)
9713   {
9714     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9715     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9716
9717     /* before DrawPlayer() to draw correct player graphic for this case */
9718     if (player->MovPos == 0)
9719       CheckGravityMovement(player);
9720   }
9721 #else
9722   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9723   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9724
9725   /* before DrawPlayer() to draw correct player graphic for this case */
9726   if (player->MovPos == 0)
9727     CheckGravityMovement(player);
9728 #endif
9729
9730   if (player->MovPos == 0)      /* player reached destination field */
9731   {
9732 #if 0
9733     printf("::: player reached destination field\n");
9734 #endif
9735
9736     if (player->move_delay_reset_counter > 0)
9737     {
9738       player->move_delay_reset_counter--;
9739
9740       if (player->move_delay_reset_counter == 0)
9741       {
9742         /* continue with normal speed after quickly moving through gate */
9743         HALVE_PLAYER_SPEED(player);
9744
9745         /* be able to make the next move without delay */
9746         player->move_delay = 0;
9747       }
9748     }
9749
9750     player->last_jx = jx;
9751     player->last_jy = jy;
9752
9753     if (Feld[jx][jy] == EL_EXIT_OPEN ||
9754         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9755         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
9756     {
9757       DrawPlayer(player);       /* needed here only to cleanup last field */
9758       RemovePlayer(player);
9759
9760       if (local_player->friends_still_needed == 0 ||
9761           IS_SP_ELEMENT(Feld[jx][jy]))
9762         player->LevelSolved = player->GameOver = TRUE;
9763     }
9764
9765     /* this breaks one level: "machine", level 000 */
9766     {
9767       int move_direction = player->MovDir;
9768       int enter_side = MV_DIR_OPPOSITE(move_direction);
9769       int leave_side = move_direction;
9770       int old_jx = last_jx;
9771       int old_jy = last_jy;
9772       int old_element = Feld[old_jx][old_jy];
9773       int new_element = Feld[jx][jy];
9774
9775       if (IS_CUSTOM_ELEMENT(old_element))
9776         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9777                                    CE_LEFT_BY_PLAYER,
9778                                    player->index_bit, leave_side);
9779
9780       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9781                                           CE_PLAYER_LEAVES_X,
9782                                           player->index_bit, leave_side);
9783
9784       if (IS_CUSTOM_ELEMENT(new_element))
9785         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9786                                    player->index_bit, enter_side);
9787
9788       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9789                                           CE_PLAYER_ENTERS_X,
9790                                           player->index_bit, enter_side);
9791
9792       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9793                                         CE_MOVE_OF_X, move_direction);
9794     }
9795
9796     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9797     {
9798       TestIfPlayerTouchesBadThing(jx, jy);
9799       TestIfPlayerTouchesCustomElement(jx, jy);
9800
9801       /* needed because pushed element has not yet reached its destination,
9802          so it would trigger a change event at its previous field location */
9803       if (!player->is_pushing)
9804         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
9805
9806       if (!player->active)
9807         RemovePlayer(player);
9808     }
9809
9810     if (level.use_step_counter)
9811     {
9812       int i;
9813
9814       TimePlayed++;
9815
9816       if (TimeLeft > 0)
9817       {
9818         TimeLeft--;
9819
9820         if (TimeLeft <= 10 && setup.time_limit)
9821           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9822
9823         DrawGameValue_Time(TimeLeft);
9824
9825         if (!TimeLeft && setup.time_limit)
9826           for (i = 0; i < MAX_PLAYERS; i++)
9827             KillPlayer(&stored_player[i]);
9828       }
9829       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9830         DrawGameValue_Time(TimePlayed);
9831     }
9832
9833     if (tape.single_step && tape.recording && !tape.pausing &&
9834         !player->programmed_action)
9835       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9836   }
9837 }
9838
9839 void ScrollScreen(struct PlayerInfo *player, int mode)
9840 {
9841   static unsigned long screen_frame_counter = 0;
9842
9843   if (mode == SCROLL_INIT)
9844   {
9845     /* set scrolling step size according to actual player's moving speed */
9846     ScrollStepSize = TILEX / player->move_delay_value;
9847
9848     screen_frame_counter = FrameCounter;
9849     ScreenMovDir = player->MovDir;
9850     ScreenMovPos = player->MovPos;
9851     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9852     return;
9853   }
9854   else if (!FrameReached(&screen_frame_counter, 1))
9855     return;
9856
9857   if (ScreenMovPos)
9858   {
9859     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9860     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9861     redraw_mask |= REDRAW_FIELD;
9862   }
9863   else
9864     ScreenMovDir = MV_NONE;
9865 }
9866
9867 void TestIfPlayerTouchesCustomElement(int x, int y)
9868 {
9869   static int xy[4][2] =
9870   {
9871     { 0, -1 },
9872     { -1, 0 },
9873     { +1, 0 },
9874     { 0, +1 }
9875   };
9876   static int trigger_sides[4][2] =
9877   {
9878     /* center side       border side */
9879     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9880     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9881     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9882     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9883   };
9884   static int touch_dir[4] =
9885   {
9886     MV_LEFT | MV_RIGHT,
9887     MV_UP   | MV_DOWN,
9888     MV_UP   | MV_DOWN,
9889     MV_LEFT | MV_RIGHT
9890   };
9891   int center_element = Feld[x][y];      /* should always be non-moving! */
9892   int i;
9893
9894   for (i = 0; i < NUM_DIRECTIONS; i++)
9895   {
9896     int xx = x + xy[i][0];
9897     int yy = y + xy[i][1];
9898     int center_side = trigger_sides[i][0];
9899     int border_side = trigger_sides[i][1];
9900     int border_element;
9901
9902     if (!IN_LEV_FIELD(xx, yy))
9903       continue;
9904
9905     if (IS_PLAYER(x, y))
9906     {
9907       struct PlayerInfo *player = PLAYERINFO(x, y);
9908
9909       if (game.engine_version < VERSION_IDENT(3,0,7,0))
9910         border_element = Feld[xx][yy];          /* may be moving! */
9911       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9912         border_element = Feld[xx][yy];
9913       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
9914         border_element = MovingOrBlocked2Element(xx, yy);
9915       else
9916         continue;               /* center and border element do not touch */
9917
9918       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9919                                  player->index_bit, border_side);
9920       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9921                                           CE_PLAYER_TOUCHES_X,
9922                                           player->index_bit, border_side);
9923     }
9924     else if (IS_PLAYER(xx, yy))
9925     {
9926       struct PlayerInfo *player = PLAYERINFO(xx, yy);
9927
9928       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9929       {
9930         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9931           continue;             /* center and border element do not touch */
9932       }
9933
9934       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9935                                  player->index_bit, center_side);
9936       CheckTriggeredElementChangeByPlayer(x, y, center_element,
9937                                           CE_PLAYER_TOUCHES_X,
9938                                           player->index_bit, center_side);
9939       break;
9940     }
9941   }
9942 }
9943
9944 void TestIfElementTouchesCustomElement(int x, int y)
9945 {
9946   static int xy[4][2] =
9947   {
9948     { 0, -1 },
9949     { -1, 0 },
9950     { +1, 0 },
9951     { 0, +1 }
9952   };
9953   static int trigger_sides[4][2] =
9954   {
9955     /* center side      border side */
9956     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9957     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9958     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9959     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9960   };
9961   static int touch_dir[4] =
9962   {
9963     MV_LEFT | MV_RIGHT,
9964     MV_UP   | MV_DOWN,
9965     MV_UP   | MV_DOWN,
9966     MV_LEFT | MV_RIGHT
9967   };
9968   boolean change_center_element = FALSE;
9969   int center_element = Feld[x][y];      /* should always be non-moving! */
9970   int i;
9971
9972   for (i = 0; i < NUM_DIRECTIONS; i++)
9973   {
9974     int xx = x + xy[i][0];
9975     int yy = y + xy[i][1];
9976     int center_side = trigger_sides[i][0];
9977     int border_side = trigger_sides[i][1];
9978     int border_element;
9979
9980     if (!IN_LEV_FIELD(xx, yy))
9981       continue;
9982
9983     if (game.engine_version < VERSION_IDENT(3,0,7,0))
9984       border_element = Feld[xx][yy];    /* may be moving! */
9985     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9986       border_element = Feld[xx][yy];
9987     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
9988       border_element = MovingOrBlocked2Element(xx, yy);
9989     else
9990       continue;                 /* center and border element do not touch */
9991
9992     /* check for change of center element (but change it only once) */
9993     if (!change_center_element)
9994       change_center_element =
9995         CheckElementChangeBySide(x, y, center_element, border_element,
9996                                  CE_TOUCHING_X, border_side);
9997
9998     /* check for change of border element */
9999     CheckElementChangeBySide(xx, yy, border_element, center_element,
10000                              CE_TOUCHING_X, center_side);
10001   }
10002 }
10003
10004 void TestIfElementHitsCustomElement(int x, int y, int direction)
10005 {
10006   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10007   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10008   int hitx = x + dx, hity = y + dy;
10009   int hitting_element = Feld[x][y];
10010   int touched_element;
10011
10012   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10013     return;
10014
10015   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10016                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10017
10018   if (IN_LEV_FIELD(hitx, hity))
10019   {
10020     int opposite_direction = MV_DIR_OPPOSITE(direction);
10021     int hitting_side = direction;
10022     int touched_side = opposite_direction;
10023     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10024                           MovDir[hitx][hity] != direction ||
10025                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10026
10027     object_hit = TRUE;
10028
10029     if (object_hit)
10030     {
10031       CheckElementChangeBySide(x, y, hitting_element, touched_element,
10032                                CE_HITTING_X, touched_side);
10033
10034       CheckElementChangeBySide(hitx, hity, touched_element,
10035                                hitting_element, CE_HIT_BY_X, hitting_side);
10036
10037       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10038                                CE_HIT_BY_SOMETHING, opposite_direction);
10039     }
10040   }
10041
10042   /* "hitting something" is also true when hitting the playfield border */
10043   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10044                            CE_HITTING_SOMETHING, direction);
10045 }
10046
10047 #if 0
10048 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10049 {
10050   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10051   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10052   int hitx = x + dx, hity = y + dy;
10053   int hitting_element = Feld[x][y];
10054   int touched_element;
10055 #if 0
10056   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10057                         !IS_FREE(hitx, hity) &&
10058                         (!IS_MOVING(hitx, hity) ||
10059                          MovDir[hitx][hity] != direction ||
10060                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10061 #endif
10062
10063   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10064     return;
10065
10066 #if 0
10067   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10068     return;
10069 #endif
10070
10071   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10072                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10073
10074   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10075                            EP_CAN_SMASH_EVERYTHING, direction);
10076
10077   if (IN_LEV_FIELD(hitx, hity))
10078   {
10079     int opposite_direction = MV_DIR_OPPOSITE(direction);
10080     int hitting_side = direction;
10081     int touched_side = opposite_direction;
10082 #if 0
10083     int touched_element = MovingOrBlocked2Element(hitx, hity);
10084 #endif
10085 #if 1
10086     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10087                           MovDir[hitx][hity] != direction ||
10088                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10089
10090     object_hit = TRUE;
10091 #endif
10092
10093     if (object_hit)
10094     {
10095       int i;
10096
10097       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10098                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10099
10100       CheckElementChangeBySide(x, y, hitting_element, touched_element,
10101                                CE_OTHER_IS_SMASHING, touched_side);
10102
10103       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10104                                CE_OTHER_GETS_SMASHED, hitting_side);
10105     }
10106   }
10107 }
10108 #endif
10109
10110 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10111 {
10112   int i, kill_x = -1, kill_y = -1;
10113
10114   int bad_element = -1;
10115   static int test_xy[4][2] =
10116   {
10117     { 0, -1 },
10118     { -1, 0 },
10119     { +1, 0 },
10120     { 0, +1 }
10121   };
10122   static int test_dir[4] =
10123   {
10124     MV_UP,
10125     MV_LEFT,
10126     MV_RIGHT,
10127     MV_DOWN
10128   };
10129
10130   for (i = 0; i < NUM_DIRECTIONS; i++)
10131   {
10132     int test_x, test_y, test_move_dir, test_element;
10133
10134     test_x = good_x + test_xy[i][0];
10135     test_y = good_y + test_xy[i][1];
10136
10137     if (!IN_LEV_FIELD(test_x, test_y))
10138       continue;
10139
10140     test_move_dir =
10141       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10142
10143     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10144
10145     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10146        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10147     */
10148     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10149         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
10150     {
10151       kill_x = test_x;
10152       kill_y = test_y;
10153       bad_element = test_element;
10154
10155       break;
10156     }
10157   }
10158
10159   if (kill_x != -1 || kill_y != -1)
10160   {
10161     if (IS_PLAYER(good_x, good_y))
10162     {
10163       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10164
10165       if (player->shield_deadly_time_left > 0 &&
10166           !IS_INDESTRUCTIBLE(bad_element))
10167         Bang(kill_x, kill_y);
10168       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10169         KillPlayer(player);
10170     }
10171     else
10172       Bang(good_x, good_y);
10173   }
10174 }
10175
10176 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10177 {
10178   int i, kill_x = -1, kill_y = -1;
10179   int bad_element = Feld[bad_x][bad_y];
10180   static int test_xy[4][2] =
10181   {
10182     { 0, -1 },
10183     { -1, 0 },
10184     { +1, 0 },
10185     { 0, +1 }
10186   };
10187   static int touch_dir[4] =
10188   {
10189     MV_LEFT | MV_RIGHT,
10190     MV_UP   | MV_DOWN,
10191     MV_UP   | MV_DOWN,
10192     MV_LEFT | MV_RIGHT
10193   };
10194   static int test_dir[4] =
10195   {
10196     MV_UP,
10197     MV_LEFT,
10198     MV_RIGHT,
10199     MV_DOWN
10200   };
10201
10202   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
10203     return;
10204
10205   for (i = 0; i < NUM_DIRECTIONS; i++)
10206   {
10207     int test_x, test_y, test_move_dir, test_element;
10208
10209     test_x = bad_x + test_xy[i][0];
10210     test_y = bad_y + test_xy[i][1];
10211     if (!IN_LEV_FIELD(test_x, test_y))
10212       continue;
10213
10214     test_move_dir =
10215       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10216
10217     test_element = Feld[test_x][test_y];
10218
10219     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10220        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10221     */
10222     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
10223         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
10224     {
10225       /* good thing is player or penguin that does not move away */
10226       if (IS_PLAYER(test_x, test_y))
10227       {
10228         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10229
10230         if (bad_element == EL_ROBOT && player->is_moving)
10231           continue;     /* robot does not kill player if he is moving */
10232
10233         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10234         {
10235           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10236             continue;           /* center and border element do not touch */
10237         }
10238
10239         kill_x = test_x;
10240         kill_y = test_y;
10241         break;
10242       }
10243       else if (test_element == EL_PENGUIN)
10244       {
10245         kill_x = test_x;
10246         kill_y = test_y;
10247         break;
10248       }
10249     }
10250   }
10251
10252   if (kill_x != -1 || kill_y != -1)
10253   {
10254     if (IS_PLAYER(kill_x, kill_y))
10255     {
10256       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10257
10258       if (player->shield_deadly_time_left > 0 &&
10259           !IS_INDESTRUCTIBLE(bad_element))
10260         Bang(bad_x, bad_y);
10261       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10262         KillPlayer(player);
10263     }
10264     else
10265       Bang(kill_x, kill_y);
10266   }
10267 }
10268
10269 void TestIfPlayerTouchesBadThing(int x, int y)
10270 {
10271   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10272 }
10273
10274 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10275 {
10276   TestIfGoodThingHitsBadThing(x, y, move_dir);
10277 }
10278
10279 void TestIfBadThingTouchesPlayer(int x, int y)
10280 {
10281   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10282 }
10283
10284 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10285 {
10286   TestIfBadThingHitsGoodThing(x, y, move_dir);
10287 }
10288
10289 void TestIfFriendTouchesBadThing(int x, int y)
10290 {
10291   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10292 }
10293
10294 void TestIfBadThingTouchesFriend(int x, int y)
10295 {
10296   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10297 }
10298
10299 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10300 {
10301   int i, kill_x = bad_x, kill_y = bad_y;
10302   static int xy[4][2] =
10303   {
10304     { 0, -1 },
10305     { -1, 0 },
10306     { +1, 0 },
10307     { 0, +1 }
10308   };
10309
10310   for (i = 0; i < NUM_DIRECTIONS; i++)
10311   {
10312     int x, y, element;
10313
10314     x = bad_x + xy[i][0];
10315     y = bad_y + xy[i][1];
10316     if (!IN_LEV_FIELD(x, y))
10317       continue;
10318
10319     element = Feld[x][y];
10320     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10321         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10322     {
10323       kill_x = x;
10324       kill_y = y;
10325       break;
10326     }
10327   }
10328
10329   if (kill_x != bad_x || kill_y != bad_y)
10330     Bang(bad_x, bad_y);
10331 }
10332
10333 void KillPlayer(struct PlayerInfo *player)
10334 {
10335   int jx = player->jx, jy = player->jy;
10336
10337   if (!player->active)
10338     return;
10339
10340   /* remove accessible field at the player's position */
10341   Feld[jx][jy] = EL_EMPTY;
10342
10343   /* deactivate shield (else Bang()/Explode() would not work right) */
10344   player->shield_normal_time_left = 0;
10345   player->shield_deadly_time_left = 0;
10346
10347   Bang(jx, jy);
10348   BuryPlayer(player);
10349 }
10350
10351 static void KillPlayerUnlessEnemyProtected(int x, int y)
10352 {
10353   if (!PLAYER_ENEMY_PROTECTED(x, y))
10354     KillPlayer(PLAYERINFO(x, y));
10355 }
10356
10357 static void KillPlayerUnlessExplosionProtected(int x, int y)
10358 {
10359   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10360     KillPlayer(PLAYERINFO(x, y));
10361 }
10362
10363 void BuryPlayer(struct PlayerInfo *player)
10364 {
10365   int jx = player->jx, jy = player->jy;
10366
10367   if (!player->active)
10368     return;
10369
10370   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10371   PlayLevelSound(jx, jy, SND_GAME_LOSING);
10372
10373   player->GameOver = TRUE;
10374   RemovePlayer(player);
10375 }
10376
10377 void RemovePlayer(struct PlayerInfo *player)
10378 {
10379   int jx = player->jx, jy = player->jy;
10380   int i, found = FALSE;
10381
10382   player->present = FALSE;
10383   player->active = FALSE;
10384
10385   if (!ExplodeField[jx][jy])
10386     StorePlayer[jx][jy] = 0;
10387
10388   if (player->is_moving)
10389     DrawLevelField(player->last_jx, player->last_jy);
10390
10391   for (i = 0; i < MAX_PLAYERS; i++)
10392     if (stored_player[i].active)
10393       found = TRUE;
10394
10395   if (!found)
10396     AllPlayersGone = TRUE;
10397
10398   ExitX = ZX = jx;
10399   ExitY = ZY = jy;
10400 }
10401
10402 #if USE_NEW_SNAP_DELAY
10403 static void setFieldForSnapping(int x, int y, int element, int direction)
10404 {
10405   struct ElementInfo *ei = &element_info[element];
10406   int direction_bit = MV_DIR_TO_BIT(direction);
10407   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10408   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10409                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10410
10411   Feld[x][y] = EL_ELEMENT_SNAPPING;
10412   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10413
10414   ResetGfxAnimation(x, y);
10415
10416   GfxElement[x][y] = element;
10417   GfxAction[x][y] = action;
10418   GfxDir[x][y] = direction;
10419   GfxFrame[x][y] = -1;
10420 }
10421 #endif
10422
10423 /*
10424   =============================================================================
10425   checkDiagonalPushing()
10426   -----------------------------------------------------------------------------
10427   check if diagonal input device direction results in pushing of object
10428   (by checking if the alternative direction is walkable, diggable, ...)
10429   =============================================================================
10430 */
10431
10432 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10433                                     int x, int y, int real_dx, int real_dy)
10434 {
10435   int jx, jy, dx, dy, xx, yy;
10436
10437   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
10438     return TRUE;
10439
10440   /* diagonal direction: check alternative direction */
10441   jx = player->jx;
10442   jy = player->jy;
10443   dx = x - jx;
10444   dy = y - jy;
10445   xx = jx + (dx == 0 ? real_dx : 0);
10446   yy = jy + (dy == 0 ? real_dy : 0);
10447
10448   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10449 }
10450
10451 /*
10452   =============================================================================
10453   DigField()
10454   -----------------------------------------------------------------------------
10455   x, y:                 field next to player (non-diagonal) to try to dig to
10456   real_dx, real_dy:     direction as read from input device (can be diagonal)
10457   =============================================================================
10458 */
10459
10460 int DigField(struct PlayerInfo *player,
10461              int oldx, int oldy, int x, int y,
10462              int real_dx, int real_dy, int mode)
10463 {
10464   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10465   boolean player_was_pushing = player->is_pushing;
10466   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10467   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10468   int jx = oldx, jy = oldy;
10469   int dx = x - jx, dy = y - jy;
10470   int nextx = x + dx, nexty = y + dy;
10471   int move_direction = (dx == -1 ? MV_LEFT  :
10472                         dx == +1 ? MV_RIGHT :
10473                         dy == -1 ? MV_UP    :
10474                         dy == +1 ? MV_DOWN  : MV_NONE);
10475   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10476   int dig_side = MV_DIR_OPPOSITE(move_direction);
10477   int old_element = Feld[jx][jy];
10478 #if USE_FIXED_DONT_RUN_INTO
10479   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10480 #else
10481   int element;
10482 #endif
10483   int collect_count;
10484
10485   if (is_player)                /* function can also be called by EL_PENGUIN */
10486   {
10487     if (player->MovPos == 0)
10488     {
10489       player->is_digging = FALSE;
10490       player->is_collecting = FALSE;
10491     }
10492
10493     if (player->MovPos == 0)    /* last pushing move finished */
10494       player->is_pushing = FALSE;
10495
10496     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
10497     {
10498       player->is_switching = FALSE;
10499       player->push_delay = -1;
10500
10501       return MP_NO_ACTION;
10502     }
10503   }
10504
10505 #if !USE_FIXED_DONT_RUN_INTO
10506   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10507     return MP_NO_ACTION;
10508 #endif
10509
10510   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10511     old_element = Back[jx][jy];
10512
10513   /* in case of element dropped at player position, check background */
10514   else if (Back[jx][jy] != EL_EMPTY &&
10515            game.engine_version >= VERSION_IDENT(2,2,0,0))
10516     old_element = Back[jx][jy];
10517
10518 #if 0
10519 #if USE_FIXED_DONT_RUN_INTO
10520   if (player_can_move && DONT_RUN_INTO(element))
10521   {
10522     if (element == EL_ACID && dx == 0 && dy == 1)
10523     {
10524       SplashAcid(x, y);
10525       Feld[jx][jy] = EL_PLAYER_1;
10526       InitMovingField(jx, jy, MV_DOWN);
10527       Store[jx][jy] = EL_ACID;
10528       ContinueMoving(jx, jy);
10529       BuryPlayer(player);
10530     }
10531     else
10532       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10533
10534     return MP_DONT_RUN_INTO;
10535   }
10536 #endif
10537 #endif
10538
10539 #if 1
10540 #if USE_FIXED_DONT_RUN_INTO
10541   if (player_can_move && DONT_RUN_INTO(element))
10542   {
10543     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10544
10545     return MP_DONT_RUN_INTO;
10546   }
10547 #endif
10548 #endif
10549
10550   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10551     return MP_NO_ACTION;        /* field has no opening in this direction */
10552
10553   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10554     return MP_NO_ACTION;        /* field has no opening in this direction */
10555
10556 #if 1
10557 #if USE_FIXED_DONT_RUN_INTO
10558   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10559   {
10560     SplashAcid(x, y);
10561     Feld[jx][jy] = EL_PLAYER_1;
10562     InitMovingField(jx, jy, MV_DOWN);
10563     Store[jx][jy] = EL_ACID;
10564     ContinueMoving(jx, jy);
10565     BuryPlayer(player);
10566
10567     return MP_DONT_RUN_INTO;
10568   }
10569 #endif
10570 #endif
10571
10572 #if 0
10573 #if USE_FIXED_DONT_RUN_INTO
10574   if (player_can_move && DONT_RUN_INTO(element))
10575   {
10576     if (element == EL_ACID && dx == 0 && dy == 1)
10577     {
10578       SplashAcid(x, y);
10579       Feld[jx][jy] = EL_PLAYER_1;
10580       InitMovingField(jx, jy, MV_DOWN);
10581       Store[jx][jy] = EL_ACID;
10582       ContinueMoving(jx, jy);
10583       BuryPlayer(player);
10584     }
10585     else
10586       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10587
10588     return MP_DONT_RUN_INTO;
10589   }
10590 #endif
10591 #endif
10592
10593 #if USE_FIXED_DONT_RUN_INTO
10594   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10595     return MP_NO_ACTION;
10596 #endif
10597
10598 #if !USE_FIXED_DONT_RUN_INTO
10599   element = Feld[x][y];
10600 #endif
10601
10602   collect_count = element_info[element].collect_count_initial;
10603
10604   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
10605     return MP_NO_ACTION;
10606
10607   if (game.engine_version < VERSION_IDENT(2,2,0,0))
10608     player_can_move = player_can_move_or_snap;
10609
10610   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10611       game.engine_version >= VERSION_IDENT(2,2,0,0))
10612   {
10613     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10614                                player->index_bit, dig_side);
10615     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10616                                         player->index_bit, dig_side);
10617
10618     if (Feld[x][y] != element)          /* field changed by snapping */
10619       return MP_ACTION;
10620
10621     return MP_NO_ACTION;
10622   }
10623
10624   if (game.gravity && is_player && !player->is_auto_moving &&
10625       canFallDown(player) && move_direction != MV_DOWN &&
10626       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10627     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
10628
10629   if (player_can_move &&
10630       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10631   {
10632     int sound_element = SND_ELEMENT(element);
10633     int sound_action = ACTION_WALKING;
10634
10635     if (IS_RND_GATE(element))
10636     {
10637       if (!player->key[RND_GATE_NR(element)])
10638         return MP_NO_ACTION;
10639     }
10640     else if (IS_RND_GATE_GRAY(element))
10641     {
10642       if (!player->key[RND_GATE_GRAY_NR(element)])
10643         return MP_NO_ACTION;
10644     }
10645     else if (IS_RND_GATE_GRAY_ACTIVE(element))
10646     {
10647       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10648         return MP_NO_ACTION;
10649     }
10650     else if (element == EL_EXIT_OPEN ||
10651              element == EL_SP_EXIT_OPEN ||
10652              element == EL_SP_EXIT_OPENING)
10653     {
10654       sound_action = ACTION_PASSING;    /* player is passing exit */
10655     }
10656     else if (element == EL_EMPTY)
10657     {
10658       sound_action = ACTION_MOVING;             /* nothing to walk on */
10659     }
10660
10661     /* play sound from background or player, whatever is available */
10662     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10663       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10664     else
10665       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10666   }
10667   else if (player_can_move &&
10668            IS_PASSABLE(element) && canPassField(x, y, move_direction))
10669   {
10670     if (!ACCESS_FROM(element, opposite_direction))
10671       return MP_NO_ACTION;      /* field not accessible from this direction */
10672
10673     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
10674       return MP_NO_ACTION;
10675
10676     if (IS_EM_GATE(element))
10677     {
10678       if (!player->key[EM_GATE_NR(element)])
10679         return MP_NO_ACTION;
10680     }
10681     else if (IS_EM_GATE_GRAY(element))
10682     {
10683       if (!player->key[EM_GATE_GRAY_NR(element)])
10684         return MP_NO_ACTION;
10685     }
10686     else if (IS_EM_GATE_GRAY_ACTIVE(element))
10687     {
10688       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10689         return MP_NO_ACTION;
10690     }
10691     else if (IS_SP_PORT(element))
10692     {
10693       if (element == EL_SP_GRAVITY_PORT_LEFT ||
10694           element == EL_SP_GRAVITY_PORT_RIGHT ||
10695           element == EL_SP_GRAVITY_PORT_UP ||
10696           element == EL_SP_GRAVITY_PORT_DOWN)
10697         game.gravity = !game.gravity;
10698       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10699                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10700                element == EL_SP_GRAVITY_ON_PORT_UP ||
10701                element == EL_SP_GRAVITY_ON_PORT_DOWN)
10702         game.gravity = TRUE;
10703       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10704                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10705                element == EL_SP_GRAVITY_OFF_PORT_UP ||
10706                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10707         game.gravity = FALSE;
10708     }
10709
10710     /* automatically move to the next field with double speed */
10711     player->programmed_action = move_direction;
10712
10713     if (player->move_delay_reset_counter == 0)
10714     {
10715       player->move_delay_reset_counter = 2;     /* two double speed steps */
10716
10717       DOUBLE_PLAYER_SPEED(player);
10718     }
10719
10720     PlayLevelSoundAction(x, y, ACTION_PASSING);
10721   }
10722   else if (player_can_move_or_snap && IS_DIGGABLE(element))
10723   {
10724     RemoveField(x, y);
10725
10726     if (mode != DF_SNAP)
10727     {
10728       GfxElement[x][y] = GFX_ELEMENT(element);
10729       player->is_digging = TRUE;
10730     }
10731
10732     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10733
10734     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10735                                         player->index_bit, dig_side);
10736
10737     if (mode == DF_SNAP)
10738     {
10739 #if USE_NEW_SNAP_DELAY
10740       if (level.block_snap_field)
10741         setFieldForSnapping(x, y, element, move_direction);
10742       else
10743         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10744 #else
10745       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
10746 #endif
10747
10748       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10749                                           player->index_bit, dig_side);
10750     }
10751   }
10752   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
10753   {
10754     RemoveField(x, y);
10755
10756     if (is_player && mode != DF_SNAP)
10757     {
10758       GfxElement[x][y] = element;
10759       player->is_collecting = TRUE;
10760     }
10761
10762     if (element == EL_SPEED_PILL)
10763     {
10764       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10765     }
10766     else if (element == EL_EXTRA_TIME && level.time > 0)
10767     {
10768       TimeLeft += level.extra_time;
10769       DrawGameValue_Time(TimeLeft);
10770     }
10771     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10772     {
10773       player->shield_normal_time_left += level.shield_normal_time;
10774       if (element == EL_SHIELD_DEADLY)
10775         player->shield_deadly_time_left += level.shield_deadly_time;
10776     }
10777     else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10778     {
10779       if (player->inventory_size < MAX_INVENTORY_SIZE)
10780         player->inventory_element[player->inventory_size++] = element;
10781
10782       DrawGameValue_Dynamite(local_player->inventory_size);
10783     }
10784     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10785     {
10786       player->dynabomb_count++;
10787       player->dynabombs_left++;
10788     }
10789     else if (element == EL_DYNABOMB_INCREASE_SIZE)
10790     {
10791       player->dynabomb_size++;
10792     }
10793     else if (element == EL_DYNABOMB_INCREASE_POWER)
10794     {
10795       player->dynabomb_xl = TRUE;
10796     }
10797     else if (IS_KEY(element))
10798     {
10799       player->key[KEY_NR(element)] = TRUE;
10800
10801       DrawGameValue_Keys(player->key);
10802
10803       redraw_mask |= REDRAW_DOOR_1;
10804     }
10805     else if (IS_ENVELOPE(element))
10806     {
10807       player->show_envelope = element;
10808     }
10809     else if (element == EL_EMC_LENSES)
10810     {
10811       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10812
10813       RedrawAllInvisibleElementsForLenses();
10814     }
10815     else if (element == EL_EMC_MAGNIFIER)
10816     {
10817       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10818
10819       RedrawAllInvisibleElementsForMagnifier();
10820     }
10821     else if (IS_DROPPABLE(element) ||
10822              IS_THROWABLE(element))     /* can be collected and dropped */
10823     {
10824       int i;
10825
10826       if (collect_count == 0)
10827         player->inventory_infinite_element = element;
10828       else
10829         for (i = 0; i < collect_count; i++)
10830           if (player->inventory_size < MAX_INVENTORY_SIZE)
10831             player->inventory_element[player->inventory_size++] = element;
10832
10833       DrawGameValue_Dynamite(local_player->inventory_size);
10834     }
10835     else if (collect_count > 0)
10836     {
10837       local_player->gems_still_needed -= collect_count;
10838       if (local_player->gems_still_needed < 0)
10839         local_player->gems_still_needed = 0;
10840
10841       DrawGameValue_Emeralds(local_player->gems_still_needed);
10842     }
10843
10844     RaiseScoreElement(element);
10845     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10846
10847     if (is_player)
10848       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10849                                           player->index_bit, dig_side);
10850
10851     if (mode == DF_SNAP)
10852     {
10853 #if USE_NEW_SNAP_DELAY
10854       if (level.block_snap_field)
10855         setFieldForSnapping(x, y, element, move_direction);
10856       else
10857         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10858 #else
10859       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
10860 #endif
10861
10862       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10863                                           player->index_bit, dig_side);
10864     }
10865   }
10866   else if (player_can_move_or_snap && IS_PUSHABLE(element))
10867   {
10868     if (mode == DF_SNAP && element != EL_BD_ROCK)
10869       return MP_NO_ACTION;
10870
10871     if (CAN_FALL(element) && dy)
10872       return MP_NO_ACTION;
10873
10874     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10875         !(element == EL_SPRING && level.use_spring_bug))
10876       return MP_NO_ACTION;
10877
10878     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10879         ((move_direction & MV_VERTICAL &&
10880           ((element_info[element].move_pattern & MV_LEFT &&
10881             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10882            (element_info[element].move_pattern & MV_RIGHT &&
10883             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10884          (move_direction & MV_HORIZONTAL &&
10885           ((element_info[element].move_pattern & MV_UP &&
10886             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10887            (element_info[element].move_pattern & MV_DOWN &&
10888             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10889       return MP_NO_ACTION;
10890
10891     /* do not push elements already moving away faster than player */
10892     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10893         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10894       return MP_NO_ACTION;
10895
10896     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10897     {
10898       if (player->push_delay_value == -1 || !player_was_pushing)
10899         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10900     }
10901     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10902     {
10903       if (player->push_delay_value == -1)
10904         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10905     }
10906     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10907     {
10908       if (!player->is_pushing)
10909         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10910     }
10911
10912     player->is_pushing = TRUE;
10913
10914     if (!(IN_LEV_FIELD(nextx, nexty) &&
10915           (IS_FREE(nextx, nexty) ||
10916            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10917             IS_SB_ELEMENT(element)))))
10918       return MP_NO_ACTION;
10919
10920     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10921       return MP_NO_ACTION;
10922
10923     if (player->push_delay == -1)       /* new pushing; restart delay */
10924       player->push_delay = 0;
10925
10926     if (player->push_delay < player->push_delay_value &&
10927         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10928         element != EL_SPRING && element != EL_BALLOON)
10929     {
10930       /* make sure that there is no move delay before next try to push */
10931       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10932         player->move_delay = 0;
10933
10934       return MP_NO_ACTION;
10935     }
10936
10937     if (IS_SB_ELEMENT(element))
10938     {
10939       if (element == EL_SOKOBAN_FIELD_FULL)
10940       {
10941         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10942         local_player->sokobanfields_still_needed++;
10943       }
10944
10945       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10946       {
10947         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10948         local_player->sokobanfields_still_needed--;
10949       }
10950
10951       Feld[x][y] = EL_SOKOBAN_OBJECT;
10952
10953       if (Back[x][y] == Back[nextx][nexty])
10954         PlayLevelSoundAction(x, y, ACTION_PUSHING);
10955       else if (Back[x][y] != 0)
10956         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10957                                     ACTION_EMPTYING);
10958       else
10959         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10960                                     ACTION_FILLING);
10961
10962       if (local_player->sokobanfields_still_needed == 0 &&
10963           game.emulation == EMU_SOKOBAN)
10964       {
10965         player->LevelSolved = player->GameOver = TRUE;
10966         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10967       }
10968     }
10969     else
10970       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10971
10972     InitMovingField(x, y, move_direction);
10973     GfxAction[x][y] = ACTION_PUSHING;
10974
10975     if (mode == DF_SNAP)
10976       ContinueMoving(x, y);
10977     else
10978       MovPos[x][y] = (dx != 0 ? dx : dy);
10979
10980     Pushed[x][y] = TRUE;
10981     Pushed[nextx][nexty] = TRUE;
10982
10983     if (game.engine_version < VERSION_IDENT(2,2,0,7))
10984       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10985     else
10986       player->push_delay_value = -1;    /* get new value later */
10987
10988     /* check for element change _after_ element has been pushed */
10989     if (game.use_change_when_pushing_bug)
10990     {
10991       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10992                                  player->index_bit, dig_side);
10993       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10994                                           player->index_bit, dig_side);
10995     }
10996   }
10997   else if (IS_SWITCHABLE(element))
10998   {
10999     if (PLAYER_SWITCHING(player, x, y))
11000     {
11001       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11002                                           player->index_bit, dig_side);
11003
11004       return MP_ACTION;
11005     }
11006
11007     player->is_switching = TRUE;
11008     player->switch_x = x;
11009     player->switch_y = y;
11010
11011     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11012
11013     if (element == EL_ROBOT_WHEEL)
11014     {
11015       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11016       ZX = x;
11017       ZY = y;
11018
11019       DrawLevelField(x, y);
11020     }
11021     else if (element == EL_SP_TERMINAL)
11022     {
11023       int xx, yy;
11024
11025 #if 1
11026       SCAN_PLAYFIELD(xx, yy)
11027 #else
11028       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11029 #endif
11030       {
11031         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11032           Bang(xx, yy);
11033         else if (Feld[xx][yy] == EL_SP_TERMINAL)
11034           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11035       }
11036     }
11037     else if (IS_BELT_SWITCH(element))
11038     {
11039       ToggleBeltSwitch(x, y);
11040     }
11041     else if (element == EL_SWITCHGATE_SWITCH_UP ||
11042              element == EL_SWITCHGATE_SWITCH_DOWN)
11043     {
11044       ToggleSwitchgateSwitch(x, y);
11045     }
11046     else if (element == EL_LIGHT_SWITCH ||
11047              element == EL_LIGHT_SWITCH_ACTIVE)
11048     {
11049       ToggleLightSwitch(x, y);
11050     }
11051     else if (element == EL_TIMEGATE_SWITCH)
11052     {
11053       ActivateTimegateSwitch(x, y);
11054     }
11055     else if (element == EL_BALLOON_SWITCH_LEFT  ||
11056              element == EL_BALLOON_SWITCH_RIGHT ||
11057              element == EL_BALLOON_SWITCH_UP    ||
11058              element == EL_BALLOON_SWITCH_DOWN  ||
11059              element == EL_BALLOON_SWITCH_NONE  ||
11060              element == EL_BALLOON_SWITCH_ANY)
11061     {
11062       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
11063                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11064                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
11065                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
11066                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
11067                              move_direction);
11068     }
11069     else if (element == EL_LAMP)
11070     {
11071       Feld[x][y] = EL_LAMP_ACTIVE;
11072       local_player->lights_still_needed--;
11073
11074       ResetGfxAnimation(x, y);
11075       DrawLevelField(x, y);
11076     }
11077     else if (element == EL_TIME_ORB_FULL)
11078     {
11079       Feld[x][y] = EL_TIME_ORB_EMPTY;
11080
11081       if (level.time > 0 || level.use_time_orb_bug)
11082       {
11083         TimeLeft += level.time_orb_time;
11084         DrawGameValue_Time(TimeLeft);
11085       }
11086
11087       ResetGfxAnimation(x, y);
11088       DrawLevelField(x, y);
11089     }
11090     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11091              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11092     {
11093       int xx, yy;
11094
11095       game.ball_state = !game.ball_state;
11096
11097 #if 1
11098       SCAN_PLAYFIELD(xx, yy)
11099 #else
11100       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11101 #endif
11102       {
11103         int e = Feld[xx][yy];
11104
11105         if (game.ball_state)
11106         {
11107           if (e == EL_EMC_MAGIC_BALL)
11108             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11109           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11110             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11111         }
11112         else
11113         {
11114           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11115             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11116           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11117             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11118         }
11119       }
11120     }
11121
11122     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11123                                         player->index_bit, dig_side);
11124
11125     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11126                                         player->index_bit, dig_side);
11127
11128     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11129                                         player->index_bit, dig_side);
11130
11131     return MP_ACTION;
11132   }
11133   else
11134   {
11135     if (!PLAYER_SWITCHING(player, x, y))
11136     {
11137       player->is_switching = TRUE;
11138       player->switch_x = x;
11139       player->switch_y = y;
11140
11141       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11142                                  player->index_bit, dig_side);
11143       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11144                                           player->index_bit, dig_side);
11145
11146       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11147                                  player->index_bit, dig_side);
11148       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11149                                           player->index_bit, dig_side);
11150     }
11151
11152     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11153                                player->index_bit, dig_side);
11154     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11155                                         player->index_bit, dig_side);
11156
11157     return MP_NO_ACTION;
11158   }
11159
11160   player->push_delay = -1;
11161
11162   if (is_player)                /* function can also be called by EL_PENGUIN */
11163   {
11164     if (Feld[x][y] != element)          /* really digged/collected something */
11165       player->is_collecting = !player->is_digging;
11166   }
11167
11168   return MP_MOVING;
11169 }
11170
11171 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11172 {
11173   int jx = player->jx, jy = player->jy;
11174   int x = jx + dx, y = jy + dy;
11175   int snap_direction = (dx == -1 ? MV_LEFT  :
11176                         dx == +1 ? MV_RIGHT :
11177                         dy == -1 ? MV_UP    :
11178                         dy == +1 ? MV_DOWN  : MV_NONE);
11179
11180   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11181     return FALSE;
11182
11183   if (!player->active || !IN_LEV_FIELD(x, y))
11184     return FALSE;
11185
11186   if (dx && dy)
11187     return FALSE;
11188
11189   if (!dx && !dy)
11190   {
11191     if (player->MovPos == 0)
11192       player->is_pushing = FALSE;
11193
11194     player->is_snapping = FALSE;
11195
11196     if (player->MovPos == 0)
11197     {
11198       player->is_moving = FALSE;
11199       player->is_digging = FALSE;
11200       player->is_collecting = FALSE;
11201     }
11202
11203     return FALSE;
11204   }
11205
11206   if (player->is_snapping)
11207     return FALSE;
11208
11209   player->MovDir = snap_direction;
11210
11211   if (player->MovPos == 0)
11212   {
11213     player->is_moving = FALSE;
11214     player->is_digging = FALSE;
11215     player->is_collecting = FALSE;
11216   }
11217
11218   player->is_dropping = FALSE;
11219
11220   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11221     return FALSE;
11222
11223   player->is_snapping = TRUE;
11224
11225   if (player->MovPos == 0)
11226   {
11227     player->is_moving = FALSE;
11228     player->is_digging = FALSE;
11229     player->is_collecting = FALSE;
11230   }
11231
11232   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
11233     DrawLevelField(player->last_jx, player->last_jy);
11234
11235   DrawLevelField(x, y);
11236
11237   return TRUE;
11238 }
11239
11240 boolean DropElement(struct PlayerInfo *player)
11241 {
11242   int old_element, new_element;
11243   int dropx = player->jx, dropy = player->jy;
11244   int drop_direction = player->MovDir;
11245   int drop_side = drop_direction;
11246   int drop_element = (player->inventory_size > 0 ?
11247                       player->inventory_element[player->inventory_size - 1] :
11248                       player->inventory_infinite_element != EL_UNDEFINED ?
11249                       player->inventory_infinite_element :
11250                       player->dynabombs_left > 0 ?
11251                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11252                       EL_UNDEFINED);
11253
11254   /* do not drop an element on top of another element; when holding drop key
11255      pressed without moving, dropped element must move away before the next
11256      element can be dropped (this is especially important if the next element
11257      is dynamite, which can be placed on background for historical reasons) */
11258   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11259     return MP_ACTION;
11260
11261   if (IS_THROWABLE(drop_element))
11262   {
11263     dropx += GET_DX_FROM_DIR(drop_direction);
11264     dropy += GET_DY_FROM_DIR(drop_direction);
11265
11266     if (!IN_LEV_FIELD(dropx, dropy))
11267       return FALSE;
11268   }
11269
11270   old_element = Feld[dropx][dropy];     /* old element at dropping position */
11271   new_element = drop_element;           /* default: no change when dropping */
11272
11273   /* check if player is active, not moving and ready to drop */
11274   if (!player->active || player->MovPos || player->drop_delay > 0)
11275     return FALSE;
11276
11277   /* check if player has anything that can be dropped */
11278   if (new_element == EL_UNDEFINED)
11279     return FALSE;
11280
11281   /* check if anything can be dropped at the current position */
11282   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11283     return FALSE;
11284
11285   /* collected custom elements can only be dropped on empty fields */
11286   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11287     return FALSE;
11288
11289   if (old_element != EL_EMPTY)
11290     Back[dropx][dropy] = old_element;   /* store old element on this field */
11291
11292   ResetGfxAnimation(dropx, dropy);
11293   ResetRandomAnimationValue(dropx, dropy);
11294
11295   if (player->inventory_size > 0 ||
11296       player->inventory_infinite_element != EL_UNDEFINED)
11297   {
11298     if (player->inventory_size > 0)
11299     {
11300       player->inventory_size--;
11301
11302       DrawGameValue_Dynamite(local_player->inventory_size);
11303
11304       if (new_element == EL_DYNAMITE)
11305         new_element = EL_DYNAMITE_ACTIVE;
11306       else if (new_element == EL_SP_DISK_RED)
11307         new_element = EL_SP_DISK_RED_ACTIVE;
11308     }
11309
11310     Feld[dropx][dropy] = new_element;
11311
11312     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11313       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11314                           el2img(Feld[dropx][dropy]), 0);
11315
11316     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11317
11318     /* needed if previous element just changed to "empty" in the last frame */
11319     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
11320
11321     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11322                                player->index_bit, drop_side);
11323     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11324                                         CE_PLAYER_DROPS_X,
11325                                         player->index_bit, drop_side);
11326
11327     TestIfElementTouchesCustomElement(dropx, dropy);
11328   }
11329   else          /* player is dropping a dyna bomb */
11330   {
11331     player->dynabombs_left--;
11332
11333     Feld[dropx][dropy] = new_element;
11334
11335     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11336       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11337                           el2img(Feld[dropx][dropy]), 0);
11338
11339     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11340   }
11341
11342   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11343     InitField_WithBug1(dropx, dropy, FALSE);
11344
11345   new_element = Feld[dropx][dropy];     /* element might have changed */
11346
11347   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11348       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11349   {
11350     int move_direction, nextx, nexty;
11351
11352     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11353       MovDir[dropx][dropy] = drop_direction;
11354
11355     move_direction = MovDir[dropx][dropy];
11356     nextx = dropx + GET_DX_FROM_DIR(move_direction);
11357     nexty = dropy + GET_DY_FROM_DIR(move_direction);
11358
11359     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
11360     CheckCollision[dropx][dropy] = 2;
11361   }
11362
11363   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11364   player->is_dropping = TRUE;
11365
11366   player->drop_x = dropx;
11367   player->drop_y = dropy;
11368
11369   return TRUE;
11370 }
11371
11372 /* ------------------------------------------------------------------------- */
11373 /* game sound playing functions                                              */
11374 /* ------------------------------------------------------------------------- */
11375
11376 static int *loop_sound_frame = NULL;
11377 static int *loop_sound_volume = NULL;
11378
11379 void InitPlayLevelSound()
11380 {
11381   int num_sounds = getSoundListSize();
11382
11383   checked_free(loop_sound_frame);
11384   checked_free(loop_sound_volume);
11385
11386   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
11387   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11388 }
11389
11390 static void PlayLevelSound(int x, int y, int nr)
11391 {
11392   int sx = SCREENX(x), sy = SCREENY(y);
11393   int volume, stereo_position;
11394   int max_distance = 8;
11395   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11396
11397   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11398       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11399     return;
11400
11401   if (!IN_LEV_FIELD(x, y) ||
11402       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11403       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11404     return;
11405
11406   volume = SOUND_MAX_VOLUME;
11407
11408   if (!IN_SCR_FIELD(sx, sy))
11409   {
11410     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11411     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11412
11413     volume -= volume * (dx > dy ? dx : dy) / max_distance;
11414   }
11415
11416   stereo_position = (SOUND_MAX_LEFT +
11417                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11418                      (SCR_FIELDX + 2 * max_distance));
11419
11420   if (IS_LOOP_SOUND(nr))
11421   {
11422     /* This assures that quieter loop sounds do not overwrite louder ones,
11423        while restarting sound volume comparison with each new game frame. */
11424
11425     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11426       return;
11427
11428     loop_sound_volume[nr] = volume;
11429     loop_sound_frame[nr] = FrameCounter;
11430   }
11431
11432   PlaySoundExt(nr, volume, stereo_position, type);
11433 }
11434
11435 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11436 {
11437   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11438                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
11439                  y < LEVELY(BY1) ? LEVELY(BY1) :
11440                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
11441                  sound_action);
11442 }
11443
11444 static void PlayLevelSoundAction(int x, int y, int action)
11445 {
11446   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11447 }
11448
11449 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11450 {
11451   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11452
11453   if (sound_effect != SND_UNDEFINED)
11454     PlayLevelSound(x, y, sound_effect);
11455 }
11456
11457 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11458                                               int action)
11459 {
11460   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11461
11462   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11463     PlayLevelSound(x, y, sound_effect);
11464 }
11465
11466 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11467 {
11468   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11469
11470   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11471     PlayLevelSound(x, y, sound_effect);
11472 }
11473
11474 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11475 {
11476   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11477
11478   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11479     StopSound(sound_effect);
11480 }
11481
11482 static void PlayLevelMusic()
11483 {
11484   if (levelset.music[level_nr] != MUS_UNDEFINED)
11485     PlayMusic(levelset.music[level_nr]);        /* from config file */
11486   else
11487     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
11488 }
11489
11490 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11491 {
11492   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11493
11494   switch (sample)
11495   {
11496     case SAMPLE_blank:
11497       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11498       break;
11499
11500     case SAMPLE_roll:
11501       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11502       break;
11503
11504     case SAMPLE_stone:
11505       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11506       break;
11507
11508     case SAMPLE_nut:
11509       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11510       break;
11511
11512     case SAMPLE_crack:
11513       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11514       break;
11515
11516     case SAMPLE_bug:
11517       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11518       break;
11519
11520     case SAMPLE_tank:
11521       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11522       break;
11523
11524     case SAMPLE_android_clone:
11525       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11526       break;
11527
11528     case SAMPLE_android_move:
11529       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11530       break;
11531
11532     case SAMPLE_spring:
11533       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11534       break;
11535
11536     case SAMPLE_slurp:
11537       PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
11538       break;
11539
11540     case SAMPLE_eater:
11541       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11542       break;
11543
11544     case SAMPLE_eater_eat:
11545       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11546       break;
11547
11548     case SAMPLE_alien:
11549       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11550       break;
11551
11552     case SAMPLE_collect:
11553       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11554       break;
11555
11556     case SAMPLE_diamond:
11557       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11558       break;
11559
11560     case SAMPLE_squash:
11561       /* !!! CHECK THIS !!! */
11562 #if 1
11563       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11564 #else
11565       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11566 #endif
11567       break;
11568
11569     case SAMPLE_wonderfall:
11570       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11571       break;
11572
11573     case SAMPLE_drip:
11574       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11575       break;
11576
11577     case SAMPLE_push:
11578       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11579       break;
11580
11581     case SAMPLE_dirt:
11582       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11583       break;
11584
11585     case SAMPLE_acid:
11586       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11587       break;
11588
11589     case SAMPLE_ball:
11590       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11591       break;
11592
11593     case SAMPLE_grow:
11594       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11595       break;
11596
11597     case SAMPLE_wonder:
11598       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11599       break;
11600
11601     case SAMPLE_door:
11602       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11603       break;
11604
11605     case SAMPLE_exit_open:
11606       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11607       break;
11608
11609     case SAMPLE_exit_leave:
11610       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11611       break;
11612
11613     case SAMPLE_dynamite:
11614       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11615       break;
11616
11617     case SAMPLE_tick:
11618       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11619       break;
11620
11621     case SAMPLE_press:
11622       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11623       break;
11624
11625     case SAMPLE_wheel:
11626       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11627       break;
11628
11629     case SAMPLE_boom:
11630       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11631       break;
11632
11633     case SAMPLE_die:
11634       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11635       break;
11636
11637     case SAMPLE_time:
11638       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11639       break;
11640
11641     default:
11642       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11643       break;
11644   }
11645 }
11646
11647 void RaiseScore(int value)
11648 {
11649   local_player->score += value;
11650
11651   DrawGameValue_Score(local_player->score);
11652 }
11653
11654 void RaiseScoreElement(int element)
11655 {
11656   switch(element)
11657   {
11658     case EL_EMERALD:
11659     case EL_BD_DIAMOND:
11660     case EL_EMERALD_YELLOW:
11661     case EL_EMERALD_RED:
11662     case EL_EMERALD_PURPLE:
11663     case EL_SP_INFOTRON:
11664       RaiseScore(level.score[SC_EMERALD]);
11665       break;
11666     case EL_DIAMOND:
11667       RaiseScore(level.score[SC_DIAMOND]);
11668       break;
11669     case EL_CRYSTAL:
11670       RaiseScore(level.score[SC_CRYSTAL]);
11671       break;
11672     case EL_PEARL:
11673       RaiseScore(level.score[SC_PEARL]);
11674       break;
11675     case EL_BUG:
11676     case EL_BD_BUTTERFLY:
11677     case EL_SP_ELECTRON:
11678       RaiseScore(level.score[SC_BUG]);
11679       break;
11680     case EL_SPACESHIP:
11681     case EL_BD_FIREFLY:
11682     case EL_SP_SNIKSNAK:
11683       RaiseScore(level.score[SC_SPACESHIP]);
11684       break;
11685     case EL_YAMYAM:
11686     case EL_DARK_YAMYAM:
11687       RaiseScore(level.score[SC_YAMYAM]);
11688       break;
11689     case EL_ROBOT:
11690       RaiseScore(level.score[SC_ROBOT]);
11691       break;
11692     case EL_PACMAN:
11693       RaiseScore(level.score[SC_PACMAN]);
11694       break;
11695     case EL_NUT:
11696       RaiseScore(level.score[SC_NUT]);
11697       break;
11698     case EL_DYNAMITE:
11699     case EL_SP_DISK_RED:
11700     case EL_DYNABOMB_INCREASE_NUMBER:
11701     case EL_DYNABOMB_INCREASE_SIZE:
11702     case EL_DYNABOMB_INCREASE_POWER:
11703       RaiseScore(level.score[SC_DYNAMITE]);
11704       break;
11705     case EL_SHIELD_NORMAL:
11706     case EL_SHIELD_DEADLY:
11707       RaiseScore(level.score[SC_SHIELD]);
11708       break;
11709     case EL_EXTRA_TIME:
11710       RaiseScore(level.extra_time_score);
11711       break;
11712     case EL_KEY_1:
11713     case EL_KEY_2:
11714     case EL_KEY_3:
11715     case EL_KEY_4:
11716     case EL_EM_KEY_1:
11717     case EL_EM_KEY_2:
11718     case EL_EM_KEY_3:
11719     case EL_EM_KEY_4:
11720     case EL_EMC_KEY_5:
11721     case EL_EMC_KEY_6:
11722     case EL_EMC_KEY_7:
11723     case EL_EMC_KEY_8:
11724       RaiseScore(level.score[SC_KEY]);
11725       break;
11726     default:
11727       RaiseScore(element_info[element].collect_score);
11728       break;
11729   }
11730 }
11731
11732 void RequestQuitGame(boolean ask_if_really_quit)
11733 {
11734   if (AllPlayersGone ||
11735       !ask_if_really_quit ||
11736       level_editor_test_game ||
11737       Request("Do you really want to quit the game ?",
11738               REQ_ASK | REQ_STAY_CLOSED))
11739   {
11740 #if defined(NETWORK_AVALIABLE)
11741     if (options.network)
11742       SendToServer_StopPlaying();
11743     else
11744 #endif
11745     {
11746       game_status = GAME_MODE_MAIN;
11747       DrawMainMenu();
11748     }
11749   }
11750   else
11751   {
11752     if (tape.playing && tape.deactivate_display)
11753       TapeDeactivateDisplayOff(TRUE);
11754
11755     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11756
11757     if (tape.playing && tape.deactivate_display)
11758       TapeDeactivateDisplayOn();
11759   }
11760 }
11761
11762
11763 /* ---------- new game button stuff ---------------------------------------- */
11764
11765 /* graphic position values for game buttons */
11766 #define GAME_BUTTON_XSIZE       30
11767 #define GAME_BUTTON_YSIZE       30
11768 #define GAME_BUTTON_XPOS        5
11769 #define GAME_BUTTON_YPOS        215
11770 #define SOUND_BUTTON_XPOS       5
11771 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11772
11773 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11774 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11775 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11776 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11777 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11778 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11779
11780 static struct
11781 {
11782   int x, y;
11783   int gadget_id;
11784   char *infotext;
11785 } gamebutton_info[NUM_GAME_BUTTONS] =
11786 {
11787   {
11788     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
11789     GAME_CTRL_ID_STOP,
11790     "stop game"
11791   },
11792   {
11793     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
11794     GAME_CTRL_ID_PAUSE,
11795     "pause game"
11796   },
11797   {
11798     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
11799     GAME_CTRL_ID_PLAY,
11800     "play game"
11801   },
11802   {
11803     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
11804     SOUND_CTRL_ID_MUSIC,
11805     "background music on/off"
11806   },
11807   {
11808     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
11809     SOUND_CTRL_ID_LOOPS,
11810     "sound loops on/off"
11811   },
11812   {
11813     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
11814     SOUND_CTRL_ID_SIMPLE,
11815     "normal sounds on/off"
11816   }
11817 };
11818
11819 void CreateGameButtons()
11820 {
11821   int i;
11822
11823   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11824   {
11825     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11826     struct GadgetInfo *gi;
11827     int button_type;
11828     boolean checked;
11829     unsigned long event_mask;
11830     int gd_xoffset, gd_yoffset;
11831     int gd_x1, gd_x2, gd_y1, gd_y2;
11832     int id = i;
11833
11834     gd_xoffset = gamebutton_info[i].x;
11835     gd_yoffset = gamebutton_info[i].y;
11836     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11837     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11838
11839     if (id == GAME_CTRL_ID_STOP ||
11840         id == GAME_CTRL_ID_PAUSE ||
11841         id == GAME_CTRL_ID_PLAY)
11842     {
11843       button_type = GD_TYPE_NORMAL_BUTTON;
11844       checked = FALSE;
11845       event_mask = GD_EVENT_RELEASED;
11846       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11847       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11848     }
11849     else
11850     {
11851       button_type = GD_TYPE_CHECK_BUTTON;
11852       checked =
11853         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11854          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11855          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11856       event_mask = GD_EVENT_PRESSED;
11857       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
11858       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11859     }
11860
11861     gi = CreateGadget(GDI_CUSTOM_ID, id,
11862                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
11863                       GDI_X, DX + gd_xoffset,
11864                       GDI_Y, DY + gd_yoffset,
11865                       GDI_WIDTH, GAME_BUTTON_XSIZE,
11866                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
11867                       GDI_TYPE, button_type,
11868                       GDI_STATE, GD_BUTTON_UNPRESSED,
11869                       GDI_CHECKED, checked,
11870                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11871                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11872                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11873                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11874                       GDI_EVENT_MASK, event_mask,
11875                       GDI_CALLBACK_ACTION, HandleGameButtons,
11876                       GDI_END);
11877
11878     if (gi == NULL)
11879       Error(ERR_EXIT, "cannot create gadget");
11880
11881     game_gadget[id] = gi;
11882   }
11883 }
11884
11885 void FreeGameButtons()
11886 {
11887   int i;
11888
11889   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11890     FreeGadget(game_gadget[i]);
11891 }
11892
11893 static void MapGameButtons()
11894 {
11895   int i;
11896
11897   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11898     MapGadget(game_gadget[i]);
11899 }
11900
11901 void UnmapGameButtons()
11902 {
11903   int i;
11904
11905   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11906     UnmapGadget(game_gadget[i]);
11907 }
11908
11909 static void HandleGameButtons(struct GadgetInfo *gi)
11910 {
11911   int id = gi->custom_id;
11912
11913   if (game_status != GAME_MODE_PLAYING)
11914     return;
11915
11916   switch (id)
11917   {
11918     case GAME_CTRL_ID_STOP:
11919       RequestQuitGame(TRUE);
11920       break;
11921
11922     case GAME_CTRL_ID_PAUSE:
11923       if (options.network)
11924       {
11925 #if defined(NETWORK_AVALIABLE)
11926         if (tape.pausing)
11927           SendToServer_ContinuePlaying();
11928         else
11929           SendToServer_PausePlaying();
11930 #endif
11931       }
11932       else
11933         TapeTogglePause(TAPE_TOGGLE_MANUAL);
11934       break;
11935
11936     case GAME_CTRL_ID_PLAY:
11937       if (tape.pausing)
11938       {
11939 #if defined(NETWORK_AVALIABLE)
11940         if (options.network)
11941           SendToServer_ContinuePlaying();
11942         else
11943 #endif
11944         {
11945           tape.pausing = FALSE;
11946           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11947         }
11948       }
11949       break;
11950
11951     case SOUND_CTRL_ID_MUSIC:
11952       if (setup.sound_music)
11953       { 
11954         setup.sound_music = FALSE;
11955         FadeMusic();
11956       }
11957       else if (audio.music_available)
11958       { 
11959         setup.sound = setup.sound_music = TRUE;
11960
11961         SetAudioMode(setup.sound);
11962
11963         PlayLevelMusic();
11964       }
11965       break;
11966
11967     case SOUND_CTRL_ID_LOOPS:
11968       if (setup.sound_loops)
11969         setup.sound_loops = FALSE;
11970       else if (audio.loops_available)
11971       {
11972         setup.sound = setup.sound_loops = TRUE;
11973         SetAudioMode(setup.sound);
11974       }
11975       break;
11976
11977     case SOUND_CTRL_ID_SIMPLE:
11978       if (setup.sound_simple)
11979         setup.sound_simple = FALSE;
11980       else if (audio.sound_available)
11981       {
11982         setup.sound = setup.sound_simple = TRUE;
11983         SetAudioMode(setup.sound);
11984       }
11985       break;
11986
11987     default:
11988       break;
11989   }
11990 }