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