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