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