rnd-20060110-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_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 CreateField(int x, int y, int target_element)
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(target_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(target_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] = target_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(target_element))
7471     RelocatePlayer(x, y, target_element);
7472
7473 #if 0
7474   ChangeCount[x][y]++;          /* count number of changes in the same frame */
7475 #endif
7476
7477   TestIfBadThingTouchesPlayer(x, y);
7478   TestIfPlayerTouchesCustomElement(x, y);
7479   TestIfElementTouchesCustomElement(x, y);
7480 }
7481
7482 static void CreateElementFromChange(int x, int y, int element)
7483 {
7484   CreateField(x, y, element);
7485
7486   ChangeCount[x][y]++;          /* count number of changes in the same frame */
7487 }
7488
7489 static boolean ChangeElement(int x, int y, int element, int page)
7490 {
7491   struct ElementChangeInfo *change = &element_info[element].change_page[page];
7492   int target_element;
7493   int old_element = Feld[x][y];
7494
7495   /* always use default change event to prevent running into a loop */
7496   if (ChangeEvent[x][y] == -1)
7497     ChangeEvent[x][y] = CE_DELAY;
7498
7499   if (ChangeEvent[x][y] == CE_DELAY)
7500   {
7501     /* reset actual trigger element, trigger player and action element */
7502     change->actual_trigger_element = EL_EMPTY;
7503     change->actual_trigger_player = EL_PLAYER_1;
7504     change->actual_trigger_side = CH_SIDE_NONE;
7505     change->actual_trigger_ce_value = 0;
7506   }
7507
7508   /* do not change elements more than a specified maximum number of changes */
7509   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7510     return FALSE;
7511
7512   ChangeCount[x][y]++;          /* count number of changes in the same frame */
7513
7514   if (change->explode)
7515   {
7516     Bang(x, y);
7517
7518     return TRUE;
7519   }
7520
7521   if (change->use_target_content)
7522   {
7523     boolean complete_replace = TRUE;
7524     boolean can_replace[3][3];
7525     int xx, yy;
7526
7527     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7528     {
7529       boolean is_empty;
7530       boolean is_walkable;
7531       boolean is_diggable;
7532       boolean is_collectible;
7533       boolean is_removable;
7534       boolean is_destructible;
7535       int ex = x + xx - 1;
7536       int ey = y + yy - 1;
7537       int content_element = change->target_content.e[xx][yy];
7538       int e;
7539
7540       can_replace[xx][yy] = TRUE;
7541
7542       if (ex == x && ey == y)   /* do not check changing element itself */
7543         continue;
7544
7545       if (content_element == EL_EMPTY_SPACE)
7546       {
7547         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
7548
7549         continue;
7550       }
7551
7552       if (!IN_LEV_FIELD(ex, ey))
7553       {
7554         can_replace[xx][yy] = FALSE;
7555         complete_replace = FALSE;
7556
7557         continue;
7558       }
7559
7560       e = Feld[ex][ey];
7561
7562       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7563         e = MovingOrBlocked2Element(ex, ey);
7564
7565       is_empty = (IS_FREE(ex, ey) ||
7566                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7567
7568       is_walkable     = (is_empty || IS_WALKABLE(e));
7569       is_diggable     = (is_empty || IS_DIGGABLE(e));
7570       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
7571       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7572       is_removable    = (is_diggable || is_collectible);
7573
7574       can_replace[xx][yy] =
7575         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
7576           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
7577           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
7578           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
7579           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
7580           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7581          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7582
7583       if (!can_replace[xx][yy])
7584         complete_replace = FALSE;
7585     }
7586
7587     if (!change->only_if_complete || complete_replace)
7588     {
7589       boolean something_has_changed = FALSE;
7590
7591       if (change->only_if_complete && change->use_random_replace &&
7592           RND(100) < change->random_percentage)
7593         return FALSE;
7594
7595       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7596       {
7597         int ex = x + xx - 1;
7598         int ey = y + yy - 1;
7599         int content_element;
7600
7601         if (can_replace[xx][yy] && (!change->use_random_replace ||
7602                                     RND(100) < change->random_percentage))
7603         {
7604           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7605             RemoveMovingField(ex, ey);
7606
7607           ChangeEvent[ex][ey] = ChangeEvent[x][y];
7608
7609           content_element = change->target_content.e[xx][yy];
7610           target_element = GET_TARGET_ELEMENT(content_element, change);
7611
7612           CreateElementFromChange(ex, ey, target_element);
7613
7614           something_has_changed = TRUE;
7615
7616           /* for symmetry reasons, freeze newly created border elements */
7617           if (ex != x || ey != y)
7618             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
7619         }
7620       }
7621
7622       if (something_has_changed)
7623       {
7624         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7625         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7626       }
7627     }
7628   }
7629   else
7630   {
7631     target_element = GET_TARGET_ELEMENT(change->target_element, change);
7632
7633     CreateElementFromChange(x, y, target_element);
7634
7635     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7636     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7637   }
7638
7639   /* this uses direct change before indirect change */
7640   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
7641
7642   return TRUE;
7643 }
7644
7645 #if USE_NEW_DELAYED_ACTION
7646
7647 static void HandleElementChange(int x, int y, int page)
7648 {
7649   int element = MovingOrBlocked2Element(x, y);
7650   struct ElementInfo *ei = &element_info[element];
7651   struct ElementChangeInfo *change = &ei->change_page[page];
7652
7653 #ifdef DEBUG
7654   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7655       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7656   {
7657     printf("\n\n");
7658     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7659            x, y, element, element_info[element].token_name);
7660     printf("HandleElementChange(): This should never happen!\n");
7661     printf("\n\n");
7662   }
7663 #endif
7664
7665   /* this can happen with classic bombs on walkable, changing elements */
7666   if (!CAN_CHANGE_OR_HAS_ACTION(element))
7667   {
7668 #if 0
7669     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7670       ChangeDelay[x][y] = 0;
7671 #endif
7672
7673     return;
7674   }
7675
7676   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7677   {
7678     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7679
7680     if (change->can_change)
7681     {
7682       ResetGfxAnimation(x, y);
7683       ResetRandomAnimationValue(x, y);
7684
7685       if (change->pre_change_function)
7686         change->pre_change_function(x, y);
7687     }
7688   }
7689
7690   ChangeDelay[x][y]--;
7691
7692   if (ChangeDelay[x][y] != 0)           /* continue element change */
7693   {
7694     if (change->can_change)
7695     {
7696       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7697
7698       if (IS_ANIMATED(graphic))
7699         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7700
7701       if (change->change_function)
7702         change->change_function(x, y);
7703     }
7704   }
7705   else                                  /* finish element change */
7706   {
7707     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7708     {
7709       page = ChangePage[x][y];
7710       ChangePage[x][y] = -1;
7711
7712       change = &ei->change_page[page];
7713     }
7714
7715     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7716     {
7717       ChangeDelay[x][y] = 1;            /* try change after next move step */
7718       ChangePage[x][y] = page;          /* remember page to use for change */
7719
7720       return;
7721     }
7722
7723     if (change->can_change)
7724     {
7725       if (ChangeElement(x, y, element, page))
7726       {
7727         if (change->post_change_function)
7728           change->post_change_function(x, y);
7729       }
7730     }
7731
7732     if (change->has_action)
7733       ExecuteCustomElementAction(x, y, element, page);
7734   }
7735 }
7736
7737 #else
7738
7739 static void HandleElementChange(int x, int y, int page)
7740 {
7741   int element = MovingOrBlocked2Element(x, y);
7742   struct ElementInfo *ei = &element_info[element];
7743   struct ElementChangeInfo *change = &ei->change_page[page];
7744
7745 #ifdef DEBUG
7746   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7747   {
7748     printf("\n\n");
7749     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
7750            x, y, element, element_info[element].token_name);
7751     printf("HandleElementChange(): This should never happen!\n");
7752     printf("\n\n");
7753   }
7754 #endif
7755
7756   /* this can happen with classic bombs on walkable, changing elements */
7757   if (!CAN_CHANGE(element))
7758   {
7759 #if 0
7760     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7761       ChangeDelay[x][y] = 0;
7762 #endif
7763
7764     return;
7765   }
7766
7767   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7768   {
7769     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7770
7771     ResetGfxAnimation(x, y);
7772     ResetRandomAnimationValue(x, y);
7773
7774     if (change->pre_change_function)
7775       change->pre_change_function(x, y);
7776   }
7777
7778   ChangeDelay[x][y]--;
7779
7780   if (ChangeDelay[x][y] != 0)           /* continue element change */
7781   {
7782     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7783
7784     if (IS_ANIMATED(graphic))
7785       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7786
7787     if (change->change_function)
7788       change->change_function(x, y);
7789   }
7790   else                                  /* finish element change */
7791   {
7792     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7793     {
7794       page = ChangePage[x][y];
7795       ChangePage[x][y] = -1;
7796
7797       change = &ei->change_page[page];
7798     }
7799
7800     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7801     {
7802       ChangeDelay[x][y] = 1;            /* try change after next move step */
7803       ChangePage[x][y] = page;          /* remember page to use for change */
7804
7805       return;
7806     }
7807
7808     if (ChangeElement(x, y, element, page))
7809     {
7810       if (change->post_change_function)
7811         change->post_change_function(x, y);
7812     }
7813   }
7814 }
7815
7816 #endif
7817
7818 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
7819                                               int trigger_element,
7820                                               int trigger_event,
7821                                               int trigger_player,
7822                                               int trigger_side,
7823                                               int trigger_page)
7824 {
7825   boolean change_done_any = FALSE;
7826   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7827   int i;
7828
7829   if (!(trigger_events[trigger_element][trigger_event]))
7830     return FALSE;
7831
7832   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7833   {
7834     int element = EL_CUSTOM_START + i;
7835     boolean change_done = FALSE;
7836     int p;
7837
7838     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7839         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7840       continue;
7841
7842     for (p = 0; p < element_info[element].num_change_pages; p++)
7843     {
7844       struct ElementChangeInfo *change = &element_info[element].change_page[p];
7845
7846       if (change->can_change_or_has_action &&
7847           change->has_event[trigger_event] &&
7848           change->trigger_side & trigger_side &&
7849           change->trigger_player & trigger_player &&
7850           change->trigger_page & trigger_page_bits &&
7851           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7852       {
7853         change->actual_trigger_element = trigger_element;
7854         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7855         change->actual_trigger_side = trigger_side;
7856         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
7857
7858         if ((change->can_change && !change_done) || change->has_action)
7859         {
7860           int x, y;
7861
7862 #if 1
7863           SCAN_PLAYFIELD(x, y)
7864 #else
7865           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7866 #endif
7867           {
7868             if (Feld[x][y] == element)
7869             {
7870               if (change->can_change && !change_done)
7871               {
7872                 ChangeDelay[x][y] = 1;
7873                 ChangeEvent[x][y] = trigger_event;
7874
7875                 HandleElementChange(x, y, p);
7876               }
7877 #if USE_NEW_DELAYED_ACTION
7878               else if (change->has_action)
7879               {
7880                 ExecuteCustomElementAction(x, y, element, p);
7881                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7882               }
7883 #else
7884               if (change->has_action)
7885               {
7886                 ExecuteCustomElementAction(x, y, element, p);
7887                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7888               }
7889 #endif
7890             }
7891           }
7892
7893           if (change->can_change)
7894           {
7895             change_done = TRUE;
7896             change_done_any = TRUE;
7897           }
7898         }
7899       }
7900     }
7901   }
7902
7903   return change_done_any;
7904 }
7905
7906 static boolean CheckElementChangeExt(int x, int y,
7907                                      int element,
7908                                      int trigger_element,
7909                                      int trigger_event,
7910                                      int trigger_player,
7911                                      int trigger_side)
7912 {
7913   boolean change_done = FALSE;
7914   int p;
7915
7916   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7917       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7918     return FALSE;
7919
7920   if (Feld[x][y] == EL_BLOCKED)
7921   {
7922     Blocked2Moving(x, y, &x, &y);
7923     element = Feld[x][y];
7924   }
7925
7926   if (Feld[x][y] != element)    /* check if element has already changed */
7927     return FALSE;
7928
7929   for (p = 0; p < element_info[element].num_change_pages; p++)
7930   {
7931     struct ElementChangeInfo *change = &element_info[element].change_page[p];
7932
7933     boolean check_trigger_element =
7934       (trigger_event == CE_TOUCHING_X ||
7935        trigger_event == CE_HITTING_X ||
7936        trigger_event == CE_HIT_BY_X);
7937
7938     if (change->can_change_or_has_action &&
7939         change->has_event[trigger_event] &&
7940         change->trigger_side & trigger_side &&
7941         change->trigger_player & trigger_player &&
7942         (!check_trigger_element ||
7943          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7944     {
7945       change->actual_trigger_element = trigger_element;
7946       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7947       change->actual_trigger_side = trigger_side;
7948       change->actual_trigger_ce_value = CustomValue[x][y];
7949
7950       /* special case: trigger element not at (x,y) position for some events */
7951       if (check_trigger_element)
7952       {
7953         static struct
7954         {
7955           int dx, dy;
7956         } move_xy[] =
7957           {
7958             {  0,  0 },
7959             { -1,  0 },
7960             { +1,  0 },
7961             {  0,  0 },
7962             {  0, -1 },
7963             {  0,  0 }, { 0, 0 }, { 0, 0 },
7964             {  0, +1 }
7965           };
7966
7967         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
7968         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
7969
7970         change->actual_trigger_ce_value = CustomValue[xx][yy];
7971       }
7972
7973       if (change->can_change && !change_done)
7974       {
7975         ChangeDelay[x][y] = 1;
7976         ChangeEvent[x][y] = trigger_event;
7977
7978         HandleElementChange(x, y, p);
7979
7980         change_done = TRUE;
7981       }
7982 #if USE_NEW_DELAYED_ACTION
7983       else if (change->has_action)
7984       {
7985         ExecuteCustomElementAction(x, y, element, p);
7986         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7987       }
7988 #else
7989       if (change->has_action)
7990       {
7991         ExecuteCustomElementAction(x, y, element, p);
7992         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7993       }
7994 #endif
7995     }
7996   }
7997
7998   return change_done;
7999 }
8000
8001 static void PlayPlayerSound(struct PlayerInfo *player)
8002 {
8003   int jx = player->jx, jy = player->jy;
8004   int sound_element = player->artwork_element;
8005   int last_action = player->last_action_waiting;
8006   int action = player->action_waiting;
8007
8008   if (player->is_waiting)
8009   {
8010     if (action != last_action)
8011       PlayLevelSoundElementAction(jx, jy, sound_element, action);
8012     else
8013       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8014   }
8015   else
8016   {
8017     if (action != last_action)
8018       StopSound(element_info[sound_element].sound[last_action]);
8019
8020     if (last_action == ACTION_SLEEPING)
8021       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8022   }
8023 }
8024
8025 static void PlayAllPlayersSound()
8026 {
8027   int i;
8028
8029   for (i = 0; i < MAX_PLAYERS; i++)
8030     if (stored_player[i].active)
8031       PlayPlayerSound(&stored_player[i]);
8032 }
8033
8034 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8035 {
8036   boolean last_waiting = player->is_waiting;
8037   int move_dir = player->MovDir;
8038
8039   player->last_action_waiting = player->action_waiting;
8040
8041   if (is_waiting)
8042   {
8043     if (!last_waiting)          /* not waiting -> waiting */
8044     {
8045       player->is_waiting = TRUE;
8046
8047       player->frame_counter_bored =
8048         FrameCounter +
8049         game.player_boring_delay_fixed +
8050         SimpleRND(game.player_boring_delay_random);
8051       player->frame_counter_sleeping =
8052         FrameCounter +
8053         game.player_sleeping_delay_fixed +
8054         SimpleRND(game.player_sleeping_delay_random);
8055
8056       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8057     }
8058
8059     if (game.player_sleeping_delay_fixed +
8060         game.player_sleeping_delay_random > 0 &&
8061         player->anim_delay_counter == 0 &&
8062         player->post_delay_counter == 0 &&
8063         FrameCounter >= player->frame_counter_sleeping)
8064       player->is_sleeping = TRUE;
8065     else if (game.player_boring_delay_fixed +
8066              game.player_boring_delay_random > 0 &&
8067              FrameCounter >= player->frame_counter_bored)
8068       player->is_bored = TRUE;
8069
8070     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8071                               player->is_bored ? ACTION_BORING :
8072                               ACTION_WAITING);
8073
8074     if (player->is_sleeping)
8075     {
8076       if (player->num_special_action_sleeping > 0)
8077       {
8078         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8079         {
8080           int last_special_action = player->special_action_sleeping;
8081           int num_special_action = player->num_special_action_sleeping;
8082           int special_action =
8083             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8084              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8085              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8086              last_special_action + 1 : ACTION_SLEEPING);
8087           int special_graphic =
8088             el_act_dir2img(player->artwork_element, special_action, move_dir);
8089
8090           player->anim_delay_counter =
8091             graphic_info[special_graphic].anim_delay_fixed +
8092             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8093           player->post_delay_counter =
8094             graphic_info[special_graphic].post_delay_fixed +
8095             SimpleRND(graphic_info[special_graphic].post_delay_random);
8096
8097           player->special_action_sleeping = special_action;
8098         }
8099
8100         if (player->anim_delay_counter > 0)
8101         {
8102           player->action_waiting = player->special_action_sleeping;
8103           player->anim_delay_counter--;
8104         }
8105         else if (player->post_delay_counter > 0)
8106         {
8107           player->post_delay_counter--;
8108         }
8109       }
8110     }
8111     else if (player->is_bored)
8112     {
8113       if (player->num_special_action_bored > 0)
8114       {
8115         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8116         {
8117           int special_action =
8118             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8119           int special_graphic =
8120             el_act_dir2img(player->artwork_element, special_action, move_dir);
8121
8122           player->anim_delay_counter =
8123             graphic_info[special_graphic].anim_delay_fixed +
8124             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8125           player->post_delay_counter =
8126             graphic_info[special_graphic].post_delay_fixed +
8127             SimpleRND(graphic_info[special_graphic].post_delay_random);
8128
8129           player->special_action_bored = special_action;
8130         }
8131
8132         if (player->anim_delay_counter > 0)
8133         {
8134           player->action_waiting = player->special_action_bored;
8135           player->anim_delay_counter--;
8136         }
8137         else if (player->post_delay_counter > 0)
8138         {
8139           player->post_delay_counter--;
8140         }
8141       }
8142     }
8143   }
8144   else if (last_waiting)        /* waiting -> not waiting */
8145   {
8146     player->is_waiting = FALSE;
8147     player->is_bored = FALSE;
8148     player->is_sleeping = FALSE;
8149
8150     player->frame_counter_bored = -1;
8151     player->frame_counter_sleeping = -1;
8152
8153     player->anim_delay_counter = 0;
8154     player->post_delay_counter = 0;
8155
8156     player->action_waiting = ACTION_DEFAULT;
8157
8158     player->special_action_bored = ACTION_DEFAULT;
8159     player->special_action_sleeping = ACTION_DEFAULT;
8160   }
8161 }
8162
8163 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8164 {
8165   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8166   int left      = player_action & JOY_LEFT;
8167   int right     = player_action & JOY_RIGHT;
8168   int up        = player_action & JOY_UP;
8169   int down      = player_action & JOY_DOWN;
8170   int button1   = player_action & JOY_BUTTON_1;
8171   int button2   = player_action & JOY_BUTTON_2;
8172   int dx        = (left ? -1    : right ? 1     : 0);
8173   int dy        = (up   ? -1    : down  ? 1     : 0);
8174
8175   if (!player->active || tape.pausing)
8176     return 0;
8177
8178   if (player_action)
8179   {
8180     if (button1)
8181       snapped = SnapField(player, dx, dy);
8182     else
8183     {
8184       if (button2)
8185         dropped = DropElement(player);
8186
8187       moved = MovePlayer(player, dx, dy);
8188     }
8189
8190     if (tape.single_step && tape.recording && !tape.pausing)
8191     {
8192       if (button1 || (dropped && !moved))
8193       {
8194         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8195         SnapField(player, 0, 0);                /* stop snapping */
8196       }
8197     }
8198
8199     SetPlayerWaiting(player, FALSE);
8200
8201     return player_action;
8202   }
8203   else
8204   {
8205     /* no actions for this player (no input at player's configured device) */
8206
8207     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8208     SnapField(player, 0, 0);
8209     CheckGravityMovementWhenNotMoving(player);
8210
8211     if (player->MovPos == 0)
8212       SetPlayerWaiting(player, TRUE);
8213
8214     if (player->MovPos == 0)    /* needed for tape.playing */
8215       player->is_moving = FALSE;
8216
8217     player->is_dropping = FALSE;
8218
8219     return 0;
8220   }
8221 }
8222
8223 void AdvanceFrameAndPlayerCounters(int player_nr)
8224 {
8225   int i;
8226
8227   /* advance frame counters (global frame counter and time frame counter) */
8228   FrameCounter++;
8229   TimeFrames++;
8230
8231   /* advance player counters (counters for move delay, move animation etc.) */
8232   for (i = 0; i < MAX_PLAYERS; i++)
8233   {
8234     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8235     int move_delay_value = stored_player[i].move_delay_value;
8236     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8237
8238     if (!advance_player_counters)       /* not all players may be affected */
8239       continue;
8240
8241 #if USE_NEW_PLAYER_ANIM
8242     if (move_frames == 0)       /* less than one move per game frame */
8243     {
8244       int stepsize = TILEX / move_delay_value;
8245       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8246       int count = (stored_player[i].is_moving ?
8247                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8248
8249       if (count % delay == 0)
8250         move_frames = 1;
8251     }
8252 #endif
8253
8254     stored_player[i].Frame += move_frames;
8255
8256     if (stored_player[i].MovPos != 0)
8257       stored_player[i].StepFrame += move_frames;
8258
8259     if (stored_player[i].move_delay > 0)
8260       stored_player[i].move_delay--;
8261
8262     /* due to bugs in previous versions, counter must count up, not down */
8263     if (stored_player[i].push_delay != -1)
8264       stored_player[i].push_delay++;
8265
8266     if (stored_player[i].drop_delay > 0)
8267       stored_player[i].drop_delay--;
8268   }
8269 }
8270
8271 void GameActions()
8272 {
8273   static unsigned long game_frame_delay = 0;
8274   unsigned long game_frame_delay_value;
8275   int magic_wall_x = 0, magic_wall_y = 0;
8276   int i, x, y, element, graphic;
8277   byte *recorded_player_action;
8278   byte summarized_player_action = 0;
8279   byte tape_action[MAX_PLAYERS];
8280
8281   if (game_status != GAME_MODE_PLAYING)
8282     return;
8283
8284   game_frame_delay_value =
8285     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8286
8287   if (tape.playing && tape.warp_forward && !tape.pausing)
8288     game_frame_delay_value = 0;
8289
8290   /* ---------- main game synchronization point ---------- */
8291
8292   InitPlayfieldScanModeVars();
8293
8294   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8295
8296   if (network_playing && !network_player_action_received)
8297   {
8298     /* try to get network player actions in time */
8299
8300 #if defined(NETWORK_AVALIABLE)
8301     /* last chance to get network player actions without main loop delay */
8302     HandleNetworking();
8303 #endif
8304
8305     /* game was quit by network peer */
8306     if (game_status != GAME_MODE_PLAYING)
8307       return;
8308
8309     if (!network_player_action_received)
8310       return;           /* failed to get network player actions in time */
8311   }
8312
8313   if (tape.pausing)
8314     return;
8315
8316   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8317
8318 #if 1
8319   /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8320   if (recorded_player_action == NULL && tape.pausing)
8321     return;
8322 #endif
8323
8324   for (i = 0; i < MAX_PLAYERS; i++)
8325   {
8326     summarized_player_action |= stored_player[i].action;
8327
8328     if (!network_playing)
8329       stored_player[i].effective_action = stored_player[i].action;
8330   }
8331
8332 #if defined(NETWORK_AVALIABLE)
8333   if (network_playing)
8334     SendToServer_MovePlayer(summarized_player_action);
8335 #endif
8336
8337   if (!options.network && !setup.team_mode)
8338     local_player->effective_action = summarized_player_action;
8339
8340   if (recorded_player_action != NULL)
8341     for (i = 0; i < MAX_PLAYERS; i++)
8342       stored_player[i].effective_action = recorded_player_action[i];
8343
8344   for (i = 0; i < MAX_PLAYERS; i++)
8345   {
8346     tape_action[i] = stored_player[i].effective_action;
8347
8348     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8349       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8350   }
8351
8352   /* only save actions from input devices, but not programmed actions */
8353   if (tape.recording)
8354     TapeRecordAction(tape_action);
8355
8356   for (i = 0; i < MAX_PLAYERS; i++)
8357   {
8358     int actual_player_action = stored_player[i].effective_action;
8359
8360 #if 1
8361     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8362        - rnd_equinox_tetrachloride 048
8363        - rnd_equinox_tetrachloride_ii 096
8364        - rnd_emanuel_schmieg 002
8365        - doctor_sloan_ww 001, 020
8366     */
8367     if (stored_player[i].MovPos == 0)
8368       CheckGravityMovement(&stored_player[i]);
8369 #endif
8370
8371     /* overwrite programmed action with tape action */
8372     if (stored_player[i].programmed_action)
8373       actual_player_action = stored_player[i].programmed_action;
8374
8375 #if 1
8376     PlayerActions(&stored_player[i], actual_player_action);
8377 #else
8378     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8379
8380     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8381       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8382 #endif
8383
8384     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8385   }
8386
8387   network_player_action_received = FALSE;
8388
8389   ScrollScreen(NULL, SCROLL_GO_ON);
8390
8391   /* for backwards compatibility, the following code emulates a fixed bug that
8392      occured when pushing elements (causing elements that just made their last
8393      pushing step to already (if possible) make their first falling step in the
8394      same game frame, which is bad); this code is also needed to use the famous
8395      "spring push bug" which is used in older levels and might be wanted to be
8396      used also in newer levels, but in this case the buggy pushing code is only
8397      affecting the "spring" element and no other elements */
8398
8399   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8400   {
8401     for (i = 0; i < MAX_PLAYERS; i++)
8402     {
8403       struct PlayerInfo *player = &stored_player[i];
8404       int x = player->jx;
8405       int y = player->jy;
8406
8407       if (player->active && player->is_pushing && player->is_moving &&
8408           IS_MOVING(x, y) &&
8409           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8410            Feld[x][y] == EL_SPRING))
8411       {
8412         ContinueMoving(x, y);
8413
8414         /* continue moving after pushing (this is actually a bug) */
8415         if (!IS_MOVING(x, y))
8416         {
8417           Stop[x][y] = FALSE;
8418         }
8419       }
8420     }
8421   }
8422
8423 #if 1
8424   SCAN_PLAYFIELD(x, y)
8425 #else
8426   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8427 #endif
8428   {
8429     ChangeCount[x][y] = 0;
8430     ChangeEvent[x][y] = -1;
8431
8432     /* this must be handled before main playfield loop */
8433     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8434     {
8435       MovDelay[x][y]--;
8436       if (MovDelay[x][y] <= 0)
8437         RemoveField(x, y);
8438     }
8439
8440 #if USE_NEW_SNAP_DELAY
8441     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8442     {
8443       MovDelay[x][y]--;
8444       if (MovDelay[x][y] <= 0)
8445       {
8446         RemoveField(x, y);
8447         DrawLevelField(x, y);
8448
8449         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
8450       }
8451     }
8452 #endif
8453
8454 #if DEBUG
8455     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8456     {
8457       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8458       printf("GameActions(): This should never happen!\n");
8459
8460       ChangePage[x][y] = -1;
8461     }
8462 #endif
8463
8464     Stop[x][y] = FALSE;
8465     if (WasJustMoving[x][y] > 0)
8466       WasJustMoving[x][y]--;
8467     if (WasJustFalling[x][y] > 0)
8468       WasJustFalling[x][y]--;
8469     if (CheckCollision[x][y] > 0)
8470       CheckCollision[x][y]--;
8471
8472     GfxFrame[x][y]++;
8473
8474     /* reset finished pushing action (not done in ContinueMoving() to allow
8475        continous pushing animation for elements with zero push delay) */
8476     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8477     {
8478       ResetGfxAnimation(x, y);
8479       DrawLevelField(x, y);
8480     }
8481
8482 #if DEBUG
8483     if (IS_BLOCKED(x, y))
8484     {
8485       int oldx, oldy;
8486
8487       Blocked2Moving(x, y, &oldx, &oldy);
8488       if (!IS_MOVING(oldx, oldy))
8489       {
8490         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8491         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8492         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8493         printf("GameActions(): This should never happen!\n");
8494       }
8495     }
8496 #endif
8497   }
8498
8499 #if 1
8500   SCAN_PLAYFIELD(x, y)
8501 #else
8502   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8503 #endif
8504   {
8505     element = Feld[x][y];
8506     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8507
8508 #if 0
8509     printf("::: %d,%d\n", x, y);
8510
8511     if (element == EL_ROCK)
8512       printf("::: Yo man! Rocks can fall!\n");
8513 #endif
8514
8515     if (graphic_info[graphic].anim_global_sync)
8516       GfxFrame[x][y] = FrameCounter;
8517     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
8518     {
8519       int old_gfx_frame = GfxFrame[x][y];
8520
8521       GfxFrame[x][y] = CustomValue[x][y];
8522
8523 #if 1
8524       if (GfxFrame[x][y] != old_gfx_frame)
8525 #endif
8526         DrawLevelGraphicAnimation(x, y, graphic);
8527     }
8528     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
8529     {
8530       int old_gfx_frame = GfxFrame[x][y];
8531
8532       GfxFrame[x][y] = element_info[element].collect_score;
8533
8534 #if 1
8535       if (GfxFrame[x][y] != old_gfx_frame)
8536 #endif
8537         DrawLevelGraphicAnimation(x, y, graphic);
8538     }
8539
8540     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8541         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8542       ResetRandomAnimationValue(x, y);
8543
8544     SetRandomAnimationValue(x, y);
8545
8546     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8547
8548     if (IS_INACTIVE(element))
8549     {
8550       if (IS_ANIMATED(graphic))
8551         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8552
8553       continue;
8554     }
8555
8556     /* this may take place after moving, so 'element' may have changed */
8557     if (IS_CHANGING(x, y) &&
8558         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8559     {
8560       int page = element_info[element].event_page_nr[CE_DELAY];
8561 #if 0
8562       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8563 #else
8564
8565 #if 0
8566       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8567 #endif
8568
8569 #if 0
8570       if (element == EL_CUSTOM_255)
8571         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8572 #endif
8573
8574 #if 1
8575       HandleElementChange(x, y, page);
8576 #else
8577       if (CAN_CHANGE(element))
8578         HandleElementChange(x, y, page);
8579
8580       if (HAS_ACTION(element))
8581         ExecuteCustomElementAction(x, y, element, page);
8582 #endif
8583
8584 #endif
8585
8586       element = Feld[x][y];
8587       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8588     }
8589
8590     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8591     {
8592       StartMoving(x, y);
8593
8594       element = Feld[x][y];
8595       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8596
8597       if (IS_ANIMATED(graphic) &&
8598           !IS_MOVING(x, y) &&
8599           !Stop[x][y])
8600         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8601
8602       if (IS_GEM(element) || element == EL_SP_INFOTRON)
8603         EdelsteinFunkeln(x, y);
8604     }
8605     else if ((element == EL_ACID ||
8606               element == EL_EXIT_OPEN ||
8607               element == EL_SP_EXIT_OPEN ||
8608               element == EL_SP_TERMINAL ||
8609               element == EL_SP_TERMINAL_ACTIVE ||
8610               element == EL_EXTRA_TIME ||
8611               element == EL_SHIELD_NORMAL ||
8612               element == EL_SHIELD_DEADLY) &&
8613              IS_ANIMATED(graphic))
8614       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8615     else if (IS_MOVING(x, y))
8616       ContinueMoving(x, y);
8617     else if (IS_ACTIVE_BOMB(element))
8618       CheckDynamite(x, y);
8619     else if (element == EL_AMOEBA_GROWING)
8620       AmoebeWaechst(x, y);
8621     else if (element == EL_AMOEBA_SHRINKING)
8622       AmoebaDisappearing(x, y);
8623
8624 #if !USE_NEW_AMOEBA_CODE
8625     else if (IS_AMOEBALIVE(element))
8626       AmoebeAbleger(x, y);
8627 #endif
8628
8629     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8630       Life(x, y);
8631     else if (element == EL_EXIT_CLOSED)
8632       CheckExit(x, y);
8633     else if (element == EL_SP_EXIT_CLOSED)
8634       CheckExitSP(x, y);
8635     else if (element == EL_EXPANDABLE_WALL_GROWING)
8636       MauerWaechst(x, y);
8637     else if (element == EL_EXPANDABLE_WALL ||
8638              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8639              element == EL_EXPANDABLE_WALL_VERTICAL ||
8640              element == EL_EXPANDABLE_WALL_ANY)
8641       MauerAbleger(x, y);
8642     else if (element == EL_FLAMES)
8643       CheckForDragon(x, y);
8644     else if (element == EL_EXPLOSION)
8645       ; /* drawing of correct explosion animation is handled separately */
8646     else if (element == EL_ELEMENT_SNAPPING)
8647     {
8648 #if 1
8649       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
8650
8651       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8652 #endif
8653     }
8654     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8655       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8656
8657 #if 0
8658     if (element == EL_CUSTOM_255 ||
8659         element == EL_CUSTOM_256)
8660       DrawLevelGraphicAnimation(x, y, graphic);
8661 #endif
8662
8663     if (IS_BELT_ACTIVE(element))
8664       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8665
8666     if (game.magic_wall_active)
8667     {
8668       int jx = local_player->jx, jy = local_player->jy;
8669
8670       /* play the element sound at the position nearest to the player */
8671       if ((element == EL_MAGIC_WALL_FULL ||
8672            element == EL_MAGIC_WALL_ACTIVE ||
8673            element == EL_MAGIC_WALL_EMPTYING ||
8674            element == EL_BD_MAGIC_WALL_FULL ||
8675            element == EL_BD_MAGIC_WALL_ACTIVE ||
8676            element == EL_BD_MAGIC_WALL_EMPTYING) &&
8677           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8678       {
8679         magic_wall_x = x;
8680         magic_wall_y = y;
8681       }
8682     }
8683   }
8684
8685 #if USE_NEW_AMOEBA_CODE
8686   /* new experimental amoeba growth stuff */
8687   if (!(FrameCounter % 8))
8688   {
8689     static unsigned long random = 1684108901;
8690
8691     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8692     {
8693       x = RND(lev_fieldx);
8694       y = RND(lev_fieldy);
8695       element = Feld[x][y];
8696
8697       if (!IS_PLAYER(x,y) &&
8698           (element == EL_EMPTY ||
8699            CAN_GROW_INTO(element) ||
8700            element == EL_QUICKSAND_EMPTY ||
8701            element == EL_ACID_SPLASH_LEFT ||
8702            element == EL_ACID_SPLASH_RIGHT))
8703       {
8704         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8705             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8706             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8707             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8708           Feld[x][y] = EL_AMOEBA_DROP;
8709       }
8710
8711       random = random * 129 + 1;
8712     }
8713   }
8714 #endif
8715
8716 #if 0
8717   if (game.explosions_delayed)
8718 #endif
8719   {
8720     game.explosions_delayed = FALSE;
8721
8722 #if 1
8723     SCAN_PLAYFIELD(x, y)
8724 #else
8725     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8726 #endif
8727     {
8728       element = Feld[x][y];
8729
8730       if (ExplodeField[x][y])
8731         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8732       else if (element == EL_EXPLOSION)
8733         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8734
8735       ExplodeField[x][y] = EX_TYPE_NONE;
8736     }
8737
8738     game.explosions_delayed = TRUE;
8739   }
8740
8741   if (game.magic_wall_active)
8742   {
8743     if (!(game.magic_wall_time_left % 4))
8744     {
8745       int element = Feld[magic_wall_x][magic_wall_y];
8746
8747       if (element == EL_BD_MAGIC_WALL_FULL ||
8748           element == EL_BD_MAGIC_WALL_ACTIVE ||
8749           element == EL_BD_MAGIC_WALL_EMPTYING)
8750         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8751       else
8752         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8753     }
8754
8755     if (game.magic_wall_time_left > 0)
8756     {
8757       game.magic_wall_time_left--;
8758       if (!game.magic_wall_time_left)
8759       {
8760 #if 1
8761         SCAN_PLAYFIELD(x, y)
8762 #else
8763         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8764 #endif
8765         {
8766           element = Feld[x][y];
8767
8768           if (element == EL_MAGIC_WALL_ACTIVE ||
8769               element == EL_MAGIC_WALL_FULL)
8770           {
8771             Feld[x][y] = EL_MAGIC_WALL_DEAD;
8772             DrawLevelField(x, y);
8773           }
8774           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8775                    element == EL_BD_MAGIC_WALL_FULL)
8776           {
8777             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8778             DrawLevelField(x, y);
8779           }
8780         }
8781
8782         game.magic_wall_active = FALSE;
8783       }
8784     }
8785   }
8786
8787   if (game.light_time_left > 0)
8788   {
8789     game.light_time_left--;
8790
8791     if (game.light_time_left == 0)
8792       RedrawAllLightSwitchesAndInvisibleElements();
8793   }
8794
8795   if (game.timegate_time_left > 0)
8796   {
8797     game.timegate_time_left--;
8798
8799     if (game.timegate_time_left == 0)
8800       CloseAllOpenTimegates();
8801   }
8802
8803   if (game.lenses_time_left > 0)
8804   {
8805     game.lenses_time_left--;
8806
8807     if (game.lenses_time_left == 0)
8808       RedrawAllInvisibleElementsForLenses();
8809   }
8810
8811   if (game.magnify_time_left > 0)
8812   {
8813     game.magnify_time_left--;
8814
8815     if (game.magnify_time_left == 0)
8816       RedrawAllInvisibleElementsForMagnifier();
8817   }
8818
8819   for (i = 0; i < MAX_PLAYERS; i++)
8820   {
8821     struct PlayerInfo *player = &stored_player[i];
8822
8823     if (SHIELD_ON(player))
8824     {
8825       if (player->shield_deadly_time_left)
8826         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8827       else if (player->shield_normal_time_left)
8828         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8829     }
8830   }
8831
8832   if (TimeFrames >= FRAMES_PER_SECOND)
8833   {
8834     TimeFrames = 0;
8835     TapeTime++;
8836
8837     for (i = 0; i < MAX_PLAYERS; i++)
8838     {
8839       struct PlayerInfo *player = &stored_player[i];
8840
8841       if (SHIELD_ON(player))
8842       {
8843         player->shield_normal_time_left--;
8844
8845         if (player->shield_deadly_time_left > 0)
8846           player->shield_deadly_time_left--;
8847       }
8848     }
8849
8850     if (!level.use_step_counter)
8851     {
8852       TimePlayed++;
8853
8854       if (TimeLeft > 0)
8855       {
8856         TimeLeft--;
8857
8858         if (TimeLeft <= 10 && setup.time_limit)
8859           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8860
8861         DrawGameValue_Time(TimeLeft);
8862
8863         if (!TimeLeft && setup.time_limit)
8864           for (i = 0; i < MAX_PLAYERS; i++)
8865             KillPlayer(&stored_player[i]);
8866       }
8867       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8868         DrawGameValue_Time(TimePlayed);
8869     }
8870
8871     if (tape.recording || tape.playing)
8872       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8873   }
8874
8875   DrawAllPlayers();
8876   PlayAllPlayersSound();
8877
8878   if (options.debug)                    /* calculate frames per second */
8879   {
8880     static unsigned long fps_counter = 0;
8881     static int fps_frames = 0;
8882     unsigned long fps_delay_ms = Counter() - fps_counter;
8883
8884     fps_frames++;
8885
8886     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
8887     {
8888       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8889
8890       fps_frames = 0;
8891       fps_counter = Counter();
8892     }
8893
8894     redraw_mask |= REDRAW_FPS;
8895   }
8896
8897   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
8898
8899   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8900   {
8901     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8902
8903     local_player->show_envelope = 0;
8904   }
8905
8906   /* use random number generator in every frame to make it less predictable */
8907   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8908     RND(1);
8909 }
8910
8911 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8912 {
8913   int min_x = x, min_y = y, max_x = x, max_y = y;
8914   int i;
8915
8916   for (i = 0; i < MAX_PLAYERS; i++)
8917   {
8918     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8919
8920     if (!stored_player[i].active || &stored_player[i] == player)
8921       continue;
8922
8923     min_x = MIN(min_x, jx);
8924     min_y = MIN(min_y, jy);
8925     max_x = MAX(max_x, jx);
8926     max_y = MAX(max_y, jy);
8927   }
8928
8929   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8930 }
8931
8932 static boolean AllPlayersInVisibleScreen()
8933 {
8934   int i;
8935
8936   for (i = 0; i < MAX_PLAYERS; i++)
8937   {
8938     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8939
8940     if (!stored_player[i].active)
8941       continue;
8942
8943     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8944       return FALSE;
8945   }
8946
8947   return TRUE;
8948 }
8949
8950 void ScrollLevel(int dx, int dy)
8951 {
8952   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8953   int x, y;
8954
8955   BlitBitmap(drawto_field, drawto_field,
8956              FX + TILEX * (dx == -1) - softscroll_offset,
8957              FY + TILEY * (dy == -1) - softscroll_offset,
8958              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8959              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8960              FX + TILEX * (dx == 1) - softscroll_offset,
8961              FY + TILEY * (dy == 1) - softscroll_offset);
8962
8963   if (dx)
8964   {
8965     x = (dx == 1 ? BX1 : BX2);
8966     for (y = BY1; y <= BY2; y++)
8967       DrawScreenField(x, y);
8968   }
8969
8970   if (dy)
8971   {
8972     y = (dy == 1 ? BY1 : BY2);
8973     for (x = BX1; x <= BX2; x++)
8974       DrawScreenField(x, y);
8975   }
8976
8977   redraw_mask |= REDRAW_FIELD;
8978 }
8979
8980 static boolean canFallDown(struct PlayerInfo *player)
8981 {
8982   int jx = player->jx, jy = player->jy;
8983
8984   return (IN_LEV_FIELD(jx, jy + 1) &&
8985           (IS_FREE(jx, jy + 1) ||
8986            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8987           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8988           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8989 }
8990
8991 static boolean canPassField(int x, int y, int move_dir)
8992 {
8993   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8994   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8995   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
8996   int nextx = x + dx;
8997   int nexty = y + dy;
8998   int element = Feld[x][y];
8999
9000   return (IS_PASSABLE_FROM(element, opposite_dir) &&
9001           !CAN_MOVE(element) &&
9002           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9003           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9004           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9005 }
9006
9007 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9008 {
9009   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9010   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9011   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9012   int newx = x + dx;
9013   int newy = y + dy;
9014
9015   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9016           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9017           (IS_DIGGABLE(Feld[newx][newy]) ||
9018            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9019            canPassField(newx, newy, move_dir)));
9020 }
9021
9022 static void CheckGravityMovement(struct PlayerInfo *player)
9023 {
9024   if (game.gravity && !player->programmed_action)
9025   {
9026     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9027     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9028     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9029     int jx = player->jx, jy = player->jy;
9030     boolean player_is_moving_to_valid_field =
9031       (!player_is_snapping &&
9032        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9033         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9034     boolean player_can_fall_down = canFallDown(player);
9035
9036     if (player_can_fall_down &&
9037         !player_is_moving_to_valid_field)
9038       player->programmed_action = MV_DOWN;
9039   }
9040 }
9041
9042 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9043 {
9044   return CheckGravityMovement(player);
9045
9046   if (game.gravity && !player->programmed_action)
9047   {
9048     int jx = player->jx, jy = player->jy;
9049     boolean field_under_player_is_free =
9050       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9051     boolean player_is_standing_on_valid_field =
9052       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9053        (IS_WALKABLE(Feld[jx][jy]) &&
9054         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9055
9056     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9057       player->programmed_action = MV_DOWN;
9058   }
9059 }
9060
9061 /*
9062   MovePlayerOneStep()
9063   -----------------------------------------------------------------------------
9064   dx, dy:               direction (non-diagonal) to try to move the player to
9065   real_dx, real_dy:     direction as read from input device (can be diagonal)
9066 */
9067
9068 boolean MovePlayerOneStep(struct PlayerInfo *player,
9069                           int dx, int dy, int real_dx, int real_dy)
9070 {
9071   int jx = player->jx, jy = player->jy;
9072   int new_jx = jx + dx, new_jy = jy + dy;
9073   int element;
9074   int can_move;
9075
9076   if (!player->active || (!dx && !dy))
9077     return MF_NO_ACTION;
9078
9079   player->MovDir = (dx < 0 ? MV_LEFT :
9080                     dx > 0 ? MV_RIGHT :
9081                     dy < 0 ? MV_UP :
9082                     dy > 0 ? MV_DOWN :  MV_NONE);
9083
9084   if (!IN_LEV_FIELD(new_jx, new_jy))
9085     return MF_NO_ACTION;
9086
9087   if (player->cannot_move)
9088   {
9089 #if 1
9090     if (player->MovPos == 0)
9091     {
9092       player->is_moving = FALSE;
9093       player->is_digging = FALSE;
9094       player->is_collecting = FALSE;
9095       player->is_snapping = FALSE;
9096       player->is_pushing = FALSE;
9097     }
9098 #else
9099     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9100     SnapField(player, 0, 0);
9101 #endif
9102
9103     return MF_NO_ACTION;
9104   }
9105
9106   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9107     return MF_NO_ACTION;
9108
9109   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9110
9111   if (DONT_RUN_INTO(element))
9112   {
9113     if (element == EL_ACID && dx == 0 && dy == 1)
9114     {
9115       SplashAcid(new_jx, new_jy);
9116       Feld[jx][jy] = EL_PLAYER_1;
9117       InitMovingField(jx, jy, MV_DOWN);
9118       Store[jx][jy] = EL_ACID;
9119       ContinueMoving(jx, jy);
9120       BuryPlayer(player);
9121     }
9122     else
9123       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9124
9125     return MF_MOVING;
9126   }
9127
9128   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9129   if (can_move != MF_MOVING)
9130     return can_move;
9131
9132   /* check if DigField() has caused relocation of the player */
9133   if (player->jx != jx || player->jy != jy)
9134     return MF_NO_ACTION;        /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
9135
9136   StorePlayer[jx][jy] = 0;
9137   player->last_jx = jx;
9138   player->last_jy = jy;
9139   player->jx = new_jx;
9140   player->jy = new_jy;
9141   StorePlayer[new_jx][new_jy] = player->element_nr;
9142
9143   if (player->move_delay_value_next != -1)
9144   {
9145     player->move_delay_value = player->move_delay_value_next;
9146     player->move_delay_value_next = -1;
9147   }
9148
9149   player->MovPos =
9150     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9151
9152   player->step_counter++;
9153
9154   PlayerVisit[jx][jy] = FrameCounter;
9155
9156   ScrollPlayer(player, SCROLL_INIT);
9157
9158   return MF_MOVING;
9159 }
9160
9161 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9162 {
9163   int jx = player->jx, jy = player->jy;
9164   int old_jx = jx, old_jy = jy;
9165   int moved = MF_NO_ACTION;
9166
9167   if (!player->active)
9168     return FALSE;
9169
9170   if (!dx && !dy)
9171   {
9172     if (player->MovPos == 0)
9173     {
9174       player->is_moving = FALSE;
9175       player->is_digging = FALSE;
9176       player->is_collecting = FALSE;
9177       player->is_snapping = FALSE;
9178       player->is_pushing = FALSE;
9179     }
9180
9181     return FALSE;
9182   }
9183
9184   if (player->move_delay > 0)
9185     return FALSE;
9186
9187   player->move_delay = -1;              /* set to "uninitialized" value */
9188
9189   /* store if player is automatically moved to next field */
9190   player->is_auto_moving = (player->programmed_action != MV_NONE);
9191
9192   /* remove the last programmed player action */
9193   player->programmed_action = 0;
9194
9195   if (player->MovPos)
9196   {
9197     /* should only happen if pre-1.2 tape recordings are played */
9198     /* this is only for backward compatibility */
9199
9200     int original_move_delay_value = player->move_delay_value;
9201
9202 #if DEBUG
9203     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9204            tape.counter);
9205 #endif
9206
9207     /* scroll remaining steps with finest movement resolution */
9208     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9209
9210     while (player->MovPos)
9211     {
9212       ScrollPlayer(player, SCROLL_GO_ON);
9213       ScrollScreen(NULL, SCROLL_GO_ON);
9214
9215       AdvanceFrameAndPlayerCounters(player->index_nr);
9216
9217       DrawAllPlayers();
9218       BackToFront();
9219     }
9220
9221     player->move_delay_value = original_move_delay_value;
9222   }
9223
9224   if (player->last_move_dir & MV_HORIZONTAL)
9225   {
9226     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9227       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9228   }
9229   else
9230   {
9231     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9232       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9233   }
9234
9235   jx = player->jx;
9236   jy = player->jy;
9237
9238   if (moved & MF_MOVING && !ScreenMovPos &&
9239       (player == local_player || !options.network))
9240   {
9241     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9242     int offset = (setup.scroll_delay ? 3 : 0);
9243
9244     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9245     {
9246       /* actual player has left the screen -- scroll in that direction */
9247       if (jx != old_jx)         /* player has moved horizontally */
9248         scroll_x += (jx - old_jx);
9249       else                      /* player has moved vertically */
9250         scroll_y += (jy - old_jy);
9251     }
9252     else
9253     {
9254       if (jx != old_jx)         /* player has moved horizontally */
9255       {
9256         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
9257             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9258           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9259
9260         /* don't scroll over playfield boundaries */
9261         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9262           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9263
9264         /* don't scroll more than one field at a time */
9265         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9266
9267         /* don't scroll against the player's moving direction */
9268         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
9269             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9270           scroll_x = old_scroll_x;
9271       }
9272       else                      /* player has moved vertically */
9273       {
9274         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
9275             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9276           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9277
9278         /* don't scroll over playfield boundaries */
9279         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9280           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9281
9282         /* don't scroll more than one field at a time */
9283         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9284
9285         /* don't scroll against the player's moving direction */
9286         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
9287             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9288           scroll_y = old_scroll_y;
9289       }
9290     }
9291
9292     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9293     {
9294       if (!options.network && !AllPlayersInVisibleScreen())
9295       {
9296         scroll_x = old_scroll_x;
9297         scroll_y = old_scroll_y;
9298       }
9299       else
9300       {
9301         ScrollScreen(player, SCROLL_INIT);
9302         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9303       }
9304     }
9305   }
9306
9307   player->StepFrame = 0;
9308
9309   if (moved & MF_MOVING)
9310   {
9311     if (old_jx != jx && old_jy == jy)
9312       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9313     else if (old_jx == jx && old_jy != jy)
9314       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9315
9316     DrawLevelField(jx, jy);     /* for "crumbled sand" */
9317
9318     player->last_move_dir = player->MovDir;
9319     player->is_moving = TRUE;
9320     player->is_snapping = FALSE;
9321     player->is_switching = FALSE;
9322     player->is_dropping = FALSE;
9323   }
9324   else
9325   {
9326     CheckGravityMovementWhenNotMoving(player);
9327
9328     player->is_moving = FALSE;
9329
9330     /* at this point, the player is allowed to move, but cannot move right now
9331        (e.g. because of something blocking the way) -- ensure that the player
9332        is also allowed to move in the next frame (in old versions before 3.1.1,
9333        the player was forced to wait again for eight frames before next try) */
9334
9335     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9336       player->move_delay = 0;   /* allow direct movement in the next frame */
9337   }
9338
9339   if (player->move_delay == -1)         /* not yet initialized by DigField() */
9340     player->move_delay = player->move_delay_value;
9341
9342   if (game.engine_version < VERSION_IDENT(3,0,7,0))
9343   {
9344     TestIfPlayerTouchesBadThing(jx, jy);
9345     TestIfPlayerTouchesCustomElement(jx, jy);
9346   }
9347
9348   if (!player->active)
9349     RemovePlayer(player);
9350
9351   return moved;
9352 }
9353
9354 void ScrollPlayer(struct PlayerInfo *player, int mode)
9355 {
9356   int jx = player->jx, jy = player->jy;
9357   int last_jx = player->last_jx, last_jy = player->last_jy;
9358   int move_stepsize = TILEX / player->move_delay_value;
9359
9360 #if USE_NEW_PLAYER_SPEED
9361   if (!player->active)
9362     return;
9363
9364   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
9365     return;
9366 #else
9367   if (!player->active || player->MovPos == 0)
9368     return;
9369 #endif
9370
9371   if (mode == SCROLL_INIT)
9372   {
9373     player->actual_frame_counter = FrameCounter;
9374     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9375
9376     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9377         Feld[last_jx][last_jy] == EL_EMPTY)
9378     {
9379       int last_field_block_delay = 0;   /* start with no blocking at all */
9380       int block_delay_adjustment = player->block_delay_adjustment;
9381
9382       /* if player blocks last field, add delay for exactly one move */
9383       if (player->block_last_field)
9384       {
9385         last_field_block_delay += player->move_delay_value;
9386
9387         /* when blocking enabled, prevent moving up despite gravity */
9388         if (game.gravity && player->MovDir == MV_UP)
9389           block_delay_adjustment = -1;
9390       }
9391
9392       /* add block delay adjustment (also possible when not blocking) */
9393       last_field_block_delay += block_delay_adjustment;
9394
9395       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9396       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9397     }
9398
9399 #if USE_NEW_PLAYER_SPEED
9400     if (player->MovPos != 0)    /* player has not yet reached destination */
9401       return;
9402 #else
9403     return;
9404 #endif
9405   }
9406   else if (!FrameReached(&player->actual_frame_counter, 1))
9407     return;
9408
9409 #if 0
9410   printf("::: player->MovPos: %d -> %d\n",
9411          player->MovPos,
9412          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9413 #endif
9414
9415 #if USE_NEW_PLAYER_SPEED
9416   if (player->MovPos != 0)
9417   {
9418     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9419     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9420
9421     /* before DrawPlayer() to draw correct player graphic for this case */
9422     if (player->MovPos == 0)
9423       CheckGravityMovement(player);
9424   }
9425 #else
9426   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9427   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9428
9429   /* before DrawPlayer() to draw correct player graphic for this case */
9430   if (player->MovPos == 0)
9431     CheckGravityMovement(player);
9432 #endif
9433
9434   if (player->MovPos == 0)      /* player reached destination field */
9435   {
9436 #if 0
9437     printf("::: player reached destination field\n");
9438 #endif
9439
9440     if (player->move_delay_reset_counter > 0)
9441     {
9442       player->move_delay_reset_counter--;
9443
9444       if (player->move_delay_reset_counter == 0)
9445       {
9446         /* continue with normal speed after quickly moving through gate */
9447         HALVE_PLAYER_SPEED(player);
9448
9449         /* be able to make the next move without delay */
9450         player->move_delay = 0;
9451       }
9452     }
9453
9454     player->last_jx = jx;
9455     player->last_jy = jy;
9456
9457     if (Feld[jx][jy] == EL_EXIT_OPEN ||
9458         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9459         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
9460     {
9461       DrawPlayer(player);       /* needed here only to cleanup last field */
9462       RemovePlayer(player);
9463
9464       if (local_player->friends_still_needed == 0 ||
9465           IS_SP_ELEMENT(Feld[jx][jy]))
9466         player->LevelSolved = player->GameOver = TRUE;
9467     }
9468
9469     /* this breaks one level: "machine", level 000 */
9470     {
9471       int move_direction = player->MovDir;
9472       int enter_side = MV_DIR_OPPOSITE(move_direction);
9473       int leave_side = move_direction;
9474       int old_jx = last_jx;
9475       int old_jy = last_jy;
9476       int old_element = Feld[old_jx][old_jy];
9477       int new_element = Feld[jx][jy];
9478
9479       if (IS_CUSTOM_ELEMENT(old_element))
9480         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9481                                    CE_LEFT_BY_PLAYER,
9482                                    player->index_bit, leave_side);
9483
9484       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9485                                           CE_PLAYER_LEAVES_X,
9486                                           player->index_bit, leave_side);
9487
9488       if (IS_CUSTOM_ELEMENT(new_element))
9489         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9490                                    player->index_bit, enter_side);
9491
9492       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9493                                           CE_PLAYER_ENTERS_X,
9494                                           player->index_bit, enter_side);
9495
9496       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9497                                         CE_MOVE_OF_X, move_direction);
9498     }
9499
9500     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9501     {
9502       TestIfPlayerTouchesBadThing(jx, jy);
9503       TestIfPlayerTouchesCustomElement(jx, jy);
9504
9505       /* needed because pushed element has not yet reached its destination,
9506          so it would trigger a change event at its previous field location */
9507       if (!player->is_pushing)
9508         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
9509
9510       if (!player->active)
9511         RemovePlayer(player);
9512     }
9513
9514     if (level.use_step_counter)
9515     {
9516       int i;
9517
9518       TimePlayed++;
9519
9520       if (TimeLeft > 0)
9521       {
9522         TimeLeft--;
9523
9524         if (TimeLeft <= 10 && setup.time_limit)
9525           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9526
9527         DrawGameValue_Time(TimeLeft);
9528
9529         if (!TimeLeft && setup.time_limit)
9530           for (i = 0; i < MAX_PLAYERS; i++)
9531             KillPlayer(&stored_player[i]);
9532       }
9533       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9534         DrawGameValue_Time(TimePlayed);
9535     }
9536
9537     if (tape.single_step && tape.recording && !tape.pausing &&
9538         !player->programmed_action)
9539       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9540   }
9541 }
9542
9543 void ScrollScreen(struct PlayerInfo *player, int mode)
9544 {
9545   static unsigned long screen_frame_counter = 0;
9546
9547   if (mode == SCROLL_INIT)
9548   {
9549     /* set scrolling step size according to actual player's moving speed */
9550     ScrollStepSize = TILEX / player->move_delay_value;
9551
9552     screen_frame_counter = FrameCounter;
9553     ScreenMovDir = player->MovDir;
9554     ScreenMovPos = player->MovPos;
9555     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9556     return;
9557   }
9558   else if (!FrameReached(&screen_frame_counter, 1))
9559     return;
9560
9561   if (ScreenMovPos)
9562   {
9563     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9564     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9565     redraw_mask |= REDRAW_FIELD;
9566   }
9567   else
9568     ScreenMovDir = MV_NONE;
9569 }
9570
9571 void TestIfPlayerTouchesCustomElement(int x, int y)
9572 {
9573   static int xy[4][2] =
9574   {
9575     { 0, -1 },
9576     { -1, 0 },
9577     { +1, 0 },
9578     { 0, +1 }
9579   };
9580   static int trigger_sides[4][2] =
9581   {
9582     /* center side       border side */
9583     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9584     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9585     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9586     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9587   };
9588   static int touch_dir[4] =
9589   {
9590     MV_LEFT | MV_RIGHT,
9591     MV_UP   | MV_DOWN,
9592     MV_UP   | MV_DOWN,
9593     MV_LEFT | MV_RIGHT
9594   };
9595   int center_element = Feld[x][y];      /* should always be non-moving! */
9596   int i;
9597
9598   for (i = 0; i < NUM_DIRECTIONS; i++)
9599   {
9600     int xx = x + xy[i][0];
9601     int yy = y + xy[i][1];
9602     int center_side = trigger_sides[i][0];
9603     int border_side = trigger_sides[i][1];
9604     int border_element;
9605
9606     if (!IN_LEV_FIELD(xx, yy))
9607       continue;
9608
9609     if (IS_PLAYER(x, y))
9610     {
9611       struct PlayerInfo *player = PLAYERINFO(x, y);
9612
9613       if (game.engine_version < VERSION_IDENT(3,0,7,0))
9614         border_element = Feld[xx][yy];          /* may be moving! */
9615       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9616         border_element = Feld[xx][yy];
9617       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
9618         border_element = MovingOrBlocked2Element(xx, yy);
9619       else
9620         continue;               /* center and border element do not touch */
9621
9622       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9623                                  player->index_bit, border_side);
9624       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9625                                           CE_PLAYER_TOUCHES_X,
9626                                           player->index_bit, border_side);
9627     }
9628     else if (IS_PLAYER(xx, yy))
9629     {
9630       struct PlayerInfo *player = PLAYERINFO(xx, yy);
9631
9632       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9633       {
9634         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9635           continue;             /* center and border element do not touch */
9636       }
9637
9638       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9639                                  player->index_bit, center_side);
9640       CheckTriggeredElementChangeByPlayer(x, y, center_element,
9641                                           CE_PLAYER_TOUCHES_X,
9642                                           player->index_bit, center_side);
9643       break;
9644     }
9645   }
9646 }
9647
9648 void TestIfElementTouchesCustomElement(int x, int y)
9649 {
9650   static int xy[4][2] =
9651   {
9652     { 0, -1 },
9653     { -1, 0 },
9654     { +1, 0 },
9655     { 0, +1 }
9656   };
9657   static int trigger_sides[4][2] =
9658   {
9659     /* center side      border side */
9660     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9661     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9662     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9663     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9664   };
9665   static int touch_dir[4] =
9666   {
9667     MV_LEFT | MV_RIGHT,
9668     MV_UP   | MV_DOWN,
9669     MV_UP   | MV_DOWN,
9670     MV_LEFT | MV_RIGHT
9671   };
9672   boolean change_center_element = FALSE;
9673   int center_element = Feld[x][y];      /* should always be non-moving! */
9674   int i;
9675
9676   for (i = 0; i < NUM_DIRECTIONS; i++)
9677   {
9678     int xx = x + xy[i][0];
9679     int yy = y + xy[i][1];
9680     int center_side = trigger_sides[i][0];
9681     int border_side = trigger_sides[i][1];
9682     int border_element;
9683
9684     if (!IN_LEV_FIELD(xx, yy))
9685       continue;
9686
9687     if (game.engine_version < VERSION_IDENT(3,0,7,0))
9688       border_element = Feld[xx][yy];    /* may be moving! */
9689     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9690       border_element = Feld[xx][yy];
9691     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
9692       border_element = MovingOrBlocked2Element(xx, yy);
9693     else
9694       continue;                 /* center and border element do not touch */
9695
9696     /* check for change of center element (but change it only once) */
9697     if (!change_center_element)
9698       change_center_element =
9699         CheckElementChangeBySide(x, y, center_element, border_element,
9700                                  CE_TOUCHING_X, border_side);
9701
9702     /* check for change of border element */
9703     CheckElementChangeBySide(xx, yy, border_element, center_element,
9704                              CE_TOUCHING_X, center_side);
9705   }
9706 }
9707
9708 void TestIfElementHitsCustomElement(int x, int y, int direction)
9709 {
9710   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9711   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9712   int hitx = x + dx, hity = y + dy;
9713   int hitting_element = Feld[x][y];
9714   int touched_element;
9715
9716   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9717     return;
9718
9719   touched_element = (IN_LEV_FIELD(hitx, hity) ?
9720                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9721
9722   if (IN_LEV_FIELD(hitx, hity))
9723   {
9724     int opposite_direction = MV_DIR_OPPOSITE(direction);
9725     int hitting_side = direction;
9726     int touched_side = opposite_direction;
9727     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9728                           MovDir[hitx][hity] != direction ||
9729                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9730
9731     object_hit = TRUE;
9732
9733     if (object_hit)
9734     {
9735       CheckElementChangeBySide(x, y, hitting_element, touched_element,
9736                                CE_HITTING_X, touched_side);
9737
9738       CheckElementChangeBySide(hitx, hity, touched_element,
9739                                hitting_element, CE_HIT_BY_X, hitting_side);
9740
9741       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9742                                CE_HIT_BY_SOMETHING, opposite_direction);
9743     }
9744   }
9745
9746   /* "hitting something" is also true when hitting the playfield border */
9747   CheckElementChangeBySide(x, y, hitting_element, touched_element,
9748                            CE_HITTING_SOMETHING, direction);
9749 }
9750
9751 #if 0
9752 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9753 {
9754   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9755   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9756   int hitx = x + dx, hity = y + dy;
9757   int hitting_element = Feld[x][y];
9758   int touched_element;
9759 #if 0
9760   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9761                         !IS_FREE(hitx, hity) &&
9762                         (!IS_MOVING(hitx, hity) ||
9763                          MovDir[hitx][hity] != direction ||
9764                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9765 #endif
9766
9767   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9768     return;
9769
9770 #if 0
9771   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9772     return;
9773 #endif
9774
9775   touched_element = (IN_LEV_FIELD(hitx, hity) ?
9776                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9777
9778   CheckElementChangeBySide(x, y, hitting_element, touched_element,
9779                            EP_CAN_SMASH_EVERYTHING, direction);
9780
9781   if (IN_LEV_FIELD(hitx, hity))
9782   {
9783     int opposite_direction = MV_DIR_OPPOSITE(direction);
9784     int hitting_side = direction;
9785     int touched_side = opposite_direction;
9786 #if 0
9787     int touched_element = MovingOrBlocked2Element(hitx, hity);
9788 #endif
9789 #if 1
9790     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9791                           MovDir[hitx][hity] != direction ||
9792                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9793
9794     object_hit = TRUE;
9795 #endif
9796
9797     if (object_hit)
9798     {
9799       int i;
9800
9801       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9802                                CE_SMASHED_BY_SOMETHING, opposite_direction);
9803
9804       CheckElementChangeBySide(x, y, hitting_element, touched_element,
9805                                CE_OTHER_IS_SMASHING, touched_side);
9806
9807       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9808                                CE_OTHER_GETS_SMASHED, hitting_side);
9809     }
9810   }
9811 }
9812 #endif
9813
9814 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9815 {
9816   int i, kill_x = -1, kill_y = -1;
9817   int bad_element = -1;
9818   static int test_xy[4][2] =
9819   {
9820     { 0, -1 },
9821     { -1, 0 },
9822     { +1, 0 },
9823     { 0, +1 }
9824   };
9825   static int test_dir[4] =
9826   {
9827     MV_UP,
9828     MV_LEFT,
9829     MV_RIGHT,
9830     MV_DOWN
9831   };
9832
9833   for (i = 0; i < NUM_DIRECTIONS; i++)
9834   {
9835     int test_x, test_y, test_move_dir, test_element;
9836
9837     test_x = good_x + test_xy[i][0];
9838     test_y = good_y + test_xy[i][1];
9839
9840     if (!IN_LEV_FIELD(test_x, test_y))
9841       continue;
9842
9843     test_move_dir =
9844       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9845
9846     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9847
9848     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9849        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9850     */
9851     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9852         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
9853     {
9854       kill_x = test_x;
9855       kill_y = test_y;
9856       bad_element = test_element;
9857
9858       break;
9859     }
9860   }
9861
9862   if (kill_x != -1 || kill_y != -1)
9863   {
9864     if (IS_PLAYER(good_x, good_y))
9865     {
9866       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9867
9868       if (player->shield_deadly_time_left > 0 &&
9869           !IS_INDESTRUCTIBLE(bad_element))
9870         Bang(kill_x, kill_y);
9871       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9872         KillPlayer(player);
9873     }
9874     else
9875       Bang(good_x, good_y);
9876   }
9877 }
9878
9879 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9880 {
9881   int i, kill_x = -1, kill_y = -1;
9882   int bad_element = Feld[bad_x][bad_y];
9883   static int test_xy[4][2] =
9884   {
9885     { 0, -1 },
9886     { -1, 0 },
9887     { +1, 0 },
9888     { 0, +1 }
9889   };
9890   static int touch_dir[4] =
9891   {
9892     MV_LEFT | MV_RIGHT,
9893     MV_UP   | MV_DOWN,
9894     MV_UP   | MV_DOWN,
9895     MV_LEFT | MV_RIGHT
9896   };
9897   static int test_dir[4] =
9898   {
9899     MV_UP,
9900     MV_LEFT,
9901     MV_RIGHT,
9902     MV_DOWN
9903   };
9904
9905   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
9906     return;
9907
9908   for (i = 0; i < NUM_DIRECTIONS; i++)
9909   {
9910     int test_x, test_y, test_move_dir, test_element;
9911
9912     test_x = bad_x + test_xy[i][0];
9913     test_y = bad_y + test_xy[i][1];
9914     if (!IN_LEV_FIELD(test_x, test_y))
9915       continue;
9916
9917     test_move_dir =
9918       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
9919
9920     test_element = Feld[test_x][test_y];
9921
9922     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9923        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9924     */
9925     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
9926         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
9927     {
9928       /* good thing is player or penguin that does not move away */
9929       if (IS_PLAYER(test_x, test_y))
9930       {
9931         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9932
9933         if (bad_element == EL_ROBOT && player->is_moving)
9934           continue;     /* robot does not kill player if he is moving */
9935
9936         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9937         {
9938           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9939             continue;           /* center and border element do not touch */
9940         }
9941
9942         kill_x = test_x;
9943         kill_y = test_y;
9944         break;
9945       }
9946       else if (test_element == EL_PENGUIN)
9947       {
9948         kill_x = test_x;
9949         kill_y = test_y;
9950         break;
9951       }
9952     }
9953   }
9954
9955   if (kill_x != -1 || kill_y != -1)
9956   {
9957     if (IS_PLAYER(kill_x, kill_y))
9958     {
9959       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9960
9961       if (player->shield_deadly_time_left > 0 &&
9962           !IS_INDESTRUCTIBLE(bad_element))
9963         Bang(bad_x, bad_y);
9964       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9965         KillPlayer(player);
9966     }
9967     else
9968       Bang(kill_x, kill_y);
9969   }
9970 }
9971
9972 void TestIfPlayerTouchesBadThing(int x, int y)
9973 {
9974   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9975 }
9976
9977 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9978 {
9979   TestIfGoodThingHitsBadThing(x, y, move_dir);
9980 }
9981
9982 void TestIfBadThingTouchesPlayer(int x, int y)
9983 {
9984   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
9985 }
9986
9987 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9988 {
9989   TestIfBadThingHitsGoodThing(x, y, move_dir);
9990 }
9991
9992 void TestIfFriendTouchesBadThing(int x, int y)
9993 {
9994   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
9995 }
9996
9997 void TestIfBadThingTouchesFriend(int x, int y)
9998 {
9999   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10000 }
10001
10002 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10003 {
10004   int i, kill_x = bad_x, kill_y = bad_y;
10005   static int xy[4][2] =
10006   {
10007     { 0, -1 },
10008     { -1, 0 },
10009     { +1, 0 },
10010     { 0, +1 }
10011   };
10012
10013   for (i = 0; i < NUM_DIRECTIONS; i++)
10014   {
10015     int x, y, element;
10016
10017     x = bad_x + xy[i][0];
10018     y = bad_y + xy[i][1];
10019     if (!IN_LEV_FIELD(x, y))
10020       continue;
10021
10022     element = Feld[x][y];
10023     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10024         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10025     {
10026       kill_x = x;
10027       kill_y = y;
10028       break;
10029     }
10030   }
10031
10032   if (kill_x != bad_x || kill_y != bad_y)
10033     Bang(bad_x, bad_y);
10034 }
10035
10036 void KillPlayer(struct PlayerInfo *player)
10037 {
10038   int jx = player->jx, jy = player->jy;
10039
10040   if (!player->active)
10041     return;
10042
10043   /* remove accessible field at the player's position */
10044   Feld[jx][jy] = EL_EMPTY;
10045
10046   /* deactivate shield (else Bang()/Explode() would not work right) */
10047   player->shield_normal_time_left = 0;
10048   player->shield_deadly_time_left = 0;
10049
10050   Bang(jx, jy);
10051   BuryPlayer(player);
10052 }
10053
10054 static void KillPlayerUnlessEnemyProtected(int x, int y)
10055 {
10056   if (!PLAYER_ENEMY_PROTECTED(x, y))
10057     KillPlayer(PLAYERINFO(x, y));
10058 }
10059
10060 static void KillPlayerUnlessExplosionProtected(int x, int y)
10061 {
10062   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10063     KillPlayer(PLAYERINFO(x, y));
10064 }
10065
10066 void BuryPlayer(struct PlayerInfo *player)
10067 {
10068   int jx = player->jx, jy = player->jy;
10069
10070   if (!player->active)
10071     return;
10072
10073   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10074   PlayLevelSound(jx, jy, SND_GAME_LOSING);
10075
10076   player->GameOver = TRUE;
10077   RemovePlayer(player);
10078 }
10079
10080 void RemovePlayer(struct PlayerInfo *player)
10081 {
10082   int jx = player->jx, jy = player->jy;
10083   int i, found = FALSE;
10084
10085   player->present = FALSE;
10086   player->active = FALSE;
10087
10088   if (!ExplodeField[jx][jy])
10089     StorePlayer[jx][jy] = 0;
10090
10091   if (player->is_moving)
10092     DrawLevelField(player->last_jx, player->last_jy);
10093
10094   for (i = 0; i < MAX_PLAYERS; i++)
10095     if (stored_player[i].active)
10096       found = TRUE;
10097
10098   if (!found)
10099     AllPlayersGone = TRUE;
10100
10101   ExitX = ZX = jx;
10102   ExitY = ZY = jy;
10103 }
10104
10105 #if USE_NEW_SNAP_DELAY
10106 static void setFieldForSnapping(int x, int y, int element, int direction)
10107 {
10108   struct ElementInfo *ei = &element_info[element];
10109   int direction_bit = MV_DIR_BIT(direction);
10110   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10111   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10112                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10113
10114   Feld[x][y] = EL_ELEMENT_SNAPPING;
10115   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10116
10117   ResetGfxAnimation(x, y);
10118
10119   GfxElement[x][y] = element;
10120   GfxAction[x][y] = action;
10121   GfxDir[x][y] = direction;
10122   GfxFrame[x][y] = -1;
10123 }
10124 #endif
10125
10126 /*
10127   =============================================================================
10128   checkDiagonalPushing()
10129   -----------------------------------------------------------------------------
10130   check if diagonal input device direction results in pushing of object
10131   (by checking if the alternative direction is walkable, diggable, ...)
10132   =============================================================================
10133 */
10134
10135 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10136                                     int x, int y, int real_dx, int real_dy)
10137 {
10138   int jx, jy, dx, dy, xx, yy;
10139
10140   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
10141     return TRUE;
10142
10143   /* diagonal direction: check alternative direction */
10144   jx = player->jx;
10145   jy = player->jy;
10146   dx = x - jx;
10147   dy = y - jy;
10148   xx = jx + (dx == 0 ? real_dx : 0);
10149   yy = jy + (dy == 0 ? real_dy : 0);
10150
10151   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10152 }
10153
10154 /*
10155   =============================================================================
10156   DigField()
10157   -----------------------------------------------------------------------------
10158   x, y:                 field next to player (non-diagonal) to try to dig to
10159   real_dx, real_dy:     direction as read from input device (can be diagonal)
10160   =============================================================================
10161 */
10162
10163 int DigField(struct PlayerInfo *player,
10164              int oldx, int oldy, int x, int y,
10165              int real_dx, int real_dy, int mode)
10166 {
10167   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10168   boolean player_was_pushing = player->is_pushing;
10169   int jx = oldx, jy = oldy;
10170   int dx = x - jx, dy = y - jy;
10171   int nextx = x + dx, nexty = y + dy;
10172   int move_direction = (dx == -1 ? MV_LEFT  :
10173                         dx == +1 ? MV_RIGHT :
10174                         dy == -1 ? MV_UP    :
10175                         dy == +1 ? MV_DOWN  : MV_NONE);
10176   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10177   int dig_side = MV_DIR_OPPOSITE(move_direction);
10178   int old_element = Feld[jx][jy];
10179   int element;
10180   int collect_count;
10181
10182   if (is_player)                /* function can also be called by EL_PENGUIN */
10183   {
10184     if (player->MovPos == 0)
10185     {
10186       player->is_digging = FALSE;
10187       player->is_collecting = FALSE;
10188     }
10189
10190     if (player->MovPos == 0)    /* last pushing move finished */
10191       player->is_pushing = FALSE;
10192
10193     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
10194     {
10195       player->is_switching = FALSE;
10196       player->push_delay = -1;
10197
10198       return MF_NO_ACTION;
10199     }
10200   }
10201
10202   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10203     return MF_NO_ACTION;
10204
10205   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10206     old_element = Back[jx][jy];
10207
10208   /* in case of element dropped at player position, check background */
10209   else if (Back[jx][jy] != EL_EMPTY &&
10210            game.engine_version >= VERSION_IDENT(2,2,0,0))
10211     old_element = Back[jx][jy];
10212
10213   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10214     return MF_NO_ACTION;        /* field has no opening in this direction */
10215
10216   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10217     return MF_NO_ACTION;        /* field has no opening in this direction */
10218
10219   element = Feld[x][y];
10220 #if USE_NEW_CUSTOM_VALUE
10221
10222 #if 1
10223   collect_count = element_info[element].collect_count_initial;
10224 #else
10225   collect_count = CustomValue[x][y];
10226 #endif
10227
10228 #else
10229   collect_count = element_info[element].collect_count_initial;
10230 #endif
10231
10232 #if 0
10233   if (element != EL_BLOCKED &&
10234       CustomValue[x][y] != element_info[element].collect_count_initial)
10235     printf("::: %d: %d != %d\n",
10236            element,
10237            CustomValue[x][y],
10238            element_info[element].collect_count_initial);
10239 #endif
10240
10241   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
10242     return MF_NO_ACTION;
10243
10244   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10245       game.engine_version >= VERSION_IDENT(2,2,0,0))
10246   {
10247     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10248                                player->index_bit, dig_side);
10249     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10250                                         player->index_bit, dig_side);
10251
10252     if (Feld[x][y] != element)          /* field changed by snapping */
10253       return MF_ACTION;
10254
10255     return MF_NO_ACTION;
10256   }
10257
10258   if (game.gravity && is_player && !player->is_auto_moving &&
10259       canFallDown(player) && move_direction != MV_DOWN &&
10260       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10261     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
10262
10263   if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10264   {
10265     int sound_element = SND_ELEMENT(element);
10266     int sound_action = ACTION_WALKING;
10267
10268     if (IS_RND_GATE(element))
10269     {
10270       if (!player->key[RND_GATE_NR(element)])
10271         return MF_NO_ACTION;
10272     }
10273     else if (IS_RND_GATE_GRAY(element))
10274     {
10275       if (!player->key[RND_GATE_GRAY_NR(element)])
10276         return MF_NO_ACTION;
10277     }
10278     else if (IS_RND_GATE_GRAY_ACTIVE(element))
10279     {
10280       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10281         return MF_NO_ACTION;
10282     }
10283     else if (element == EL_EXIT_OPEN ||
10284              element == EL_SP_EXIT_OPEN ||
10285              element == EL_SP_EXIT_OPENING)
10286     {
10287       sound_action = ACTION_PASSING;    /* player is passing exit */
10288     }
10289     else if (element == EL_EMPTY)
10290     {
10291       sound_action = ACTION_MOVING;             /* nothing to walk on */
10292     }
10293
10294     /* play sound from background or player, whatever is available */
10295     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10296       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10297     else
10298       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10299   }
10300   else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10301   {
10302     if (!ACCESS_FROM(element, opposite_direction))
10303       return MF_NO_ACTION;      /* field not accessible from this direction */
10304
10305     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
10306       return MF_NO_ACTION;
10307
10308     if (IS_EM_GATE(element))
10309     {
10310       if (!player->key[EM_GATE_NR(element)])
10311         return MF_NO_ACTION;
10312     }
10313     else if (IS_EM_GATE_GRAY(element))
10314     {
10315       if (!player->key[EM_GATE_GRAY_NR(element)])
10316         return MF_NO_ACTION;
10317     }
10318     else if (IS_EM_GATE_GRAY_ACTIVE(element))
10319     {
10320       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10321         return MF_NO_ACTION;
10322     }
10323     else if (IS_SP_PORT(element))
10324     {
10325       if (element == EL_SP_GRAVITY_PORT_LEFT ||
10326           element == EL_SP_GRAVITY_PORT_RIGHT ||
10327           element == EL_SP_GRAVITY_PORT_UP ||
10328           element == EL_SP_GRAVITY_PORT_DOWN)
10329         game.gravity = !game.gravity;
10330       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10331                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10332                element == EL_SP_GRAVITY_ON_PORT_UP ||
10333                element == EL_SP_GRAVITY_ON_PORT_DOWN)
10334         game.gravity = TRUE;
10335       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10336                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10337                element == EL_SP_GRAVITY_OFF_PORT_UP ||
10338                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10339         game.gravity = FALSE;
10340     }
10341
10342     /* automatically move to the next field with double speed */
10343     player->programmed_action = move_direction;
10344
10345     if (player->move_delay_reset_counter == 0)
10346     {
10347       player->move_delay_reset_counter = 2;     /* two double speed steps */
10348
10349       DOUBLE_PLAYER_SPEED(player);
10350     }
10351
10352     PlayLevelSoundAction(x, y, ACTION_PASSING);
10353   }
10354   else if (IS_DIGGABLE(element))
10355   {
10356     RemoveField(x, y);
10357
10358     if (mode != DF_SNAP)
10359     {
10360       GfxElement[x][y] = GFX_ELEMENT(element);
10361       player->is_digging = TRUE;
10362     }
10363
10364     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10365
10366     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10367                                         player->index_bit, dig_side);
10368
10369     if (mode == DF_SNAP)
10370     {
10371 #if USE_NEW_SNAP_DELAY
10372       if (level.block_snap_field)
10373         setFieldForSnapping(x, y, element, move_direction);
10374       else
10375         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10376 #else
10377       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
10378 #endif
10379
10380       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10381                                           player->index_bit, dig_side);
10382     }
10383   }
10384   else if (IS_COLLECTIBLE(element))
10385   {
10386     RemoveField(x, y);
10387
10388     if (is_player && mode != DF_SNAP)
10389     {
10390       GfxElement[x][y] = element;
10391       player->is_collecting = TRUE;
10392     }
10393
10394     if (element == EL_SPEED_PILL)
10395     {
10396       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10397     }
10398     else if (element == EL_EXTRA_TIME && level.time > 0)
10399     {
10400       TimeLeft += level.extra_time;
10401       DrawGameValue_Time(TimeLeft);
10402     }
10403     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10404     {
10405       player->shield_normal_time_left += level.shield_normal_time;
10406       if (element == EL_SHIELD_DEADLY)
10407         player->shield_deadly_time_left += level.shield_deadly_time;
10408     }
10409     else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10410     {
10411       if (player->inventory_size < MAX_INVENTORY_SIZE)
10412         player->inventory_element[player->inventory_size++] = element;
10413
10414       DrawGameValue_Dynamite(local_player->inventory_size);
10415     }
10416     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10417     {
10418       player->dynabomb_count++;
10419       player->dynabombs_left++;
10420     }
10421     else if (element == EL_DYNABOMB_INCREASE_SIZE)
10422     {
10423       player->dynabomb_size++;
10424     }
10425     else if (element == EL_DYNABOMB_INCREASE_POWER)
10426     {
10427       player->dynabomb_xl = TRUE;
10428     }
10429     else if (IS_KEY(element))
10430     {
10431       player->key[KEY_NR(element)] = TRUE;
10432
10433       DrawGameValue_Keys(player->key);
10434
10435       redraw_mask |= REDRAW_DOOR_1;
10436     }
10437     else if (IS_ENVELOPE(element))
10438     {
10439       player->show_envelope = element;
10440     }
10441     else if (element == EL_EMC_LENSES)
10442     {
10443       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10444
10445       RedrawAllInvisibleElementsForLenses();
10446     }
10447     else if (element == EL_EMC_MAGNIFIER)
10448     {
10449       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10450
10451       RedrawAllInvisibleElementsForMagnifier();
10452     }
10453     else if (IS_DROPPABLE(element) ||
10454              IS_THROWABLE(element))     /* can be collected and dropped */
10455     {
10456       int i;
10457
10458       if (collect_count == 0)
10459         player->inventory_infinite_element = element;
10460       else
10461         for (i = 0; i < collect_count; i++)
10462           if (player->inventory_size < MAX_INVENTORY_SIZE)
10463             player->inventory_element[player->inventory_size++] = element;
10464
10465       DrawGameValue_Dynamite(local_player->inventory_size);
10466     }
10467     else if (collect_count > 0)
10468     {
10469       local_player->gems_still_needed -= collect_count;
10470       if (local_player->gems_still_needed < 0)
10471         local_player->gems_still_needed = 0;
10472
10473       DrawGameValue_Emeralds(local_player->gems_still_needed);
10474     }
10475
10476     RaiseScoreElement(element);
10477     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10478
10479     if (is_player)
10480       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10481                                           player->index_bit, dig_side);
10482
10483     if (mode == DF_SNAP)
10484     {
10485 #if USE_NEW_SNAP_DELAY
10486       if (level.block_snap_field)
10487         setFieldForSnapping(x, y, element, move_direction);
10488       else
10489         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10490 #else
10491       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
10492 #endif
10493
10494       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10495                                           player->index_bit, dig_side);
10496     }
10497   }
10498   else if (IS_PUSHABLE(element))
10499   {
10500     if (mode == DF_SNAP && element != EL_BD_ROCK)
10501       return MF_NO_ACTION;
10502
10503     if (CAN_FALL(element) && dy)
10504       return MF_NO_ACTION;
10505
10506     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
10507         !(element == EL_SPRING && level.use_spring_bug))
10508       return MF_NO_ACTION;
10509
10510     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
10511         ((move_direction & MV_VERTICAL &&
10512           ((element_info[element].move_pattern & MV_LEFT &&
10513             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
10514            (element_info[element].move_pattern & MV_RIGHT &&
10515             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
10516          (move_direction & MV_HORIZONTAL &&
10517           ((element_info[element].move_pattern & MV_UP &&
10518             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
10519            (element_info[element].move_pattern & MV_DOWN &&
10520             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
10521       return MF_NO_ACTION;
10522
10523     /* do not push elements already moving away faster than player */
10524     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
10525         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
10526       return MF_NO_ACTION;
10527
10528     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10529     {
10530       if (player->push_delay_value == -1 || !player_was_pushing)
10531         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10532     }
10533     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10534     {
10535       if (player->push_delay_value == -1)
10536         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10537     }
10538     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
10539     {
10540       if (!player->is_pushing)
10541         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10542     }
10543
10544     player->is_pushing = TRUE;
10545
10546     if (!(IN_LEV_FIELD(nextx, nexty) &&
10547           (IS_FREE(nextx, nexty) ||
10548            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
10549             IS_SB_ELEMENT(element)))))
10550       return MF_NO_ACTION;
10551
10552     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
10553       return MF_NO_ACTION;
10554
10555     if (player->push_delay == -1)       /* new pushing; restart delay */
10556       player->push_delay = 0;
10557
10558     if (player->push_delay < player->push_delay_value &&
10559         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
10560         element != EL_SPRING && element != EL_BALLOON)
10561     {
10562       /* make sure that there is no move delay before next try to push */
10563       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
10564         player->move_delay = 0;
10565
10566       return MF_NO_ACTION;
10567     }
10568
10569     if (IS_SB_ELEMENT(element))
10570     {
10571       if (element == EL_SOKOBAN_FIELD_FULL)
10572       {
10573         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
10574         local_player->sokobanfields_still_needed++;
10575       }
10576
10577       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
10578       {
10579         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
10580         local_player->sokobanfields_still_needed--;
10581       }
10582
10583       Feld[x][y] = EL_SOKOBAN_OBJECT;
10584
10585       if (Back[x][y] == Back[nextx][nexty])
10586         PlayLevelSoundAction(x, y, ACTION_PUSHING);
10587       else if (Back[x][y] != 0)
10588         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
10589                                     ACTION_EMPTYING);
10590       else
10591         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
10592                                     ACTION_FILLING);
10593
10594       if (local_player->sokobanfields_still_needed == 0 &&
10595           game.emulation == EMU_SOKOBAN)
10596       {
10597         player->LevelSolved = player->GameOver = TRUE;
10598         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
10599       }
10600     }
10601     else
10602       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10603
10604     InitMovingField(x, y, move_direction);
10605     GfxAction[x][y] = ACTION_PUSHING;
10606
10607     if (mode == DF_SNAP)
10608       ContinueMoving(x, y);
10609     else
10610       MovPos[x][y] = (dx != 0 ? dx : dy);
10611
10612     Pushed[x][y] = TRUE;
10613     Pushed[nextx][nexty] = TRUE;
10614
10615     if (game.engine_version < VERSION_IDENT(2,2,0,7))
10616       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
10617     else
10618       player->push_delay_value = -1;    /* get new value later */
10619
10620     /* check for element change _after_ element has been pushed */
10621     if (game.use_change_when_pushing_bug)
10622     {
10623       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
10624                                  player->index_bit, dig_side);
10625       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
10626                                           player->index_bit, dig_side);
10627     }
10628   }
10629   else if (IS_SWITCHABLE(element))
10630   {
10631     if (PLAYER_SWITCHING(player, x, y))
10632     {
10633       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10634                                           player->index_bit, dig_side);
10635
10636       return MF_ACTION;
10637     }
10638
10639     player->is_switching = TRUE;
10640     player->switch_x = x;
10641     player->switch_y = y;
10642
10643     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10644
10645     if (element == EL_ROBOT_WHEEL)
10646     {
10647       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
10648       ZX = x;
10649       ZY = y;
10650
10651       DrawLevelField(x, y);
10652     }
10653     else if (element == EL_SP_TERMINAL)
10654     {
10655       int xx, yy;
10656
10657 #if 1
10658       SCAN_PLAYFIELD(xx, yy)
10659 #else
10660       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
10661 #endif
10662       {
10663         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
10664           Bang(xx, yy);
10665         else if (Feld[xx][yy] == EL_SP_TERMINAL)
10666           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
10667       }
10668     }
10669     else if (IS_BELT_SWITCH(element))
10670     {
10671       ToggleBeltSwitch(x, y);
10672     }
10673     else if (element == EL_SWITCHGATE_SWITCH_UP ||
10674              element == EL_SWITCHGATE_SWITCH_DOWN)
10675     {
10676       ToggleSwitchgateSwitch(x, y);
10677     }
10678     else if (element == EL_LIGHT_SWITCH ||
10679              element == EL_LIGHT_SWITCH_ACTIVE)
10680     {
10681       ToggleLightSwitch(x, y);
10682     }
10683     else if (element == EL_TIMEGATE_SWITCH)
10684     {
10685       ActivateTimegateSwitch(x, y);
10686     }
10687     else if (element == EL_BALLOON_SWITCH_LEFT  ||
10688              element == EL_BALLOON_SWITCH_RIGHT ||
10689              element == EL_BALLOON_SWITCH_UP    ||
10690              element == EL_BALLOON_SWITCH_DOWN  ||
10691              element == EL_BALLOON_SWITCH_NONE  ||
10692              element == EL_BALLOON_SWITCH_ANY)
10693     {
10694       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
10695                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
10696                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
10697                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
10698                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
10699                              move_direction);
10700     }
10701     else if (element == EL_LAMP)
10702     {
10703       Feld[x][y] = EL_LAMP_ACTIVE;
10704       local_player->lights_still_needed--;
10705
10706       ResetGfxAnimation(x, y);
10707       DrawLevelField(x, y);
10708     }
10709     else if (element == EL_TIME_ORB_FULL)
10710     {
10711       Feld[x][y] = EL_TIME_ORB_EMPTY;
10712
10713       if (level.time > 0 || level.use_time_orb_bug)
10714       {
10715         TimeLeft += level.time_orb_time;
10716         DrawGameValue_Time(TimeLeft);
10717       }
10718
10719       ResetGfxAnimation(x, y);
10720       DrawLevelField(x, y);
10721     }
10722     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
10723              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
10724     {
10725       int xx, yy;
10726
10727       game.ball_state = !game.ball_state;
10728
10729 #if 1
10730       SCAN_PLAYFIELD(xx, yy)
10731 #else
10732       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
10733 #endif
10734       {
10735         int e = Feld[xx][yy];
10736
10737         if (game.ball_state)
10738         {
10739           if (e == EL_EMC_MAGIC_BALL)
10740             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
10741           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
10742             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
10743         }
10744         else
10745         {
10746           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
10747             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
10748           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
10749             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
10750         }
10751       }
10752     }
10753
10754     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10755                                         player->index_bit, dig_side);
10756
10757     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10758                                         player->index_bit, dig_side);
10759
10760     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10761                                         player->index_bit, dig_side);
10762
10763     return MF_ACTION;
10764   }
10765   else
10766   {
10767     if (!PLAYER_SWITCHING(player, x, y))
10768     {
10769       player->is_switching = TRUE;
10770       player->switch_x = x;
10771       player->switch_y = y;
10772
10773       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
10774                                  player->index_bit, dig_side);
10775       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
10776                                           player->index_bit, dig_side);
10777
10778       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
10779                                  player->index_bit, dig_side);
10780       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
10781                                           player->index_bit, dig_side);
10782     }
10783
10784     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
10785                                player->index_bit, dig_side);
10786     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
10787                                         player->index_bit, dig_side);
10788
10789     return MF_NO_ACTION;
10790   }
10791
10792   player->push_delay = -1;
10793
10794   if (is_player)                /* function can also be called by EL_PENGUIN */
10795   {
10796     if (Feld[x][y] != element)          /* really digged/collected something */
10797       player->is_collecting = !player->is_digging;
10798   }
10799
10800   return MF_MOVING;
10801 }
10802
10803 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
10804 {
10805   int jx = player->jx, jy = player->jy;
10806   int x = jx + dx, y = jy + dy;
10807   int snap_direction = (dx == -1 ? MV_LEFT  :
10808                         dx == +1 ? MV_RIGHT :
10809                         dy == -1 ? MV_UP    :
10810                         dy == +1 ? MV_DOWN  : MV_NONE);
10811
10812   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
10813     return FALSE;
10814
10815   if (!player->active || !IN_LEV_FIELD(x, y))
10816     return FALSE;
10817
10818   if (dx && dy)
10819     return FALSE;
10820
10821   if (!dx && !dy)
10822   {
10823     if (player->MovPos == 0)
10824       player->is_pushing = FALSE;
10825
10826     player->is_snapping = FALSE;
10827
10828     if (player->MovPos == 0)
10829     {
10830       player->is_moving = FALSE;
10831       player->is_digging = FALSE;
10832       player->is_collecting = FALSE;
10833     }
10834
10835     return FALSE;
10836   }
10837
10838   if (player->is_snapping)
10839     return FALSE;
10840
10841   player->MovDir = snap_direction;
10842
10843   if (player->MovPos == 0)
10844   {
10845     player->is_moving = FALSE;
10846     player->is_digging = FALSE;
10847     player->is_collecting = FALSE;
10848   }
10849
10850   player->is_dropping = FALSE;
10851
10852   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10853     return FALSE;
10854
10855   player->is_snapping = TRUE;
10856
10857   if (player->MovPos == 0)
10858   {
10859     player->is_moving = FALSE;
10860     player->is_digging = FALSE;
10861     player->is_collecting = FALSE;
10862   }
10863
10864   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
10865     DrawLevelField(player->last_jx, player->last_jy);
10866
10867   DrawLevelField(x, y);
10868
10869   return TRUE;
10870 }
10871
10872 boolean DropElement(struct PlayerInfo *player)
10873 {
10874   int old_element, new_element;
10875   int dropx = player->jx, dropy = player->jy;
10876   int drop_direction = player->MovDir;
10877   int drop_side = drop_direction;
10878   int drop_element = (player->inventory_size > 0 ?
10879                       player->inventory_element[player->inventory_size - 1] :
10880                       player->inventory_infinite_element != EL_UNDEFINED ?
10881                       player->inventory_infinite_element :
10882                       player->dynabombs_left > 0 ?
10883                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10884                       EL_UNDEFINED);
10885
10886   /* do not drop an element on top of another element; when holding drop key
10887      pressed without moving, dropped element must move away before the next
10888      element can be dropped (this is especially important if the next element
10889      is dynamite, which can be placed on background for historical reasons) */
10890   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10891     return MF_ACTION;
10892
10893   if (IS_THROWABLE(drop_element))
10894   {
10895     dropx += GET_DX_FROM_DIR(drop_direction);
10896     dropy += GET_DY_FROM_DIR(drop_direction);
10897
10898     if (!IN_LEV_FIELD(dropx, dropy))
10899       return FALSE;
10900   }
10901
10902   old_element = Feld[dropx][dropy];     /* old element at dropping position */
10903   new_element = drop_element;           /* default: no change when dropping */
10904
10905   /* check if player is active, not moving and ready to drop */
10906   if (!player->active || player->MovPos || player->drop_delay > 0)
10907     return FALSE;
10908
10909   /* check if player has anything that can be dropped */
10910   if (new_element == EL_UNDEFINED)
10911     return FALSE;
10912
10913   /* check if anything can be dropped at the current position */
10914   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10915     return FALSE;
10916
10917   /* collected custom elements can only be dropped on empty fields */
10918   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10919     return FALSE;
10920
10921   if (old_element != EL_EMPTY)
10922     Back[dropx][dropy] = old_element;   /* store old element on this field */
10923
10924   ResetGfxAnimation(dropx, dropy);
10925   ResetRandomAnimationValue(dropx, dropy);
10926
10927   if (player->inventory_size > 0 ||
10928       player->inventory_infinite_element != EL_UNDEFINED)
10929   {
10930     if (player->inventory_size > 0)
10931     {
10932       player->inventory_size--;
10933
10934       DrawGameValue_Dynamite(local_player->inventory_size);
10935
10936       if (new_element == EL_DYNAMITE)
10937         new_element = EL_DYNAMITE_ACTIVE;
10938       else if (new_element == EL_SP_DISK_RED)
10939         new_element = EL_SP_DISK_RED_ACTIVE;
10940     }
10941
10942     Feld[dropx][dropy] = new_element;
10943
10944     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10945       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10946                           el2img(Feld[dropx][dropy]), 0);
10947
10948     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10949
10950     /* needed if previous element just changed to "empty" in the last frame */
10951     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
10952
10953     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10954                                player->index_bit, drop_side);
10955     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
10956                                         CE_PLAYER_DROPS_X,
10957                                         player->index_bit, drop_side);
10958
10959     TestIfElementTouchesCustomElement(dropx, dropy);
10960   }
10961   else          /* player is dropping a dyna bomb */
10962   {
10963     player->dynabombs_left--;
10964
10965     Feld[dropx][dropy] = new_element;
10966
10967     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10968       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10969                           el2img(Feld[dropx][dropy]), 0);
10970
10971     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10972   }
10973
10974   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10975     InitField_WithBug1(dropx, dropy, FALSE);
10976
10977   new_element = Feld[dropx][dropy];     /* element might have changed */
10978
10979   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10980       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10981   {
10982     int move_direction, nextx, nexty;
10983
10984     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10985       MovDir[dropx][dropy] = drop_direction;
10986
10987     move_direction = MovDir[dropx][dropy];
10988     nextx = dropx + GET_DX_FROM_DIR(move_direction);
10989     nexty = dropy + GET_DY_FROM_DIR(move_direction);
10990
10991     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
10992     CheckCollision[dropx][dropy] = 2;
10993   }
10994
10995   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10996   player->is_dropping = TRUE;
10997
10998   player->drop_x = dropx;
10999   player->drop_y = dropy;
11000
11001   return TRUE;
11002 }
11003
11004 /* ------------------------------------------------------------------------- */
11005 /* game sound playing functions                                              */
11006 /* ------------------------------------------------------------------------- */
11007
11008 static int *loop_sound_frame = NULL;
11009 static int *loop_sound_volume = NULL;
11010
11011 void InitPlayLevelSound()
11012 {
11013   int num_sounds = getSoundListSize();
11014
11015   checked_free(loop_sound_frame);
11016   checked_free(loop_sound_volume);
11017
11018   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
11019   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11020 }
11021
11022 static void PlayLevelSound(int x, int y, int nr)
11023 {
11024   int sx = SCREENX(x), sy = SCREENY(y);
11025   int volume, stereo_position;
11026   int max_distance = 8;
11027   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11028
11029   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11030       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11031     return;
11032
11033   if (!IN_LEV_FIELD(x, y) ||
11034       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11035       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11036     return;
11037
11038   volume = SOUND_MAX_VOLUME;
11039
11040   if (!IN_SCR_FIELD(sx, sy))
11041   {
11042     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11043     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11044
11045     volume -= volume * (dx > dy ? dx : dy) / max_distance;
11046   }
11047
11048   stereo_position = (SOUND_MAX_LEFT +
11049                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11050                      (SCR_FIELDX + 2 * max_distance));
11051
11052   if (IS_LOOP_SOUND(nr))
11053   {
11054     /* This assures that quieter loop sounds do not overwrite louder ones,
11055        while restarting sound volume comparison with each new game frame. */
11056
11057     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11058       return;
11059
11060     loop_sound_volume[nr] = volume;
11061     loop_sound_frame[nr] = FrameCounter;
11062   }
11063
11064   PlaySoundExt(nr, volume, stereo_position, type);
11065 }
11066
11067 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11068 {
11069   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11070                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
11071                  y < LEVELY(BY1) ? LEVELY(BY1) :
11072                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
11073                  sound_action);
11074 }
11075
11076 static void PlayLevelSoundAction(int x, int y, int action)
11077 {
11078   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11079 }
11080
11081 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11082 {
11083   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11084
11085   if (sound_effect != SND_UNDEFINED)
11086     PlayLevelSound(x, y, sound_effect);
11087 }
11088
11089 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11090                                               int action)
11091 {
11092   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11093
11094   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11095     PlayLevelSound(x, y, sound_effect);
11096 }
11097
11098 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11099 {
11100   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11101
11102   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11103     PlayLevelSound(x, y, sound_effect);
11104 }
11105
11106 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11107 {
11108   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11109
11110   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11111     StopSound(sound_effect);
11112 }
11113
11114 static void PlayLevelMusic()
11115 {
11116   if (levelset.music[level_nr] != MUS_UNDEFINED)
11117     PlayMusic(levelset.music[level_nr]);        /* from config file */
11118   else
11119     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
11120 }
11121
11122 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11123 {
11124   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11125
11126   switch (sample)
11127   {
11128     case SAMPLE_blank:
11129       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11130       break;
11131
11132     case SAMPLE_roll:
11133       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11134       break;
11135
11136     case SAMPLE_stone:
11137       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11138       break;
11139
11140     case SAMPLE_nut:
11141       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11142       break;
11143
11144     case SAMPLE_crack:
11145       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11146       break;
11147
11148     case SAMPLE_bug:
11149       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11150       break;
11151
11152     case SAMPLE_tank:
11153       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11154       break;
11155
11156     case SAMPLE_android_clone:
11157       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11158       break;
11159
11160     case SAMPLE_android_move:
11161       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11162       break;
11163
11164     case SAMPLE_spring:
11165       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11166       break;
11167
11168     case SAMPLE_slurp:
11169       PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
11170       break;
11171
11172     case SAMPLE_eater:
11173       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11174       break;
11175
11176     case SAMPLE_eater_eat:
11177       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11178       break;
11179
11180     case SAMPLE_alien:
11181       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11182       break;
11183
11184     case SAMPLE_collect:
11185       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11186       break;
11187
11188     case SAMPLE_diamond:
11189       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11190       break;
11191
11192     case SAMPLE_squash:
11193       /* !!! CHECK THIS !!! */
11194 #if 1
11195       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11196 #else
11197       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11198 #endif
11199       break;
11200
11201     case SAMPLE_wonderfall:
11202       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11203       break;
11204
11205     case SAMPLE_drip:
11206       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11207       break;
11208
11209     case SAMPLE_push:
11210       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11211       break;
11212
11213     case SAMPLE_dirt:
11214       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11215       break;
11216
11217     case SAMPLE_acid:
11218       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11219       break;
11220
11221     case SAMPLE_ball:
11222       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11223       break;
11224
11225     case SAMPLE_grow:
11226       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11227       break;
11228
11229     case SAMPLE_wonder:
11230       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11231       break;
11232
11233     case SAMPLE_door:
11234       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11235       break;
11236
11237     case SAMPLE_exit_open:
11238       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11239       break;
11240
11241     case SAMPLE_exit_leave:
11242       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11243       break;
11244
11245     case SAMPLE_dynamite:
11246       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11247       break;
11248
11249     case SAMPLE_tick:
11250       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11251       break;
11252
11253     case SAMPLE_press:
11254       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11255       break;
11256
11257     case SAMPLE_wheel:
11258       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11259       break;
11260
11261     case SAMPLE_boom:
11262       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11263       break;
11264
11265     case SAMPLE_die:
11266       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11267       break;
11268
11269     case SAMPLE_time:
11270       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11271       break;
11272
11273     default:
11274       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11275       break;
11276   }
11277 }
11278
11279 void RaiseScore(int value)
11280 {
11281   local_player->score += value;
11282
11283   DrawGameValue_Score(local_player->score);
11284 }
11285
11286 void RaiseScoreElement(int element)
11287 {
11288   switch(element)
11289   {
11290     case EL_EMERALD:
11291     case EL_BD_DIAMOND:
11292     case EL_EMERALD_YELLOW:
11293     case EL_EMERALD_RED:
11294     case EL_EMERALD_PURPLE:
11295     case EL_SP_INFOTRON:
11296       RaiseScore(level.score[SC_EMERALD]);
11297       break;
11298     case EL_DIAMOND:
11299       RaiseScore(level.score[SC_DIAMOND]);
11300       break;
11301     case EL_CRYSTAL:
11302       RaiseScore(level.score[SC_CRYSTAL]);
11303       break;
11304     case EL_PEARL:
11305       RaiseScore(level.score[SC_PEARL]);
11306       break;
11307     case EL_BUG:
11308     case EL_BD_BUTTERFLY:
11309     case EL_SP_ELECTRON:
11310       RaiseScore(level.score[SC_BUG]);
11311       break;
11312     case EL_SPACESHIP:
11313     case EL_BD_FIREFLY:
11314     case EL_SP_SNIKSNAK:
11315       RaiseScore(level.score[SC_SPACESHIP]);
11316       break;
11317     case EL_YAMYAM:
11318     case EL_DARK_YAMYAM:
11319       RaiseScore(level.score[SC_YAMYAM]);
11320       break;
11321     case EL_ROBOT:
11322       RaiseScore(level.score[SC_ROBOT]);
11323       break;
11324     case EL_PACMAN:
11325       RaiseScore(level.score[SC_PACMAN]);
11326       break;
11327     case EL_NUT:
11328       RaiseScore(level.score[SC_NUT]);
11329       break;
11330     case EL_DYNAMITE:
11331     case EL_SP_DISK_RED:
11332     case EL_DYNABOMB_INCREASE_NUMBER:
11333     case EL_DYNABOMB_INCREASE_SIZE:
11334     case EL_DYNABOMB_INCREASE_POWER:
11335       RaiseScore(level.score[SC_DYNAMITE]);
11336       break;
11337     case EL_SHIELD_NORMAL:
11338     case EL_SHIELD_DEADLY:
11339       RaiseScore(level.score[SC_SHIELD]);
11340       break;
11341     case EL_EXTRA_TIME:
11342       RaiseScore(level.extra_time_score);
11343       break;
11344     case EL_KEY_1:
11345     case EL_KEY_2:
11346     case EL_KEY_3:
11347     case EL_KEY_4:
11348     case EL_EM_KEY_1:
11349     case EL_EM_KEY_2:
11350     case EL_EM_KEY_3:
11351     case EL_EM_KEY_4:
11352     case EL_EMC_KEY_5:
11353     case EL_EMC_KEY_6:
11354     case EL_EMC_KEY_7:
11355     case EL_EMC_KEY_8:
11356       RaiseScore(level.score[SC_KEY]);
11357       break;
11358     default:
11359       RaiseScore(element_info[element].collect_score);
11360       break;
11361   }
11362 }
11363
11364 void RequestQuitGame(boolean ask_if_really_quit)
11365 {
11366   if (AllPlayersGone ||
11367       !ask_if_really_quit ||
11368       level_editor_test_game ||
11369       Request("Do you really want to quit the game ?",
11370               REQ_ASK | REQ_STAY_CLOSED))
11371   {
11372 #if defined(NETWORK_AVALIABLE)
11373     if (options.network)
11374       SendToServer_StopPlaying();
11375     else
11376 #endif
11377     {
11378       game_status = GAME_MODE_MAIN;
11379       DrawMainMenu();
11380     }
11381   }
11382   else
11383   {
11384     if (tape.playing && tape.deactivate_display)
11385       TapeDeactivateDisplayOff(TRUE);
11386
11387     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11388
11389     if (tape.playing && tape.deactivate_display)
11390       TapeDeactivateDisplayOn();
11391   }
11392 }
11393
11394
11395 /* ---------- new game button stuff ---------------------------------------- */
11396
11397 /* graphic position values for game buttons */
11398 #define GAME_BUTTON_XSIZE       30
11399 #define GAME_BUTTON_YSIZE       30
11400 #define GAME_BUTTON_XPOS        5
11401 #define GAME_BUTTON_YPOS        215
11402 #define SOUND_BUTTON_XPOS       5
11403 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11404
11405 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11406 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11407 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11408 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11409 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11410 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11411
11412 static struct
11413 {
11414   int x, y;
11415   int gadget_id;
11416   char *infotext;
11417 } gamebutton_info[NUM_GAME_BUTTONS] =
11418 {
11419   {
11420     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
11421     GAME_CTRL_ID_STOP,
11422     "stop game"
11423   },
11424   {
11425     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
11426     GAME_CTRL_ID_PAUSE,
11427     "pause game"
11428   },
11429   {
11430     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
11431     GAME_CTRL_ID_PLAY,
11432     "play game"
11433   },
11434   {
11435     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
11436     SOUND_CTRL_ID_MUSIC,
11437     "background music on/off"
11438   },
11439   {
11440     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
11441     SOUND_CTRL_ID_LOOPS,
11442     "sound loops on/off"
11443   },
11444   {
11445     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
11446     SOUND_CTRL_ID_SIMPLE,
11447     "normal sounds on/off"
11448   }
11449 };
11450
11451 void CreateGameButtons()
11452 {
11453   int i;
11454
11455   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11456   {
11457     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11458     struct GadgetInfo *gi;
11459     int button_type;
11460     boolean checked;
11461     unsigned long event_mask;
11462     int gd_xoffset, gd_yoffset;
11463     int gd_x1, gd_x2, gd_y1, gd_y2;
11464     int id = i;
11465
11466     gd_xoffset = gamebutton_info[i].x;
11467     gd_yoffset = gamebutton_info[i].y;
11468     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11469     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11470
11471     if (id == GAME_CTRL_ID_STOP ||
11472         id == GAME_CTRL_ID_PAUSE ||
11473         id == GAME_CTRL_ID_PLAY)
11474     {
11475       button_type = GD_TYPE_NORMAL_BUTTON;
11476       checked = FALSE;
11477       event_mask = GD_EVENT_RELEASED;
11478       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11479       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11480     }
11481     else
11482     {
11483       button_type = GD_TYPE_CHECK_BUTTON;
11484       checked =
11485         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11486          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11487          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11488       event_mask = GD_EVENT_PRESSED;
11489       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
11490       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11491     }
11492
11493     gi = CreateGadget(GDI_CUSTOM_ID, id,
11494                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
11495                       GDI_X, DX + gd_xoffset,
11496                       GDI_Y, DY + gd_yoffset,
11497                       GDI_WIDTH, GAME_BUTTON_XSIZE,
11498                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
11499                       GDI_TYPE, button_type,
11500                       GDI_STATE, GD_BUTTON_UNPRESSED,
11501                       GDI_CHECKED, checked,
11502                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11503                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11504                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11505                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11506                       GDI_EVENT_MASK, event_mask,
11507                       GDI_CALLBACK_ACTION, HandleGameButtons,
11508                       GDI_END);
11509
11510     if (gi == NULL)
11511       Error(ERR_EXIT, "cannot create gadget");
11512
11513     game_gadget[id] = gi;
11514   }
11515 }
11516
11517 void FreeGameButtons()
11518 {
11519   int i;
11520
11521   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11522     FreeGadget(game_gadget[i]);
11523 }
11524
11525 static void MapGameButtons()
11526 {
11527   int i;
11528
11529   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11530     MapGadget(game_gadget[i]);
11531 }
11532
11533 void UnmapGameButtons()
11534 {
11535   int i;
11536
11537   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11538     UnmapGadget(game_gadget[i]);
11539 }
11540
11541 static void HandleGameButtons(struct GadgetInfo *gi)
11542 {
11543   int id = gi->custom_id;
11544
11545   if (game_status != GAME_MODE_PLAYING)
11546     return;
11547
11548   switch (id)
11549   {
11550     case GAME_CTRL_ID_STOP:
11551       RequestQuitGame(TRUE);
11552       break;
11553
11554     case GAME_CTRL_ID_PAUSE:
11555       if (options.network)
11556       {
11557 #if defined(NETWORK_AVALIABLE)
11558         if (tape.pausing)
11559           SendToServer_ContinuePlaying();
11560         else
11561           SendToServer_PausePlaying();
11562 #endif
11563       }
11564       else
11565         TapeTogglePause(TAPE_TOGGLE_MANUAL);
11566       break;
11567
11568     case GAME_CTRL_ID_PLAY:
11569       if (tape.pausing)
11570       {
11571 #if defined(NETWORK_AVALIABLE)
11572         if (options.network)
11573           SendToServer_ContinuePlaying();
11574         else
11575 #endif
11576         {
11577           tape.pausing = FALSE;
11578           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
11579         }
11580       }
11581       break;
11582
11583     case SOUND_CTRL_ID_MUSIC:
11584       if (setup.sound_music)
11585       { 
11586         setup.sound_music = FALSE;
11587         FadeMusic();
11588       }
11589       else if (audio.music_available)
11590       { 
11591         setup.sound = setup.sound_music = TRUE;
11592
11593         SetAudioMode(setup.sound);
11594
11595         PlayLevelMusic();
11596       }
11597       break;
11598
11599     case SOUND_CTRL_ID_LOOPS:
11600       if (setup.sound_loops)
11601         setup.sound_loops = FALSE;
11602       else if (audio.loops_available)
11603       {
11604         setup.sound = setup.sound_loops = TRUE;
11605         SetAudioMode(setup.sound);
11606       }
11607       break;
11608
11609     case SOUND_CTRL_ID_SIMPLE:
11610       if (setup.sound_simple)
11611         setup.sound_simple = FALSE;
11612       else if (audio.sound_available)
11613       {
11614         setup.sound = setup.sound_simple = TRUE;
11615         SetAudioMode(setup.sound);
11616       }
11617       break;
11618
11619     default:
11620       break;
11621   }
11622 }