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