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