rnd-20061028-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     /* check trigger element for all events where the element that is checked
8648        for changing interacts with a directly adjacent element -- this is
8649        different to element changes that affect other elements to change on the
8650        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
8651     boolean check_trigger_element =
8652       (trigger_event == CE_TOUCHING_X ||
8653        trigger_event == CE_HITTING_X ||
8654        trigger_event == CE_HIT_BY_X ||
8655 #if 1
8656        /* this one was forgotten until 3.2.3 */
8657        trigger_event == CE_DIGGING_X);
8658 #endif
8659
8660     if (change->can_change_or_has_action &&
8661         change->has_event[trigger_event] &&
8662         change->trigger_side & trigger_side &&
8663         change->trigger_player & trigger_player &&
8664         (!check_trigger_element ||
8665          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8666     {
8667       change->actual_trigger_element = trigger_element;
8668       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8669       change->actual_trigger_side = trigger_side;
8670       change->actual_trigger_ce_value = CustomValue[x][y];
8671       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8672
8673       /* special case: trigger element not at (x,y) position for some events */
8674       if (check_trigger_element)
8675       {
8676         static struct
8677         {
8678           int dx, dy;
8679         } move_xy[] =
8680           {
8681             {  0,  0 },
8682             { -1,  0 },
8683             { +1,  0 },
8684             {  0,  0 },
8685             {  0, -1 },
8686             {  0,  0 }, { 0, 0 }, { 0, 0 },
8687             {  0, +1 }
8688           };
8689
8690         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8691         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8692
8693         change->actual_trigger_ce_value = CustomValue[xx][yy];
8694         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8695       }
8696
8697       if (change->can_change && !change_done)
8698       {
8699         ChangeDelay[x][y] = 1;
8700         ChangeEvent[x][y] = trigger_event;
8701
8702         HandleElementChange(x, y, p);
8703
8704         change_done = TRUE;
8705       }
8706 #if USE_NEW_DELAYED_ACTION
8707       else if (change->has_action)
8708       {
8709         ExecuteCustomElementAction(x, y, element, p);
8710         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8711       }
8712 #else
8713       if (change->has_action)
8714       {
8715         ExecuteCustomElementAction(x, y, element, p);
8716         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8717       }
8718 #endif
8719     }
8720   }
8721
8722   return change_done;
8723 }
8724
8725 static void PlayPlayerSound(struct PlayerInfo *player)
8726 {
8727   int jx = player->jx, jy = player->jy;
8728   int sound_element = player->artwork_element;
8729   int last_action = player->last_action_waiting;
8730   int action = player->action_waiting;
8731
8732   if (player->is_waiting)
8733   {
8734     if (action != last_action)
8735       PlayLevelSoundElementAction(jx, jy, sound_element, action);
8736     else
8737       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8738   }
8739   else
8740   {
8741     if (action != last_action)
8742       StopSound(element_info[sound_element].sound[last_action]);
8743
8744     if (last_action == ACTION_SLEEPING)
8745       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8746   }
8747 }
8748
8749 static void PlayAllPlayersSound()
8750 {
8751   int i;
8752
8753   for (i = 0; i < MAX_PLAYERS; i++)
8754     if (stored_player[i].active)
8755       PlayPlayerSound(&stored_player[i]);
8756 }
8757
8758 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8759 {
8760   boolean last_waiting = player->is_waiting;
8761   int move_dir = player->MovDir;
8762
8763   player->dir_waiting = move_dir;
8764   player->last_action_waiting = player->action_waiting;
8765
8766   if (is_waiting)
8767   {
8768     if (!last_waiting)          /* not waiting -> waiting */
8769     {
8770       player->is_waiting = TRUE;
8771
8772       player->frame_counter_bored =
8773         FrameCounter +
8774         game.player_boring_delay_fixed +
8775         GetSimpleRandom(game.player_boring_delay_random);
8776       player->frame_counter_sleeping =
8777         FrameCounter +
8778         game.player_sleeping_delay_fixed +
8779         GetSimpleRandom(game.player_sleeping_delay_random);
8780
8781       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8782     }
8783
8784     if (game.player_sleeping_delay_fixed +
8785         game.player_sleeping_delay_random > 0 &&
8786         player->anim_delay_counter == 0 &&
8787         player->post_delay_counter == 0 &&
8788         FrameCounter >= player->frame_counter_sleeping)
8789       player->is_sleeping = TRUE;
8790     else if (game.player_boring_delay_fixed +
8791              game.player_boring_delay_random > 0 &&
8792              FrameCounter >= player->frame_counter_bored)
8793       player->is_bored = TRUE;
8794
8795     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8796                               player->is_bored ? ACTION_BORING :
8797                               ACTION_WAITING);
8798
8799     if (player->is_sleeping && player->use_murphy)
8800     {
8801       /* special case for sleeping Murphy when leaning against non-free tile */
8802
8803       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8804           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8805            !IS_MOVING(player->jx - 1, player->jy)))
8806         move_dir = MV_LEFT;
8807       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8808                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8809                 !IS_MOVING(player->jx + 1, player->jy)))
8810         move_dir = MV_RIGHT;
8811       else
8812         player->is_sleeping = FALSE;
8813
8814       player->dir_waiting = move_dir;
8815     }
8816
8817     if (player->is_sleeping)
8818     {
8819       if (player->num_special_action_sleeping > 0)
8820       {
8821         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8822         {
8823           int last_special_action = player->special_action_sleeping;
8824           int num_special_action = player->num_special_action_sleeping;
8825           int special_action =
8826             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8827              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8828              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8829              last_special_action + 1 : ACTION_SLEEPING);
8830           int special_graphic =
8831             el_act_dir2img(player->artwork_element, special_action, move_dir);
8832
8833           player->anim_delay_counter =
8834             graphic_info[special_graphic].anim_delay_fixed +
8835             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8836           player->post_delay_counter =
8837             graphic_info[special_graphic].post_delay_fixed +
8838             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8839
8840           player->special_action_sleeping = special_action;
8841         }
8842
8843         if (player->anim_delay_counter > 0)
8844         {
8845           player->action_waiting = player->special_action_sleeping;
8846           player->anim_delay_counter--;
8847         }
8848         else if (player->post_delay_counter > 0)
8849         {
8850           player->post_delay_counter--;
8851         }
8852       }
8853     }
8854     else if (player->is_bored)
8855     {
8856       if (player->num_special_action_bored > 0)
8857       {
8858         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8859         {
8860           int special_action =
8861             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8862           int special_graphic =
8863             el_act_dir2img(player->artwork_element, special_action, move_dir);
8864
8865           player->anim_delay_counter =
8866             graphic_info[special_graphic].anim_delay_fixed +
8867             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8868           player->post_delay_counter =
8869             graphic_info[special_graphic].post_delay_fixed +
8870             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8871
8872           player->special_action_bored = special_action;
8873         }
8874
8875         if (player->anim_delay_counter > 0)
8876         {
8877           player->action_waiting = player->special_action_bored;
8878           player->anim_delay_counter--;
8879         }
8880         else if (player->post_delay_counter > 0)
8881         {
8882           player->post_delay_counter--;
8883         }
8884       }
8885     }
8886   }
8887   else if (last_waiting)        /* waiting -> not waiting */
8888   {
8889     player->is_waiting = FALSE;
8890     player->is_bored = FALSE;
8891     player->is_sleeping = FALSE;
8892
8893     player->frame_counter_bored = -1;
8894     player->frame_counter_sleeping = -1;
8895
8896     player->anim_delay_counter = 0;
8897     player->post_delay_counter = 0;
8898
8899     player->dir_waiting = player->MovDir;
8900     player->action_waiting = ACTION_DEFAULT;
8901
8902     player->special_action_bored = ACTION_DEFAULT;
8903     player->special_action_sleeping = ACTION_DEFAULT;
8904   }
8905 }
8906
8907 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8908 {
8909   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8910   int left      = player_action & JOY_LEFT;
8911   int right     = player_action & JOY_RIGHT;
8912   int up        = player_action & JOY_UP;
8913   int down      = player_action & JOY_DOWN;
8914   int button1   = player_action & JOY_BUTTON_1;
8915   int button2   = player_action & JOY_BUTTON_2;
8916   int dx        = (left ? -1 : right ? 1 : 0);
8917   int dy        = (up   ? -1 : down  ? 1 : 0);
8918
8919   if (!player->active || tape.pausing)
8920     return 0;
8921
8922   if (player_action)
8923   {
8924     if (button1)
8925       snapped = SnapField(player, dx, dy);
8926     else
8927     {
8928       if (button2)
8929         dropped = DropElement(player);
8930
8931       moved = MovePlayer(player, dx, dy);
8932     }
8933
8934     if (tape.single_step && tape.recording && !tape.pausing)
8935     {
8936       if (button1 || (dropped && !moved))
8937       {
8938         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8939         SnapField(player, 0, 0);                /* stop snapping */
8940       }
8941     }
8942
8943     SetPlayerWaiting(player, FALSE);
8944
8945     return player_action;
8946   }
8947   else
8948   {
8949     /* no actions for this player (no input at player's configured device) */
8950
8951     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8952     SnapField(player, 0, 0);
8953     CheckGravityMovementWhenNotMoving(player);
8954
8955     if (player->MovPos == 0)
8956       SetPlayerWaiting(player, TRUE);
8957
8958     if (player->MovPos == 0)    /* needed for tape.playing */
8959       player->is_moving = FALSE;
8960
8961     player->is_dropping = FALSE;
8962     player->is_dropping_pressed = FALSE;
8963     player->drop_pressed_delay = 0;
8964
8965     return 0;
8966   }
8967 }
8968
8969 static void CheckLevelTime()
8970 {
8971   int i;
8972
8973   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8974   {
8975     if (level.native_em_level->lev->home == 0)  /* all players at home */
8976     {
8977       PlayerWins(local_player);
8978
8979       AllPlayersGone = TRUE;
8980
8981       level.native_em_level->lev->home = -1;
8982     }
8983
8984     if (level.native_em_level->ply[0]->alive == 0 &&
8985         level.native_em_level->ply[1]->alive == 0 &&
8986         level.native_em_level->ply[2]->alive == 0 &&
8987         level.native_em_level->ply[3]->alive == 0)      /* all dead */
8988       AllPlayersGone = TRUE;
8989   }
8990
8991   if (TimeFrames >= FRAMES_PER_SECOND)
8992   {
8993     TimeFrames = 0;
8994     TapeTime++;
8995
8996     for (i = 0; i < MAX_PLAYERS; i++)
8997     {
8998       struct PlayerInfo *player = &stored_player[i];
8999
9000       if (SHIELD_ON(player))
9001       {
9002         player->shield_normal_time_left--;
9003
9004         if (player->shield_deadly_time_left > 0)
9005           player->shield_deadly_time_left--;
9006       }
9007     }
9008
9009     if (!local_player->LevelSolved && !level.use_step_counter)
9010     {
9011       TimePlayed++;
9012
9013       if (TimeLeft > 0)
9014       {
9015         TimeLeft--;
9016
9017         if (TimeLeft <= 10 && setup.time_limit)
9018           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9019
9020         DrawGameValue_Time(TimeLeft);
9021
9022         if (!TimeLeft && setup.time_limit)
9023         {
9024           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9025             level.native_em_level->lev->killed_out_of_time = TRUE;
9026           else
9027             for (i = 0; i < MAX_PLAYERS; i++)
9028               KillPlayer(&stored_player[i]);
9029         }
9030       }
9031       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9032         DrawGameValue_Time(TimePlayed);
9033
9034       level.native_em_level->lev->time =
9035         (level.time == 0 ? TimePlayed : TimeLeft);
9036     }
9037
9038     if (tape.recording || tape.playing)
9039       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9040   }
9041 }
9042
9043 void AdvanceFrameAndPlayerCounters(int player_nr)
9044 {
9045   int i;
9046
9047   /* advance frame counters (global frame counter and time frame counter) */
9048   FrameCounter++;
9049   TimeFrames++;
9050
9051   /* advance player counters (counters for move delay, move animation etc.) */
9052   for (i = 0; i < MAX_PLAYERS; i++)
9053   {
9054     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9055     int move_delay_value = stored_player[i].move_delay_value;
9056     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9057
9058     if (!advance_player_counters)       /* not all players may be affected */
9059       continue;
9060
9061 #if USE_NEW_PLAYER_ANIM
9062     if (move_frames == 0)       /* less than one move per game frame */
9063     {
9064       int stepsize = TILEX / move_delay_value;
9065       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9066       int count = (stored_player[i].is_moving ?
9067                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9068
9069       if (count % delay == 0)
9070         move_frames = 1;
9071     }
9072 #endif
9073
9074     stored_player[i].Frame += move_frames;
9075
9076     if (stored_player[i].MovPos != 0)
9077       stored_player[i].StepFrame += move_frames;
9078
9079     if (stored_player[i].move_delay > 0)
9080       stored_player[i].move_delay--;
9081
9082     /* due to bugs in previous versions, counter must count up, not down */
9083     if (stored_player[i].push_delay != -1)
9084       stored_player[i].push_delay++;
9085
9086     if (stored_player[i].drop_delay > 0)
9087       stored_player[i].drop_delay--;
9088
9089     if (stored_player[i].is_dropping_pressed)
9090       stored_player[i].drop_pressed_delay++;
9091   }
9092 }
9093
9094 void StartGameActions(boolean init_network_game, boolean record_tape,
9095                       long random_seed)
9096 {
9097   unsigned long new_random_seed = InitRND(random_seed);
9098
9099   if (record_tape)
9100     TapeStartRecording(new_random_seed);
9101
9102 #if defined(NETWORK_AVALIABLE)
9103   if (init_network_game)
9104   {
9105     SendToServer_StartPlaying();
9106
9107     return;
9108   }
9109 #endif
9110
9111   InitGame();
9112 }
9113
9114 void GameActions()
9115 {
9116   static unsigned long game_frame_delay = 0;
9117   unsigned long game_frame_delay_value;
9118   byte *recorded_player_action;
9119   byte summarized_player_action = 0;
9120   byte tape_action[MAX_PLAYERS];
9121   int i;
9122
9123   if (game.restart_level)
9124     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9125
9126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9127   {
9128     if (level.native_em_level->lev->home == 0)  /* all players at home */
9129     {
9130       PlayerWins(local_player);
9131
9132       AllPlayersGone = TRUE;
9133
9134       level.native_em_level->lev->home = -1;
9135     }
9136
9137     if (level.native_em_level->ply[0]->alive == 0 &&
9138         level.native_em_level->ply[1]->alive == 0 &&
9139         level.native_em_level->ply[2]->alive == 0 &&
9140         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9141       AllPlayersGone = TRUE;
9142   }
9143
9144   if (local_player->LevelSolved)
9145     GameWon();
9146
9147   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9148     TapeStop();
9149
9150   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9151     return;
9152
9153   game_frame_delay_value =
9154     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9155
9156   if (tape.playing && tape.warp_forward && !tape.pausing)
9157     game_frame_delay_value = 0;
9158
9159   /* ---------- main game synchronization point ---------- */
9160
9161   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9162
9163   if (network_playing && !network_player_action_received)
9164   {
9165     /* try to get network player actions in time */
9166
9167 #if defined(NETWORK_AVALIABLE)
9168     /* last chance to get network player actions without main loop delay */
9169     HandleNetworking();
9170 #endif
9171
9172     /* game was quit by network peer */
9173     if (game_status != GAME_MODE_PLAYING)
9174       return;
9175
9176     if (!network_player_action_received)
9177       return;           /* failed to get network player actions in time */
9178
9179     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9180   }
9181
9182   if (tape.pausing)
9183     return;
9184
9185   /* at this point we know that we really continue executing the game */
9186
9187   network_player_action_received = FALSE;
9188
9189   /* when playing tape, read previously recorded player input from tape data */
9190   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9191
9192 #if 1
9193   /* TapePlayAction() may return NULL when toggling to "pause before death" */
9194   if (tape.pausing)
9195     return;
9196 #endif
9197
9198   if (tape.set_centered_player)
9199   {
9200     game.centered_player_nr_next = tape.centered_player_nr_next;
9201     game.set_centered_player = TRUE;
9202   }
9203
9204   for (i = 0; i < MAX_PLAYERS; i++)
9205   {
9206     summarized_player_action |= stored_player[i].action;
9207
9208     if (!network_playing)
9209       stored_player[i].effective_action = stored_player[i].action;
9210   }
9211
9212 #if defined(NETWORK_AVALIABLE)
9213   if (network_playing)
9214     SendToServer_MovePlayer(summarized_player_action);
9215 #endif
9216
9217   if (!options.network && !setup.team_mode)
9218     local_player->effective_action = summarized_player_action;
9219
9220   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9221   {
9222     for (i = 0; i < MAX_PLAYERS; i++)
9223       stored_player[i].effective_action =
9224         (i == game.centered_player_nr ? summarized_player_action : 0);
9225   }
9226
9227   if (recorded_player_action != NULL)
9228     for (i = 0; i < MAX_PLAYERS; i++)
9229       stored_player[i].effective_action = recorded_player_action[i];
9230
9231   for (i = 0; i < MAX_PLAYERS; i++)
9232   {
9233     tape_action[i] = stored_player[i].effective_action;
9234
9235     /* (this can only happen in the R'n'D game engine) */
9236     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9237       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9238   }
9239
9240   /* only record actions from input devices, but not programmed actions */
9241   if (tape.recording)
9242     TapeRecordAction(tape_action);
9243
9244   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9245   {
9246     GameActions_EM_Main();
9247   }
9248   else
9249   {
9250     GameActions_RND();
9251   }
9252 }
9253
9254 void GameActions_EM_Main()
9255 {
9256   byte effective_action[MAX_PLAYERS];
9257   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9258   int i;
9259
9260   for (i = 0; i < MAX_PLAYERS; i++)
9261     effective_action[i] = stored_player[i].effective_action;
9262
9263   GameActions_EM(effective_action, warp_mode);
9264
9265   CheckLevelTime();
9266
9267   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9268 }
9269
9270 void GameActions_RND()
9271 {
9272   int magic_wall_x = 0, magic_wall_y = 0;
9273   int i, x, y, element, graphic;
9274
9275   InitPlayfieldScanModeVars();
9276
9277 #if USE_ONE_MORE_CHANGE_PER_FRAME
9278   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9279   {
9280     SCAN_PLAYFIELD(x, y)
9281     {
9282       ChangeCount[x][y] = 0;
9283       ChangeEvent[x][y] = -1;
9284     }
9285   }
9286 #endif
9287
9288   if (game.set_centered_player)
9289   {
9290     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9291
9292     /* switching to "all players" only possible if all players fit to screen */
9293     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9294     {
9295       game.centered_player_nr_next = game.centered_player_nr;
9296       game.set_centered_player = FALSE;
9297     }
9298
9299     /* do not switch focus to non-existing (or non-active) player */
9300     if (game.centered_player_nr_next >= 0 &&
9301         !stored_player[game.centered_player_nr_next].active)
9302     {
9303       game.centered_player_nr_next = game.centered_player_nr;
9304       game.set_centered_player = FALSE;
9305     }
9306   }
9307
9308   if (game.set_centered_player &&
9309       ScreenMovPos == 0)        /* screen currently aligned at tile position */
9310   {
9311     int sx, sy;
9312
9313     if (game.centered_player_nr_next == -1)
9314     {
9315       setScreenCenteredToAllPlayers(&sx, &sy);
9316     }
9317     else
9318     {
9319       sx = stored_player[game.centered_player_nr_next].jx;
9320       sy = stored_player[game.centered_player_nr_next].jy;
9321     }
9322
9323     game.centered_player_nr = game.centered_player_nr_next;
9324     game.set_centered_player = FALSE;
9325
9326     DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9327     DrawGameDoorValues();
9328   }
9329
9330   for (i = 0; i < MAX_PLAYERS; i++)
9331   {
9332     int actual_player_action = stored_player[i].effective_action;
9333
9334 #if 1
9335     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9336        - rnd_equinox_tetrachloride 048
9337        - rnd_equinox_tetrachloride_ii 096
9338        - rnd_emanuel_schmieg 002
9339        - doctor_sloan_ww 001, 020
9340     */
9341     if (stored_player[i].MovPos == 0)
9342       CheckGravityMovement(&stored_player[i]);
9343 #endif
9344
9345     /* overwrite programmed action with tape action */
9346     if (stored_player[i].programmed_action)
9347       actual_player_action = stored_player[i].programmed_action;
9348
9349     PlayerActions(&stored_player[i], actual_player_action);
9350
9351     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9352   }
9353
9354   ScrollScreen(NULL, SCROLL_GO_ON);
9355
9356   /* for backwards compatibility, the following code emulates a fixed bug that
9357      occured when pushing elements (causing elements that just made their last
9358      pushing step to already (if possible) make their first falling step in the
9359      same game frame, which is bad); this code is also needed to use the famous
9360      "spring push bug" which is used in older levels and might be wanted to be
9361      used also in newer levels, but in this case the buggy pushing code is only
9362      affecting the "spring" element and no other elements */
9363
9364   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9365   {
9366     for (i = 0; i < MAX_PLAYERS; i++)
9367     {
9368       struct PlayerInfo *player = &stored_player[i];
9369       int x = player->jx;
9370       int y = player->jy;
9371
9372       if (player->active && player->is_pushing && player->is_moving &&
9373           IS_MOVING(x, y) &&
9374           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9375            Feld[x][y] == EL_SPRING))
9376       {
9377         ContinueMoving(x, y);
9378
9379         /* continue moving after pushing (this is actually a bug) */
9380         if (!IS_MOVING(x, y))
9381         {
9382           Stop[x][y] = FALSE;
9383         }
9384       }
9385     }
9386   }
9387
9388   SCAN_PLAYFIELD(x, y)
9389   {
9390     ChangeCount[x][y] = 0;
9391     ChangeEvent[x][y] = -1;
9392
9393     /* this must be handled before main playfield loop */
9394     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9395     {
9396       MovDelay[x][y]--;
9397       if (MovDelay[x][y] <= 0)
9398         RemoveField(x, y);
9399     }
9400
9401 #if USE_NEW_SNAP_DELAY
9402     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9403     {
9404       MovDelay[x][y]--;
9405       if (MovDelay[x][y] <= 0)
9406       {
9407         RemoveField(x, y);
9408         DrawLevelField(x, y);
9409
9410         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
9411       }
9412     }
9413 #endif
9414
9415 #if DEBUG
9416     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9417     {
9418       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9419       printf("GameActions(): This should never happen!\n");
9420
9421       ChangePage[x][y] = -1;
9422     }
9423 #endif
9424
9425     Stop[x][y] = FALSE;
9426     if (WasJustMoving[x][y] > 0)
9427       WasJustMoving[x][y]--;
9428     if (WasJustFalling[x][y] > 0)
9429       WasJustFalling[x][y]--;
9430     if (CheckCollision[x][y] > 0)
9431       CheckCollision[x][y]--;
9432
9433     GfxFrame[x][y]++;
9434
9435     /* reset finished pushing action (not done in ContinueMoving() to allow
9436        continuous pushing animation for elements with zero push delay) */
9437     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9438     {
9439       ResetGfxAnimation(x, y);
9440       DrawLevelField(x, y);
9441     }
9442
9443 #if DEBUG
9444     if (IS_BLOCKED(x, y))
9445     {
9446       int oldx, oldy;
9447
9448       Blocked2Moving(x, y, &oldx, &oldy);
9449       if (!IS_MOVING(oldx, oldy))
9450       {
9451         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9452         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9453         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9454         printf("GameActions(): This should never happen!\n");
9455       }
9456     }
9457 #endif
9458   }
9459
9460   SCAN_PLAYFIELD(x, y)
9461   {
9462     element = Feld[x][y];
9463     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9464
9465     ResetGfxFrame(x, y, TRUE);
9466
9467     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9468         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9469       ResetRandomAnimationValue(x, y);
9470
9471     SetRandomAnimationValue(x, y);
9472
9473     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9474
9475     if (IS_INACTIVE(element))
9476     {
9477       if (IS_ANIMATED(graphic))
9478         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9479
9480       continue;
9481     }
9482
9483     /* this may take place after moving, so 'element' may have changed */
9484     if (IS_CHANGING(x, y) &&
9485         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9486     {
9487       int page = element_info[element].event_page_nr[CE_DELAY];
9488
9489 #if 1
9490       HandleElementChange(x, y, page);
9491 #else
9492       if (CAN_CHANGE(element))
9493         HandleElementChange(x, y, page);
9494
9495       if (HAS_ACTION(element))
9496         ExecuteCustomElementAction(x, y, element, page);
9497 #endif
9498
9499       element = Feld[x][y];
9500       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9501     }
9502
9503     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9504     {
9505       StartMoving(x, y);
9506
9507       element = Feld[x][y];
9508       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9509
9510       if (IS_ANIMATED(graphic) &&
9511           !IS_MOVING(x, y) &&
9512           !Stop[x][y])
9513         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9514
9515       if (IS_GEM(element) || element == EL_SP_INFOTRON)
9516         DrawTwinkleOnField(x, y);
9517     }
9518     else if ((element == EL_ACID ||
9519               element == EL_EXIT_OPEN ||
9520               element == EL_SP_EXIT_OPEN ||
9521               element == EL_SP_TERMINAL ||
9522               element == EL_SP_TERMINAL_ACTIVE ||
9523               element == EL_EXTRA_TIME ||
9524               element == EL_SHIELD_NORMAL ||
9525               element == EL_SHIELD_DEADLY) &&
9526              IS_ANIMATED(graphic))
9527       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9528     else if (IS_MOVING(x, y))
9529       ContinueMoving(x, y);
9530     else if (IS_ACTIVE_BOMB(element))
9531       CheckDynamite(x, y);
9532     else if (element == EL_AMOEBA_GROWING)
9533       AmoebeWaechst(x, y);
9534     else if (element == EL_AMOEBA_SHRINKING)
9535       AmoebaDisappearing(x, y);
9536
9537 #if !USE_NEW_AMOEBA_CODE
9538     else if (IS_AMOEBALIVE(element))
9539       AmoebeAbleger(x, y);
9540 #endif
9541
9542     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9543       Life(x, y);
9544     else if (element == EL_EXIT_CLOSED)
9545       CheckExit(x, y);
9546     else if (element == EL_SP_EXIT_CLOSED)
9547       CheckExitSP(x, y);
9548     else if (element == EL_EXPANDABLE_WALL_GROWING)
9549       MauerWaechst(x, y);
9550     else if (element == EL_EXPANDABLE_WALL ||
9551              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9552              element == EL_EXPANDABLE_WALL_VERTICAL ||
9553              element == EL_EXPANDABLE_WALL_ANY ||
9554              element == EL_BD_EXPANDABLE_WALL)
9555       MauerAbleger(x, y);
9556     else if (element == EL_FLAMES)
9557       CheckForDragon(x, y);
9558     else if (element == EL_EXPLOSION)
9559       ; /* drawing of correct explosion animation is handled separately */
9560     else if (element == EL_ELEMENT_SNAPPING ||
9561              element == EL_DIAGONAL_SHRINKING ||
9562              element == EL_DIAGONAL_GROWING)
9563     {
9564       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9565
9566       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9567     }
9568     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9569       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9570
9571     if (IS_BELT_ACTIVE(element))
9572       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9573
9574     if (game.magic_wall_active)
9575     {
9576       int jx = local_player->jx, jy = local_player->jy;
9577
9578       /* play the element sound at the position nearest to the player */
9579       if ((element == EL_MAGIC_WALL_FULL ||
9580            element == EL_MAGIC_WALL_ACTIVE ||
9581            element == EL_MAGIC_WALL_EMPTYING ||
9582            element == EL_BD_MAGIC_WALL_FULL ||
9583            element == EL_BD_MAGIC_WALL_ACTIVE ||
9584            element == EL_BD_MAGIC_WALL_EMPTYING) &&
9585           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9586       {
9587         magic_wall_x = x;
9588         magic_wall_y = y;
9589       }
9590     }
9591   }
9592
9593 #if USE_NEW_AMOEBA_CODE
9594   /* new experimental amoeba growth stuff */
9595   if (!(FrameCounter % 8))
9596   {
9597     static unsigned long random = 1684108901;
9598
9599     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9600     {
9601       x = RND(lev_fieldx);
9602       y = RND(lev_fieldy);
9603       element = Feld[x][y];
9604
9605       if (!IS_PLAYER(x,y) &&
9606           (element == EL_EMPTY ||
9607            CAN_GROW_INTO(element) ||
9608            element == EL_QUICKSAND_EMPTY ||
9609            element == EL_ACID_SPLASH_LEFT ||
9610            element == EL_ACID_SPLASH_RIGHT))
9611       {
9612         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9613             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9614             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9615             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9616           Feld[x][y] = EL_AMOEBA_DROP;
9617       }
9618
9619       random = random * 129 + 1;
9620     }
9621   }
9622 #endif
9623
9624 #if 0
9625   if (game.explosions_delayed)
9626 #endif
9627   {
9628     game.explosions_delayed = FALSE;
9629
9630     SCAN_PLAYFIELD(x, y)
9631     {
9632       element = Feld[x][y];
9633
9634       if (ExplodeField[x][y])
9635         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9636       else if (element == EL_EXPLOSION)
9637         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9638
9639       ExplodeField[x][y] = EX_TYPE_NONE;
9640     }
9641
9642     game.explosions_delayed = TRUE;
9643   }
9644
9645   if (game.magic_wall_active)
9646   {
9647     if (!(game.magic_wall_time_left % 4))
9648     {
9649       int element = Feld[magic_wall_x][magic_wall_y];
9650
9651       if (element == EL_BD_MAGIC_WALL_FULL ||
9652           element == EL_BD_MAGIC_WALL_ACTIVE ||
9653           element == EL_BD_MAGIC_WALL_EMPTYING)
9654         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9655       else
9656         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9657     }
9658
9659     if (game.magic_wall_time_left > 0)
9660     {
9661       game.magic_wall_time_left--;
9662       if (!game.magic_wall_time_left)
9663       {
9664         SCAN_PLAYFIELD(x, y)
9665         {
9666           element = Feld[x][y];
9667
9668           if (element == EL_MAGIC_WALL_ACTIVE ||
9669               element == EL_MAGIC_WALL_FULL)
9670           {
9671             Feld[x][y] = EL_MAGIC_WALL_DEAD;
9672             DrawLevelField(x, y);
9673           }
9674           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9675                    element == EL_BD_MAGIC_WALL_FULL)
9676           {
9677             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9678             DrawLevelField(x, y);
9679           }
9680         }
9681
9682         game.magic_wall_active = FALSE;
9683       }
9684     }
9685   }
9686
9687   if (game.light_time_left > 0)
9688   {
9689     game.light_time_left--;
9690
9691     if (game.light_time_left == 0)
9692       RedrawAllLightSwitchesAndInvisibleElements();
9693   }
9694
9695   if (game.timegate_time_left > 0)
9696   {
9697     game.timegate_time_left--;
9698
9699     if (game.timegate_time_left == 0)
9700       CloseAllOpenTimegates();
9701   }
9702
9703   if (game.lenses_time_left > 0)
9704   {
9705     game.lenses_time_left--;
9706
9707     if (game.lenses_time_left == 0)
9708       RedrawAllInvisibleElementsForLenses();
9709   }
9710
9711   if (game.magnify_time_left > 0)
9712   {
9713     game.magnify_time_left--;
9714
9715     if (game.magnify_time_left == 0)
9716       RedrawAllInvisibleElementsForMagnifier();
9717   }
9718
9719   for (i = 0; i < MAX_PLAYERS; i++)
9720   {
9721     struct PlayerInfo *player = &stored_player[i];
9722
9723     if (SHIELD_ON(player))
9724     {
9725       if (player->shield_deadly_time_left)
9726         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9727       else if (player->shield_normal_time_left)
9728         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9729     }
9730   }
9731
9732   CheckLevelTime();
9733
9734   DrawAllPlayers();
9735   PlayAllPlayersSound();
9736
9737   if (options.debug)                    /* calculate frames per second */
9738   {
9739     static unsigned long fps_counter = 0;
9740     static int fps_frames = 0;
9741     unsigned long fps_delay_ms = Counter() - fps_counter;
9742
9743     fps_frames++;
9744
9745     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
9746     {
9747       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9748
9749       fps_frames = 0;
9750       fps_counter = Counter();
9751     }
9752
9753     redraw_mask |= REDRAW_FPS;
9754   }
9755
9756   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9757
9758   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9759   {
9760     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9761
9762     local_player->show_envelope = 0;
9763   }
9764
9765   /* use random number generator in every frame to make it less predictable */
9766   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9767     RND(1);
9768 }
9769
9770 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9771 {
9772   int min_x = x, min_y = y, max_x = x, max_y = y;
9773   int i;
9774
9775   for (i = 0; i < MAX_PLAYERS; i++)
9776   {
9777     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9778
9779     if (!stored_player[i].active || &stored_player[i] == player)
9780       continue;
9781
9782     min_x = MIN(min_x, jx);
9783     min_y = MIN(min_y, jy);
9784     max_x = MAX(max_x, jx);
9785     max_y = MAX(max_y, jy);
9786   }
9787
9788   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9789 }
9790
9791 static boolean AllPlayersInVisibleScreen()
9792 {
9793   int i;
9794
9795   for (i = 0; i < MAX_PLAYERS; i++)
9796   {
9797     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9798
9799     if (!stored_player[i].active)
9800       continue;
9801
9802     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9803       return FALSE;
9804   }
9805
9806   return TRUE;
9807 }
9808
9809 void ScrollLevel(int dx, int dy)
9810 {
9811   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9812   int x, y;
9813
9814   BlitBitmap(drawto_field, drawto_field,
9815              FX + TILEX * (dx == -1) - softscroll_offset,
9816              FY + TILEY * (dy == -1) - softscroll_offset,
9817              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9818              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9819              FX + TILEX * (dx == 1) - softscroll_offset,
9820              FY + TILEY * (dy == 1) - softscroll_offset);
9821
9822   if (dx)
9823   {
9824     x = (dx == 1 ? BX1 : BX2);
9825     for (y = BY1; y <= BY2; y++)
9826       DrawScreenField(x, y);
9827   }
9828
9829   if (dy)
9830   {
9831     y = (dy == 1 ? BY1 : BY2);
9832     for (x = BX1; x <= BX2; x++)
9833       DrawScreenField(x, y);
9834   }
9835
9836   redraw_mask |= REDRAW_FIELD;
9837 }
9838
9839 static boolean canFallDown(struct PlayerInfo *player)
9840 {
9841   int jx = player->jx, jy = player->jy;
9842
9843   return (IN_LEV_FIELD(jx, jy + 1) &&
9844           (IS_FREE(jx, jy + 1) ||
9845            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9846           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9847           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9848 }
9849
9850 static boolean canPassField(int x, int y, int move_dir)
9851 {
9852   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9853   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9854   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9855   int nextx = x + dx;
9856   int nexty = y + dy;
9857   int element = Feld[x][y];
9858
9859   return (IS_PASSABLE_FROM(element, opposite_dir) &&
9860           !CAN_MOVE(element) &&
9861           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9862           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9863           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9864 }
9865
9866 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9867 {
9868   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9869   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9870   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9871   int newx = x + dx;
9872   int newy = y + dy;
9873
9874   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9875           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9876           (IS_DIGGABLE(Feld[newx][newy]) ||
9877            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9878            canPassField(newx, newy, move_dir)));
9879 }
9880
9881 static void CheckGravityMovement(struct PlayerInfo *player)
9882 {
9883 #if USE_PLAYER_GRAVITY
9884   if (player->gravity && !player->programmed_action)
9885 #else
9886   if (game.gravity && !player->programmed_action)
9887 #endif
9888   {
9889     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9890     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9891     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9892     int jx = player->jx, jy = player->jy;
9893     boolean player_is_moving_to_valid_field =
9894       (!player_is_snapping &&
9895        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9896         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9897     boolean player_can_fall_down = canFallDown(player);
9898
9899     if (player_can_fall_down &&
9900         !player_is_moving_to_valid_field)
9901       player->programmed_action = MV_DOWN;
9902   }
9903 }
9904
9905 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9906 {
9907   return CheckGravityMovement(player);
9908
9909 #if USE_PLAYER_GRAVITY
9910   if (player->gravity && !player->programmed_action)
9911 #else
9912   if (game.gravity && !player->programmed_action)
9913 #endif
9914   {
9915     int jx = player->jx, jy = player->jy;
9916     boolean field_under_player_is_free =
9917       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9918     boolean player_is_standing_on_valid_field =
9919       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9920        (IS_WALKABLE(Feld[jx][jy]) &&
9921         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9922
9923     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9924       player->programmed_action = MV_DOWN;
9925   }
9926 }
9927
9928 /*
9929   MovePlayerOneStep()
9930   -----------------------------------------------------------------------------
9931   dx, dy:               direction (non-diagonal) to try to move the player to
9932   real_dx, real_dy:     direction as read from input device (can be diagonal)
9933 */
9934
9935 boolean MovePlayerOneStep(struct PlayerInfo *player,
9936                           int dx, int dy, int real_dx, int real_dy)
9937 {
9938   int jx = player->jx, jy = player->jy;
9939   int new_jx = jx + dx, new_jy = jy + dy;
9940 #if !USE_FIXED_DONT_RUN_INTO
9941   int element;
9942 #endif
9943   int can_move;
9944   boolean player_can_move = !player->cannot_move;
9945
9946   if (!player->active || (!dx && !dy))
9947     return MP_NO_ACTION;
9948
9949   player->MovDir = (dx < 0 ? MV_LEFT :
9950                     dx > 0 ? MV_RIGHT :
9951                     dy < 0 ? MV_UP :
9952                     dy > 0 ? MV_DOWN :  MV_NONE);
9953
9954   if (!IN_LEV_FIELD(new_jx, new_jy))
9955     return MP_NO_ACTION;
9956
9957   if (!player_can_move)
9958   {
9959     if (player->MovPos == 0)
9960     {
9961       player->is_moving = FALSE;
9962       player->is_digging = FALSE;
9963       player->is_collecting = FALSE;
9964       player->is_snapping = FALSE;
9965       player->is_pushing = FALSE;
9966     }
9967   }
9968
9969 #if 1
9970   if (!options.network && game.centered_player_nr == -1 &&
9971       !AllPlayersInSight(player, new_jx, new_jy))
9972     return MP_NO_ACTION;
9973 #else
9974   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9975     return MP_NO_ACTION;
9976 #endif
9977
9978 #if !USE_FIXED_DONT_RUN_INTO
9979   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9980
9981   /* (moved to DigField()) */
9982   if (player_can_move && DONT_RUN_INTO(element))
9983   {
9984     if (element == EL_ACID && dx == 0 && dy == 1)
9985     {
9986       SplashAcid(new_jx, new_jy);
9987       Feld[jx][jy] = EL_PLAYER_1;
9988       InitMovingField(jx, jy, MV_DOWN);
9989       Store[jx][jy] = EL_ACID;
9990       ContinueMoving(jx, jy);
9991       BuryPlayer(player);
9992     }
9993     else
9994       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9995
9996     return MP_MOVING;
9997   }
9998 #endif
9999
10000   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10001   if (can_move != MP_MOVING)
10002     return can_move;
10003
10004   /* check if DigField() has caused relocation of the player */
10005   if (player->jx != jx || player->jy != jy)
10006     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10007
10008   StorePlayer[jx][jy] = 0;
10009   player->last_jx = jx;
10010   player->last_jy = jy;
10011   player->jx = new_jx;
10012   player->jy = new_jy;
10013   StorePlayer[new_jx][new_jy] = player->element_nr;
10014
10015   if (player->move_delay_value_next != -1)
10016   {
10017     player->move_delay_value = player->move_delay_value_next;
10018     player->move_delay_value_next = -1;
10019   }
10020
10021   player->MovPos =
10022     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10023
10024   player->step_counter++;
10025
10026   PlayerVisit[jx][jy] = FrameCounter;
10027
10028 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10029   player->is_moving = TRUE;
10030 #endif
10031
10032 #if 1
10033   /* should better be called in MovePlayer(), but this breaks some tapes */
10034   ScrollPlayer(player, SCROLL_INIT);
10035 #endif
10036
10037   return MP_MOVING;
10038 }
10039
10040 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10041 {
10042   int jx = player->jx, jy = player->jy;
10043   int old_jx = jx, old_jy = jy;
10044   int moved = MP_NO_ACTION;
10045
10046   if (!player->active)
10047     return FALSE;
10048
10049   if (!dx && !dy)
10050   {
10051     if (player->MovPos == 0)
10052     {
10053       player->is_moving = FALSE;
10054       player->is_digging = FALSE;
10055       player->is_collecting = FALSE;
10056       player->is_snapping = FALSE;
10057       player->is_pushing = FALSE;
10058     }
10059
10060     return FALSE;
10061   }
10062
10063   if (player->move_delay > 0)
10064     return FALSE;
10065
10066   player->move_delay = -1;              /* set to "uninitialized" value */
10067
10068   /* store if player is automatically moved to next field */
10069   player->is_auto_moving = (player->programmed_action != MV_NONE);
10070
10071   /* remove the last programmed player action */
10072   player->programmed_action = 0;
10073
10074   if (player->MovPos)
10075   {
10076     /* should only happen if pre-1.2 tape recordings are played */
10077     /* this is only for backward compatibility */
10078
10079     int original_move_delay_value = player->move_delay_value;
10080
10081 #if DEBUG
10082     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10083            tape.counter);
10084 #endif
10085
10086     /* scroll remaining steps with finest movement resolution */
10087     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10088
10089     while (player->MovPos)
10090     {
10091       ScrollPlayer(player, SCROLL_GO_ON);
10092       ScrollScreen(NULL, SCROLL_GO_ON);
10093
10094       AdvanceFrameAndPlayerCounters(player->index_nr);
10095
10096       DrawAllPlayers();
10097       BackToFront();
10098     }
10099
10100     player->move_delay_value = original_move_delay_value;
10101   }
10102
10103   player->is_active = FALSE;
10104
10105   if (player->last_move_dir & MV_HORIZONTAL)
10106   {
10107     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10108       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10109   }
10110   else
10111   {
10112     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10113       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10114   }
10115
10116 #if USE_FIXED_BORDER_RUNNING_GFX
10117   if (!moved && !player->is_active)
10118   {
10119     player->is_moving = FALSE;
10120     player->is_digging = FALSE;
10121     player->is_collecting = FALSE;
10122     player->is_snapping = FALSE;
10123     player->is_pushing = FALSE;
10124   }
10125 #endif
10126
10127   jx = player->jx;
10128   jy = player->jy;
10129
10130 #if 1
10131   if (moved & MP_MOVING && !ScreenMovPos &&
10132       (player->index_nr == game.centered_player_nr ||
10133        game.centered_player_nr == -1))
10134 #else
10135   if (moved & MP_MOVING && !ScreenMovPos &&
10136       (player == local_player || !options.network))
10137 #endif
10138   {
10139     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10140     int offset = (setup.scroll_delay ? 3 : 0);
10141
10142     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10143     {
10144       /* actual player has left the screen -- scroll in that direction */
10145       if (jx != old_jx)         /* player has moved horizontally */
10146         scroll_x += (jx - old_jx);
10147       else                      /* player has moved vertically */
10148         scroll_y += (jy - old_jy);
10149     }
10150     else
10151     {
10152       if (jx != old_jx)         /* player has moved horizontally */
10153       {
10154         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10155             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10156           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10157
10158         /* don't scroll over playfield boundaries */
10159         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10160           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10161
10162         /* don't scroll more than one field at a time */
10163         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10164
10165         /* don't scroll against the player's moving direction */
10166         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10167             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10168           scroll_x = old_scroll_x;
10169       }
10170       else                      /* player has moved vertically */
10171       {
10172         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10173             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10174           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10175
10176         /* don't scroll over playfield boundaries */
10177         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10178           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10179
10180         /* don't scroll more than one field at a time */
10181         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10182
10183         /* don't scroll against the player's moving direction */
10184         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10185             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10186           scroll_y = old_scroll_y;
10187       }
10188     }
10189
10190     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10191     {
10192 #if 1
10193       if (!options.network && game.centered_player_nr == -1 &&
10194           !AllPlayersInVisibleScreen())
10195       {
10196         scroll_x = old_scroll_x;
10197         scroll_y = old_scroll_y;
10198       }
10199       else
10200 #else
10201       if (!options.network && !AllPlayersInVisibleScreen())
10202       {
10203         scroll_x = old_scroll_x;
10204         scroll_y = old_scroll_y;
10205       }
10206       else
10207 #endif
10208       {
10209         ScrollScreen(player, SCROLL_INIT);
10210         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10211       }
10212     }
10213   }
10214
10215   player->StepFrame = 0;
10216
10217   if (moved & MP_MOVING)
10218   {
10219     if (old_jx != jx && old_jy == jy)
10220       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10221     else if (old_jx == jx && old_jy != jy)
10222       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10223
10224     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10225
10226     player->last_move_dir = player->MovDir;
10227     player->is_moving = TRUE;
10228     player->is_snapping = FALSE;
10229     player->is_switching = FALSE;
10230     player->is_dropping = FALSE;
10231     player->is_dropping_pressed = FALSE;
10232     player->drop_pressed_delay = 0;
10233
10234 #if 0
10235     /* should better be called here than above, but this breaks some tapes */
10236     ScrollPlayer(player, SCROLL_INIT);
10237 #endif
10238   }
10239   else
10240   {
10241     CheckGravityMovementWhenNotMoving(player);
10242
10243     player->is_moving = FALSE;
10244
10245     /* at this point, the player is allowed to move, but cannot move right now
10246        (e.g. because of something blocking the way) -- ensure that the player
10247        is also allowed to move in the next frame (in old versions before 3.1.1,
10248        the player was forced to wait again for eight frames before next try) */
10249
10250     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10251       player->move_delay = 0;   /* allow direct movement in the next frame */
10252   }
10253
10254   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10255     player->move_delay = player->move_delay_value;
10256
10257   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10258   {
10259     TestIfPlayerTouchesBadThing(jx, jy);
10260     TestIfPlayerTouchesCustomElement(jx, jy);
10261   }
10262
10263   if (!player->active)
10264     RemovePlayer(player);
10265
10266   return moved;
10267 }
10268
10269 void ScrollPlayer(struct PlayerInfo *player, int mode)
10270 {
10271   int jx = player->jx, jy = player->jy;
10272   int last_jx = player->last_jx, last_jy = player->last_jy;
10273   int move_stepsize = TILEX / player->move_delay_value;
10274
10275 #if USE_NEW_PLAYER_SPEED
10276   if (!player->active)
10277     return;
10278
10279   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
10280     return;
10281 #else
10282   if (!player->active || player->MovPos == 0)
10283     return;
10284 #endif
10285
10286   if (mode == SCROLL_INIT)
10287   {
10288     player->actual_frame_counter = FrameCounter;
10289     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10290
10291     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10292         Feld[last_jx][last_jy] == EL_EMPTY)
10293     {
10294       int last_field_block_delay = 0;   /* start with no blocking at all */
10295       int block_delay_adjustment = player->block_delay_adjustment;
10296
10297       /* if player blocks last field, add delay for exactly one move */
10298       if (player->block_last_field)
10299       {
10300         last_field_block_delay += player->move_delay_value;
10301
10302         /* when blocking enabled, prevent moving up despite gravity */
10303 #if USE_PLAYER_GRAVITY
10304         if (player->gravity && player->MovDir == MV_UP)
10305           block_delay_adjustment = -1;
10306 #else
10307         if (game.gravity && player->MovDir == MV_UP)
10308           block_delay_adjustment = -1;
10309 #endif
10310       }
10311
10312       /* add block delay adjustment (also possible when not blocking) */
10313       last_field_block_delay += block_delay_adjustment;
10314
10315       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10316       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10317     }
10318
10319 #if USE_NEW_PLAYER_SPEED
10320     if (player->MovPos != 0)    /* player has not yet reached destination */
10321       return;
10322 #else
10323     return;
10324 #endif
10325   }
10326   else if (!FrameReached(&player->actual_frame_counter, 1))
10327     return;
10328
10329 #if USE_NEW_PLAYER_SPEED
10330   if (player->MovPos != 0)
10331   {
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   }
10339 #else
10340   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10341   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10342
10343   /* before DrawPlayer() to draw correct player graphic for this case */
10344   if (player->MovPos == 0)
10345     CheckGravityMovement(player);
10346 #endif
10347
10348   if (player->MovPos == 0)      /* player reached destination field */
10349   {
10350     if (player->move_delay_reset_counter > 0)
10351     {
10352       player->move_delay_reset_counter--;
10353
10354       if (player->move_delay_reset_counter == 0)
10355       {
10356         /* continue with normal speed after quickly moving through gate */
10357         HALVE_PLAYER_SPEED(player);
10358
10359         /* be able to make the next move without delay */
10360         player->move_delay = 0;
10361       }
10362     }
10363
10364     player->last_jx = jx;
10365     player->last_jy = jy;
10366
10367     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10368         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10369         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10370     {
10371       DrawPlayer(player);       /* needed here only to cleanup last field */
10372       RemovePlayer(player);
10373
10374       if (local_player->friends_still_needed == 0 ||
10375           IS_SP_ELEMENT(Feld[jx][jy]))
10376         PlayerWins(player);
10377     }
10378
10379     /* this breaks one level: "machine", level 000 */
10380     {
10381       int move_direction = player->MovDir;
10382       int enter_side = MV_DIR_OPPOSITE(move_direction);
10383       int leave_side = move_direction;
10384       int old_jx = last_jx;
10385       int old_jy = last_jy;
10386       int old_element = Feld[old_jx][old_jy];
10387       int new_element = Feld[jx][jy];
10388
10389       if (IS_CUSTOM_ELEMENT(old_element))
10390         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10391                                    CE_LEFT_BY_PLAYER,
10392                                    player->index_bit, leave_side);
10393
10394       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10395                                           CE_PLAYER_LEAVES_X,
10396                                           player->index_bit, leave_side);
10397
10398       if (IS_CUSTOM_ELEMENT(new_element))
10399         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10400                                    player->index_bit, enter_side);
10401
10402       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10403                                           CE_PLAYER_ENTERS_X,
10404                                           player->index_bit, enter_side);
10405
10406       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10407                                         CE_MOVE_OF_X, move_direction);
10408     }
10409
10410     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10411     {
10412       TestIfPlayerTouchesBadThing(jx, jy);
10413       TestIfPlayerTouchesCustomElement(jx, jy);
10414
10415       /* needed because pushed element has not yet reached its destination,
10416          so it would trigger a change event at its previous field location */
10417       if (!player->is_pushing)
10418         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10419
10420       if (!player->active)
10421         RemovePlayer(player);
10422     }
10423
10424     if (!local_player->LevelSolved && level.use_step_counter)
10425     {
10426       int i;
10427
10428       TimePlayed++;
10429
10430       if (TimeLeft > 0)
10431       {
10432         TimeLeft--;
10433
10434         if (TimeLeft <= 10 && setup.time_limit)
10435           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10436
10437         DrawGameValue_Time(TimeLeft);
10438
10439         if (!TimeLeft && setup.time_limit)
10440           for (i = 0; i < MAX_PLAYERS; i++)
10441             KillPlayer(&stored_player[i]);
10442       }
10443       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10444         DrawGameValue_Time(TimePlayed);
10445     }
10446
10447     if (tape.single_step && tape.recording && !tape.pausing &&
10448         !player->programmed_action)
10449       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10450   }
10451 }
10452
10453 void ScrollScreen(struct PlayerInfo *player, int mode)
10454 {
10455   static unsigned long screen_frame_counter = 0;
10456
10457   if (mode == SCROLL_INIT)
10458   {
10459     /* set scrolling step size according to actual player's moving speed */
10460     ScrollStepSize = TILEX / player->move_delay_value;
10461
10462     screen_frame_counter = FrameCounter;
10463     ScreenMovDir = player->MovDir;
10464     ScreenMovPos = player->MovPos;
10465     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10466     return;
10467   }
10468   else if (!FrameReached(&screen_frame_counter, 1))
10469     return;
10470
10471   if (ScreenMovPos)
10472   {
10473     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10474     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10475     redraw_mask |= REDRAW_FIELD;
10476   }
10477   else
10478     ScreenMovDir = MV_NONE;
10479 }
10480
10481 void TestIfPlayerTouchesCustomElement(int x, int y)
10482 {
10483   static int xy[4][2] =
10484   {
10485     { 0, -1 },
10486     { -1, 0 },
10487     { +1, 0 },
10488     { 0, +1 }
10489   };
10490   static int trigger_sides[4][2] =
10491   {
10492     /* center side       border side */
10493     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10494     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10495     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10496     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10497   };
10498   static int touch_dir[4] =
10499   {
10500     MV_LEFT | MV_RIGHT,
10501     MV_UP   | MV_DOWN,
10502     MV_UP   | MV_DOWN,
10503     MV_LEFT | MV_RIGHT
10504   };
10505   int center_element = Feld[x][y];      /* should always be non-moving! */
10506   int i;
10507
10508   for (i = 0; i < NUM_DIRECTIONS; i++)
10509   {
10510     int xx = x + xy[i][0];
10511     int yy = y + xy[i][1];
10512     int center_side = trigger_sides[i][0];
10513     int border_side = trigger_sides[i][1];
10514     int border_element;
10515
10516     if (!IN_LEV_FIELD(xx, yy))
10517       continue;
10518
10519     if (IS_PLAYER(x, y))
10520     {
10521       struct PlayerInfo *player = PLAYERINFO(x, y);
10522
10523       if (game.engine_version < VERSION_IDENT(3,0,7,0))
10524         border_element = Feld[xx][yy];          /* may be moving! */
10525       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10526         border_element = Feld[xx][yy];
10527       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
10528         border_element = MovingOrBlocked2Element(xx, yy);
10529       else
10530         continue;               /* center and border element do not touch */
10531
10532       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10533                                  player->index_bit, border_side);
10534       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10535                                           CE_PLAYER_TOUCHES_X,
10536                                           player->index_bit, border_side);
10537     }
10538     else if (IS_PLAYER(xx, yy))
10539     {
10540       struct PlayerInfo *player = PLAYERINFO(xx, yy);
10541
10542       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10543       {
10544         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10545           continue;             /* center and border element do not touch */
10546       }
10547
10548       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10549                                  player->index_bit, center_side);
10550       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10551                                           CE_PLAYER_TOUCHES_X,
10552                                           player->index_bit, center_side);
10553       break;
10554     }
10555   }
10556 }
10557
10558 #if USE_ELEMENT_TOUCHING_BUGFIX
10559
10560 void TestIfElementTouchesCustomElement(int x, int y)
10561 {
10562   static int xy[4][2] =
10563   {
10564     { 0, -1 },
10565     { -1, 0 },
10566     { +1, 0 },
10567     { 0, +1 }
10568   };
10569   static int trigger_sides[4][2] =
10570   {
10571     /* center side      border side */
10572     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10573     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10574     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10575     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10576   };
10577   static int touch_dir[4] =
10578   {
10579     MV_LEFT | MV_RIGHT,
10580     MV_UP   | MV_DOWN,
10581     MV_UP   | MV_DOWN,
10582     MV_LEFT | MV_RIGHT
10583   };
10584   boolean change_center_element = FALSE;
10585   int center_element = Feld[x][y];      /* should always be non-moving! */
10586   int border_element_old[NUM_DIRECTIONS];
10587   int i;
10588
10589   for (i = 0; i < NUM_DIRECTIONS; i++)
10590   {
10591     int xx = x + xy[i][0];
10592     int yy = y + xy[i][1];
10593     int border_element;
10594
10595     border_element_old[i] = -1;
10596
10597     if (!IN_LEV_FIELD(xx, yy))
10598       continue;
10599
10600     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10601       border_element = Feld[xx][yy];    /* may be moving! */
10602     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10603       border_element = Feld[xx][yy];
10604     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10605       border_element = MovingOrBlocked2Element(xx, yy);
10606     else
10607       continue;                 /* center and border element do not touch */
10608
10609     border_element_old[i] = border_element;
10610   }
10611
10612   for (i = 0; i < NUM_DIRECTIONS; i++)
10613   {
10614     int xx = x + xy[i][0];
10615     int yy = y + xy[i][1];
10616     int center_side = trigger_sides[i][0];
10617     int border_element = border_element_old[i];
10618
10619     if (border_element == -1)
10620       continue;
10621
10622     /* check for change of border element */
10623     CheckElementChangeBySide(xx, yy, border_element, center_element,
10624                              CE_TOUCHING_X, center_side);
10625   }
10626
10627   for (i = 0; i < NUM_DIRECTIONS; i++)
10628   {
10629     int border_side = trigger_sides[i][1];
10630     int border_element = border_element_old[i];
10631
10632     if (border_element == -1)
10633       continue;
10634
10635     /* check for change of center element (but change it only once) */
10636     if (!change_center_element)
10637       change_center_element =
10638         CheckElementChangeBySide(x, y, center_element, border_element,
10639                                  CE_TOUCHING_X, border_side);
10640   }
10641 }
10642
10643 #else
10644
10645 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10646 {
10647   static int xy[4][2] =
10648   {
10649     { 0, -1 },
10650     { -1, 0 },
10651     { +1, 0 },
10652     { 0, +1 }
10653   };
10654   static int trigger_sides[4][2] =
10655   {
10656     /* center side      border side */
10657     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10658     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10659     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10660     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10661   };
10662   static int touch_dir[4] =
10663   {
10664     MV_LEFT | MV_RIGHT,
10665     MV_UP   | MV_DOWN,
10666     MV_UP   | MV_DOWN,
10667     MV_LEFT | MV_RIGHT
10668   };
10669   boolean change_center_element = FALSE;
10670   int center_element = Feld[x][y];      /* should always be non-moving! */
10671   int i;
10672
10673   for (i = 0; i < NUM_DIRECTIONS; i++)
10674   {
10675     int xx = x + xy[i][0];
10676     int yy = y + xy[i][1];
10677     int center_side = trigger_sides[i][0];
10678     int border_side = trigger_sides[i][1];
10679     int border_element;
10680
10681     if (!IN_LEV_FIELD(xx, yy))
10682       continue;
10683
10684     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10685       border_element = Feld[xx][yy];    /* may be moving! */
10686     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10687       border_element = Feld[xx][yy];
10688     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10689       border_element = MovingOrBlocked2Element(xx, yy);
10690     else
10691       continue;                 /* center and border element do not touch */
10692
10693     /* check for change of center element (but change it only once) */
10694     if (!change_center_element)
10695       change_center_element =
10696         CheckElementChangeBySide(x, y, center_element, border_element,
10697                                  CE_TOUCHING_X, border_side);
10698
10699     /* check for change of border element */
10700     CheckElementChangeBySide(xx, yy, border_element, center_element,
10701                              CE_TOUCHING_X, center_side);
10702   }
10703 }
10704
10705 #endif
10706
10707 void TestIfElementHitsCustomElement(int x, int y, int direction)
10708 {
10709   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10710   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10711   int hitx = x + dx, hity = y + dy;
10712   int hitting_element = Feld[x][y];
10713   int touched_element;
10714
10715   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10716     return;
10717
10718   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10719                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10720
10721   if (IN_LEV_FIELD(hitx, hity))
10722   {
10723     int opposite_direction = MV_DIR_OPPOSITE(direction);
10724     int hitting_side = direction;
10725     int touched_side = opposite_direction;
10726     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10727                           MovDir[hitx][hity] != direction ||
10728                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10729
10730     object_hit = TRUE;
10731
10732     if (object_hit)
10733     {
10734       CheckElementChangeBySide(x, y, hitting_element, touched_element,
10735                                CE_HITTING_X, touched_side);
10736
10737       CheckElementChangeBySide(hitx, hity, touched_element,
10738                                hitting_element, CE_HIT_BY_X, hitting_side);
10739
10740       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10741                                CE_HIT_BY_SOMETHING, opposite_direction);
10742     }
10743   }
10744
10745   /* "hitting something" is also true when hitting the playfield border */
10746   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10747                            CE_HITTING_SOMETHING, direction);
10748 }
10749
10750 #if 0
10751 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10752 {
10753   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10754   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10755   int hitx = x + dx, hity = y + dy;
10756   int hitting_element = Feld[x][y];
10757   int touched_element;
10758 #if 0
10759   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10760                         !IS_FREE(hitx, hity) &&
10761                         (!IS_MOVING(hitx, hity) ||
10762                          MovDir[hitx][hity] != direction ||
10763                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10764 #endif
10765
10766   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10767     return;
10768
10769 #if 0
10770   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10771     return;
10772 #endif
10773
10774   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10775                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10776
10777   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10778                            EP_CAN_SMASH_EVERYTHING, direction);
10779
10780   if (IN_LEV_FIELD(hitx, hity))
10781   {
10782     int opposite_direction = MV_DIR_OPPOSITE(direction);
10783     int hitting_side = direction;
10784     int touched_side = opposite_direction;
10785 #if 0
10786     int touched_element = MovingOrBlocked2Element(hitx, hity);
10787 #endif
10788 #if 1
10789     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10790                           MovDir[hitx][hity] != direction ||
10791                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10792
10793     object_hit = TRUE;
10794 #endif
10795
10796     if (object_hit)
10797     {
10798       int i;
10799
10800       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10801                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10802
10803       CheckElementChangeBySide(x, y, hitting_element, touched_element,
10804                                CE_OTHER_IS_SMASHING, touched_side);
10805
10806       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10807                                CE_OTHER_GETS_SMASHED, hitting_side);
10808     }
10809   }
10810 }
10811 #endif
10812
10813 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10814 {
10815   int i, kill_x = -1, kill_y = -1;
10816
10817   int bad_element = -1;
10818   static int test_xy[4][2] =
10819   {
10820     { 0, -1 },
10821     { -1, 0 },
10822     { +1, 0 },
10823     { 0, +1 }
10824   };
10825   static int test_dir[4] =
10826   {
10827     MV_UP,
10828     MV_LEFT,
10829     MV_RIGHT,
10830     MV_DOWN
10831   };
10832
10833   for (i = 0; i < NUM_DIRECTIONS; i++)
10834   {
10835     int test_x, test_y, test_move_dir, test_element;
10836
10837     test_x = good_x + test_xy[i][0];
10838     test_y = good_y + test_xy[i][1];
10839
10840     if (!IN_LEV_FIELD(test_x, test_y))
10841       continue;
10842
10843     test_move_dir =
10844       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10845
10846     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10847
10848     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10849        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10850     */
10851     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10852         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
10853     {
10854       kill_x = test_x;
10855       kill_y = test_y;
10856       bad_element = test_element;
10857
10858       break;
10859     }
10860   }
10861
10862   if (kill_x != -1 || kill_y != -1)
10863   {
10864     if (IS_PLAYER(good_x, good_y))
10865     {
10866       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10867
10868       if (player->shield_deadly_time_left > 0 &&
10869           !IS_INDESTRUCTIBLE(bad_element))
10870         Bang(kill_x, kill_y);
10871       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10872         KillPlayer(player);
10873     }
10874     else
10875       Bang(good_x, good_y);
10876   }
10877 }
10878
10879 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10880 {
10881   int i, kill_x = -1, kill_y = -1;
10882   int bad_element = Feld[bad_x][bad_y];
10883   static int test_xy[4][2] =
10884   {
10885     { 0, -1 },
10886     { -1, 0 },
10887     { +1, 0 },
10888     { 0, +1 }
10889   };
10890   static int touch_dir[4] =
10891   {
10892     MV_LEFT | MV_RIGHT,
10893     MV_UP   | MV_DOWN,
10894     MV_UP   | MV_DOWN,
10895     MV_LEFT | MV_RIGHT
10896   };
10897   static int test_dir[4] =
10898   {
10899     MV_UP,
10900     MV_LEFT,
10901     MV_RIGHT,
10902     MV_DOWN
10903   };
10904
10905   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
10906     return;
10907
10908   for (i = 0; i < NUM_DIRECTIONS; i++)
10909   {
10910     int test_x, test_y, test_move_dir, test_element;
10911
10912     test_x = bad_x + test_xy[i][0];
10913     test_y = bad_y + test_xy[i][1];
10914     if (!IN_LEV_FIELD(test_x, test_y))
10915       continue;
10916
10917     test_move_dir =
10918       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10919
10920     test_element = Feld[test_x][test_y];
10921
10922     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10923        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10924     */
10925     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
10926         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
10927     {
10928       /* good thing is player or penguin that does not move away */
10929       if (IS_PLAYER(test_x, test_y))
10930       {
10931         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10932
10933         if (bad_element == EL_ROBOT && player->is_moving)
10934           continue;     /* robot does not kill player if he is moving */
10935
10936         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10937         {
10938           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10939             continue;           /* center and border element do not touch */
10940         }
10941
10942         kill_x = test_x;
10943         kill_y = test_y;
10944         break;
10945       }
10946       else if (test_element == EL_PENGUIN)
10947       {
10948         kill_x = test_x;
10949         kill_y = test_y;
10950         break;
10951       }
10952     }
10953   }
10954
10955   if (kill_x != -1 || kill_y != -1)
10956   {
10957     if (IS_PLAYER(kill_x, kill_y))
10958     {
10959       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10960
10961       if (player->shield_deadly_time_left > 0 &&
10962           !IS_INDESTRUCTIBLE(bad_element))
10963         Bang(bad_x, bad_y);
10964       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10965         KillPlayer(player);
10966     }
10967     else
10968       Bang(kill_x, kill_y);
10969   }
10970 }
10971
10972 void TestIfPlayerTouchesBadThing(int x, int y)
10973 {
10974   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10975 }
10976
10977 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10978 {
10979   TestIfGoodThingHitsBadThing(x, y, move_dir);
10980 }
10981
10982 void TestIfBadThingTouchesPlayer(int x, int y)
10983 {
10984   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10985 }
10986
10987 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10988 {
10989   TestIfBadThingHitsGoodThing(x, y, move_dir);
10990 }
10991
10992 void TestIfFriendTouchesBadThing(int x, int y)
10993 {
10994   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10995 }
10996
10997 void TestIfBadThingTouchesFriend(int x, int y)
10998 {
10999   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11000 }
11001
11002 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11003 {
11004   int i, kill_x = bad_x, kill_y = bad_y;
11005   static int xy[4][2] =
11006   {
11007     { 0, -1 },
11008     { -1, 0 },
11009     { +1, 0 },
11010     { 0, +1 }
11011   };
11012
11013   for (i = 0; i < NUM_DIRECTIONS; i++)
11014   {
11015     int x, y, element;
11016
11017     x = bad_x + xy[i][0];
11018     y = bad_y + xy[i][1];
11019     if (!IN_LEV_FIELD(x, y))
11020       continue;
11021
11022     element = Feld[x][y];
11023     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11024         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11025     {
11026       kill_x = x;
11027       kill_y = y;
11028       break;
11029     }
11030   }
11031
11032   if (kill_x != bad_x || kill_y != bad_y)
11033     Bang(bad_x, bad_y);
11034 }
11035
11036 void KillPlayer(struct PlayerInfo *player)
11037 {
11038   int jx = player->jx, jy = player->jy;
11039
11040   if (!player->active)
11041     return;
11042
11043   /* remove accessible field at the player's position */
11044   Feld[jx][jy] = EL_EMPTY;
11045
11046   /* deactivate shield (else Bang()/Explode() would not work right) */
11047   player->shield_normal_time_left = 0;
11048   player->shield_deadly_time_left = 0;
11049
11050   Bang(jx, jy);
11051   BuryPlayer(player);
11052 }
11053
11054 static void KillPlayerUnlessEnemyProtected(int x, int y)
11055 {
11056   if (!PLAYER_ENEMY_PROTECTED(x, y))
11057     KillPlayer(PLAYERINFO(x, y));
11058 }
11059
11060 static void KillPlayerUnlessExplosionProtected(int x, int y)
11061 {
11062   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11063     KillPlayer(PLAYERINFO(x, y));
11064 }
11065
11066 void BuryPlayer(struct PlayerInfo *player)
11067 {
11068   int jx = player->jx, jy = player->jy;
11069
11070   if (!player->active)
11071     return;
11072
11073   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11074   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11075
11076   player->GameOver = TRUE;
11077   RemovePlayer(player);
11078 }
11079
11080 void RemovePlayer(struct PlayerInfo *player)
11081 {
11082   int jx = player->jx, jy = player->jy;
11083   int i, found = FALSE;
11084
11085   player->present = FALSE;
11086   player->active = FALSE;
11087
11088   if (!ExplodeField[jx][jy])
11089     StorePlayer[jx][jy] = 0;
11090
11091   if (player->is_moving)
11092     DrawLevelField(player->last_jx, player->last_jy);
11093
11094   for (i = 0; i < MAX_PLAYERS; i++)
11095     if (stored_player[i].active)
11096       found = TRUE;
11097
11098   if (!found)
11099     AllPlayersGone = TRUE;
11100
11101   ExitX = ZX = jx;
11102   ExitY = ZY = jy;
11103 }
11104
11105 #if USE_NEW_SNAP_DELAY
11106 static void setFieldForSnapping(int x, int y, int element, int direction)
11107 {
11108   struct ElementInfo *ei = &element_info[element];
11109   int direction_bit = MV_DIR_TO_BIT(direction);
11110   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11111   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11112                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11113
11114   Feld[x][y] = EL_ELEMENT_SNAPPING;
11115   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11116
11117   ResetGfxAnimation(x, y);
11118
11119   GfxElement[x][y] = element;
11120   GfxAction[x][y] = action;
11121   GfxDir[x][y] = direction;
11122   GfxFrame[x][y] = -1;
11123 }
11124 #endif
11125
11126 /*
11127   =============================================================================
11128   checkDiagonalPushing()
11129   -----------------------------------------------------------------------------
11130   check if diagonal input device direction results in pushing of object
11131   (by checking if the alternative direction is walkable, diggable, ...)
11132   =============================================================================
11133 */
11134
11135 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11136                                     int x, int y, int real_dx, int real_dy)
11137 {
11138   int jx, jy, dx, dy, xx, yy;
11139
11140   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11141     return TRUE;
11142
11143   /* diagonal direction: check alternative direction */
11144   jx = player->jx;
11145   jy = player->jy;
11146   dx = x - jx;
11147   dy = y - jy;
11148   xx = jx + (dx == 0 ? real_dx : 0);
11149   yy = jy + (dy == 0 ? real_dy : 0);
11150
11151   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11152 }
11153
11154 /*
11155   =============================================================================
11156   DigField()
11157   -----------------------------------------------------------------------------
11158   x, y:                 field next to player (non-diagonal) to try to dig to
11159   real_dx, real_dy:     direction as read from input device (can be diagonal)
11160   =============================================================================
11161 */
11162
11163 int DigField(struct PlayerInfo *player,
11164              int oldx, int oldy, int x, int y,
11165              int real_dx, int real_dy, int mode)
11166 {
11167   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11168   boolean player_was_pushing = player->is_pushing;
11169   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11170   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11171   int jx = oldx, jy = oldy;
11172   int dx = x - jx, dy = y - jy;
11173   int nextx = x + dx, nexty = y + dy;
11174   int move_direction = (dx == -1 ? MV_LEFT  :
11175                         dx == +1 ? MV_RIGHT :
11176                         dy == -1 ? MV_UP    :
11177                         dy == +1 ? MV_DOWN  : MV_NONE);
11178   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11179   int dig_side = MV_DIR_OPPOSITE(move_direction);
11180   int old_element = Feld[jx][jy];
11181 #if USE_FIXED_DONT_RUN_INTO
11182   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11183 #else
11184   int element;
11185 #endif
11186   int collect_count;
11187
11188   if (is_player)                /* function can also be called by EL_PENGUIN */
11189   {
11190     if (player->MovPos == 0)
11191     {
11192       player->is_digging = FALSE;
11193       player->is_collecting = FALSE;
11194     }
11195
11196     if (player->MovPos == 0)    /* last pushing move finished */
11197       player->is_pushing = FALSE;
11198
11199     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11200     {
11201       player->is_switching = FALSE;
11202       player->push_delay = -1;
11203
11204       return MP_NO_ACTION;
11205     }
11206   }
11207
11208 #if !USE_FIXED_DONT_RUN_INTO
11209   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11210     return MP_NO_ACTION;
11211 #endif
11212
11213   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11214     old_element = Back[jx][jy];
11215
11216   /* in case of element dropped at player position, check background */
11217   else if (Back[jx][jy] != EL_EMPTY &&
11218            game.engine_version >= VERSION_IDENT(2,2,0,0))
11219     old_element = Back[jx][jy];
11220
11221   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11222     return MP_NO_ACTION;        /* field has no opening in this direction */
11223
11224   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11225     return MP_NO_ACTION;        /* field has no opening in this direction */
11226
11227 #if USE_FIXED_DONT_RUN_INTO
11228   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11229   {
11230     SplashAcid(x, y);
11231
11232     Feld[jx][jy] = player->artwork_element;
11233     InitMovingField(jx, jy, MV_DOWN);
11234     Store[jx][jy] = EL_ACID;
11235     ContinueMoving(jx, jy);
11236     BuryPlayer(player);
11237
11238     return MP_DONT_RUN_INTO;
11239   }
11240 #endif
11241
11242 #if USE_FIXED_DONT_RUN_INTO
11243   if (player_can_move && DONT_RUN_INTO(element))
11244   {
11245     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11246
11247     return MP_DONT_RUN_INTO;
11248   }
11249 #endif
11250
11251 #if USE_FIXED_DONT_RUN_INTO
11252   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11253     return MP_NO_ACTION;
11254 #endif
11255
11256 #if !USE_FIXED_DONT_RUN_INTO
11257   element = Feld[x][y];
11258 #endif
11259
11260   collect_count = element_info[element].collect_count_initial;
11261
11262   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11263     return MP_NO_ACTION;
11264
11265   if (game.engine_version < VERSION_IDENT(2,2,0,0))
11266     player_can_move = player_can_move_or_snap;
11267
11268   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11269       game.engine_version >= VERSION_IDENT(2,2,0,0))
11270   {
11271     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11272                                player->index_bit, dig_side);
11273     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11274                                         player->index_bit, dig_side);
11275
11276     if (Feld[x][y] != element)          /* field changed by snapping */
11277       return MP_ACTION;
11278
11279     return MP_NO_ACTION;
11280   }
11281
11282 #if USE_PLAYER_GRAVITY
11283   if (player->gravity && is_player && !player->is_auto_moving &&
11284       canFallDown(player) && move_direction != MV_DOWN &&
11285       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11286     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11287 #else
11288   if (game.gravity && is_player && !player->is_auto_moving &&
11289       canFallDown(player) && move_direction != MV_DOWN &&
11290       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11291     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11292 #endif
11293
11294   if (player_can_move &&
11295       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11296   {
11297     int sound_element = SND_ELEMENT(element);
11298     int sound_action = ACTION_WALKING;
11299
11300     if (IS_RND_GATE(element))
11301     {
11302       if (!player->key[RND_GATE_NR(element)])
11303         return MP_NO_ACTION;
11304     }
11305     else if (IS_RND_GATE_GRAY(element))
11306     {
11307       if (!player->key[RND_GATE_GRAY_NR(element)])
11308         return MP_NO_ACTION;
11309     }
11310     else if (IS_RND_GATE_GRAY_ACTIVE(element))
11311     {
11312       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11313         return MP_NO_ACTION;
11314     }
11315     else if (element == EL_EXIT_OPEN ||
11316              element == EL_SP_EXIT_OPEN ||
11317              element == EL_SP_EXIT_OPENING)
11318     {
11319       sound_action = ACTION_PASSING;    /* player is passing exit */
11320     }
11321     else if (element == EL_EMPTY)
11322     {
11323       sound_action = ACTION_MOVING;             /* nothing to walk on */
11324     }
11325
11326     /* play sound from background or player, whatever is available */
11327     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11328       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11329     else
11330       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11331   }
11332   else if (player_can_move &&
11333            IS_PASSABLE(element) && canPassField(x, y, move_direction))
11334   {
11335     if (!ACCESS_FROM(element, opposite_direction))
11336       return MP_NO_ACTION;      /* field not accessible from this direction */
11337
11338     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
11339       return MP_NO_ACTION;
11340
11341     if (IS_EM_GATE(element))
11342     {
11343       if (!player->key[EM_GATE_NR(element)])
11344         return MP_NO_ACTION;
11345     }
11346     else if (IS_EM_GATE_GRAY(element))
11347     {
11348       if (!player->key[EM_GATE_GRAY_NR(element)])
11349         return MP_NO_ACTION;
11350     }
11351     else if (IS_EM_GATE_GRAY_ACTIVE(element))
11352     {
11353       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11354         return MP_NO_ACTION;
11355     }
11356     else if (IS_EMC_GATE(element))
11357     {
11358       if (!player->key[EMC_GATE_NR(element)])
11359         return MP_NO_ACTION;
11360     }
11361     else if (IS_EMC_GATE_GRAY(element))
11362     {
11363       if (!player->key[EMC_GATE_GRAY_NR(element)])
11364         return MP_NO_ACTION;
11365     }
11366     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11367     {
11368       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11369         return MP_NO_ACTION;
11370     }
11371     else if (IS_SP_PORT(element))
11372     {
11373       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11374           element == EL_SP_GRAVITY_PORT_RIGHT ||
11375           element == EL_SP_GRAVITY_PORT_UP ||
11376           element == EL_SP_GRAVITY_PORT_DOWN)
11377 #if USE_PLAYER_GRAVITY
11378         player->gravity = !player->gravity;
11379 #else
11380         game.gravity = !game.gravity;
11381 #endif
11382       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11383                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11384                element == EL_SP_GRAVITY_ON_PORT_UP ||
11385                element == EL_SP_GRAVITY_ON_PORT_DOWN)
11386 #if USE_PLAYER_GRAVITY
11387         player->gravity = TRUE;
11388 #else
11389         game.gravity = TRUE;
11390 #endif
11391       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11392                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11393                element == EL_SP_GRAVITY_OFF_PORT_UP ||
11394                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11395 #if USE_PLAYER_GRAVITY
11396         player->gravity = FALSE;
11397 #else
11398         game.gravity = FALSE;
11399 #endif
11400     }
11401
11402     /* automatically move to the next field with double speed */
11403     player->programmed_action = move_direction;
11404
11405     if (player->move_delay_reset_counter == 0)
11406     {
11407       player->move_delay_reset_counter = 2;     /* two double speed steps */
11408
11409       DOUBLE_PLAYER_SPEED(player);
11410     }
11411
11412     PlayLevelSoundAction(x, y, ACTION_PASSING);
11413   }
11414   else if (player_can_move_or_snap && IS_DIGGABLE(element))
11415   {
11416     RemoveField(x, y);
11417
11418     if (mode != DF_SNAP)
11419     {
11420       GfxElement[x][y] = GFX_ELEMENT(element);
11421       player->is_digging = TRUE;
11422     }
11423
11424     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11425
11426     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11427                                         player->index_bit, dig_side);
11428
11429     if (mode == DF_SNAP)
11430     {
11431 #if USE_NEW_SNAP_DELAY
11432       if (level.block_snap_field)
11433         setFieldForSnapping(x, y, element, move_direction);
11434       else
11435         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11436 #else
11437       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11438 #endif
11439
11440       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11441                                           player->index_bit, dig_side);
11442     }
11443   }
11444   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11445   {
11446     RemoveField(x, y);
11447
11448     if (is_player && mode != DF_SNAP)
11449     {
11450       GfxElement[x][y] = element;
11451       player->is_collecting = TRUE;
11452     }
11453
11454     if (element == EL_SPEED_PILL)
11455     {
11456       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11457     }
11458     else if (element == EL_EXTRA_TIME && level.time > 0)
11459     {
11460       TimeLeft += level.extra_time;
11461       DrawGameValue_Time(TimeLeft);
11462     }
11463     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11464     {
11465       player->shield_normal_time_left += level.shield_normal_time;
11466       if (element == EL_SHIELD_DEADLY)
11467         player->shield_deadly_time_left += level.shield_deadly_time;
11468     }
11469     else if (element == EL_DYNAMITE ||
11470              element == EL_EM_DYNAMITE ||
11471              element == EL_SP_DISK_RED)
11472     {
11473       if (player->inventory_size < MAX_INVENTORY_SIZE)
11474         player->inventory_element[player->inventory_size++] = element;
11475
11476       DrawGameDoorValues();
11477     }
11478     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11479     {
11480       player->dynabomb_count++;
11481       player->dynabombs_left++;
11482     }
11483     else if (element == EL_DYNABOMB_INCREASE_SIZE)
11484     {
11485       player->dynabomb_size++;
11486     }
11487     else if (element == EL_DYNABOMB_INCREASE_POWER)
11488     {
11489       player->dynabomb_xl = TRUE;
11490     }
11491     else if (IS_KEY(element))
11492     {
11493       player->key[KEY_NR(element)] = TRUE;
11494
11495       DrawGameDoorValues();
11496     }
11497     else if (IS_ENVELOPE(element))
11498     {
11499       player->show_envelope = element;
11500     }
11501     else if (element == EL_EMC_LENSES)
11502     {
11503       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11504
11505       RedrawAllInvisibleElementsForLenses();
11506     }
11507     else if (element == EL_EMC_MAGNIFIER)
11508     {
11509       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11510
11511       RedrawAllInvisibleElementsForMagnifier();
11512     }
11513     else if (IS_DROPPABLE(element) ||
11514              IS_THROWABLE(element))     /* can be collected and dropped */
11515     {
11516       int i;
11517
11518       if (collect_count == 0)
11519         player->inventory_infinite_element = element;
11520       else
11521         for (i = 0; i < collect_count; i++)
11522           if (player->inventory_size < MAX_INVENTORY_SIZE)
11523             player->inventory_element[player->inventory_size++] = element;
11524
11525       DrawGameDoorValues();
11526     }
11527     else if (collect_count > 0)
11528     {
11529       local_player->gems_still_needed -= collect_count;
11530       if (local_player->gems_still_needed < 0)
11531         local_player->gems_still_needed = 0;
11532
11533       DrawGameValue_Emeralds(local_player->gems_still_needed);
11534     }
11535
11536     RaiseScoreElement(element);
11537     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11538
11539     if (is_player)
11540       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11541                                           player->index_bit, dig_side);
11542
11543     if (mode == DF_SNAP)
11544     {
11545 #if USE_NEW_SNAP_DELAY
11546       if (level.block_snap_field)
11547         setFieldForSnapping(x, y, element, move_direction);
11548       else
11549         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11550 #else
11551       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11552 #endif
11553
11554       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11555                                           player->index_bit, dig_side);
11556     }
11557   }
11558   else if (player_can_move_or_snap && IS_PUSHABLE(element))
11559   {
11560     if (mode == DF_SNAP && element != EL_BD_ROCK)
11561       return MP_NO_ACTION;
11562
11563     if (CAN_FALL(element) && dy)
11564       return MP_NO_ACTION;
11565
11566     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11567         !(element == EL_SPRING && level.use_spring_bug))
11568       return MP_NO_ACTION;
11569
11570     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11571         ((move_direction & MV_VERTICAL &&
11572           ((element_info[element].move_pattern & MV_LEFT &&
11573             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11574            (element_info[element].move_pattern & MV_RIGHT &&
11575             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11576          (move_direction & MV_HORIZONTAL &&
11577           ((element_info[element].move_pattern & MV_UP &&
11578             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11579            (element_info[element].move_pattern & MV_DOWN &&
11580             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11581       return MP_NO_ACTION;
11582
11583     /* do not push elements already moving away faster than player */
11584     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11585         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11586       return MP_NO_ACTION;
11587
11588     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11589     {
11590       if (player->push_delay_value == -1 || !player_was_pushing)
11591         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11592     }
11593     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11594     {
11595       if (player->push_delay_value == -1)
11596         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11597     }
11598     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11599     {
11600       if (!player->is_pushing)
11601         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11602     }
11603
11604     player->is_pushing = TRUE;
11605     player->is_active = TRUE;
11606
11607     if (!(IN_LEV_FIELD(nextx, nexty) &&
11608           (IS_FREE(nextx, nexty) ||
11609            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11610             IS_SB_ELEMENT(element)))))
11611       return MP_NO_ACTION;
11612
11613     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11614       return MP_NO_ACTION;
11615
11616     if (player->push_delay == -1)       /* new pushing; restart delay */
11617       player->push_delay = 0;
11618
11619     if (player->push_delay < player->push_delay_value &&
11620         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11621         element != EL_SPRING && element != EL_BALLOON)
11622     {
11623       /* make sure that there is no move delay before next try to push */
11624       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11625         player->move_delay = 0;
11626
11627       return MP_NO_ACTION;
11628     }
11629
11630     if (IS_SB_ELEMENT(element))
11631     {
11632       if (element == EL_SOKOBAN_FIELD_FULL)
11633       {
11634         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11635         local_player->sokobanfields_still_needed++;
11636       }
11637
11638       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11639       {
11640         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11641         local_player->sokobanfields_still_needed--;
11642       }
11643
11644       Feld[x][y] = EL_SOKOBAN_OBJECT;
11645
11646       if (Back[x][y] == Back[nextx][nexty])
11647         PlayLevelSoundAction(x, y, ACTION_PUSHING);
11648       else if (Back[x][y] != 0)
11649         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11650                                     ACTION_EMPTYING);
11651       else
11652         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11653                                     ACTION_FILLING);
11654
11655       if (local_player->sokobanfields_still_needed == 0 &&
11656           game.emulation == EMU_SOKOBAN)
11657       {
11658         PlayerWins(player);
11659
11660         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11661       }
11662     }
11663     else
11664       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11665
11666     InitMovingField(x, y, move_direction);
11667     GfxAction[x][y] = ACTION_PUSHING;
11668
11669     if (mode == DF_SNAP)
11670       ContinueMoving(x, y);
11671     else
11672       MovPos[x][y] = (dx != 0 ? dx : dy);
11673
11674     Pushed[x][y] = TRUE;
11675     Pushed[nextx][nexty] = TRUE;
11676
11677     if (game.engine_version < VERSION_IDENT(2,2,0,7))
11678       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11679     else
11680       player->push_delay_value = -1;    /* get new value later */
11681
11682     /* check for element change _after_ element has been pushed */
11683     if (game.use_change_when_pushing_bug)
11684     {
11685       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11686                                  player->index_bit, dig_side);
11687       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11688                                           player->index_bit, dig_side);
11689     }
11690   }
11691   else if (IS_SWITCHABLE(element))
11692   {
11693     if (PLAYER_SWITCHING(player, x, y))
11694     {
11695       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11696                                           player->index_bit, dig_side);
11697
11698       return MP_ACTION;
11699     }
11700
11701     player->is_switching = TRUE;
11702     player->switch_x = x;
11703     player->switch_y = y;
11704
11705     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11706
11707     if (element == EL_ROBOT_WHEEL)
11708     {
11709       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11710       ZX = x;
11711       ZY = y;
11712
11713       DrawLevelField(x, y);
11714     }
11715     else if (element == EL_SP_TERMINAL)
11716     {
11717       int xx, yy;
11718
11719       SCAN_PLAYFIELD(xx, yy)
11720       {
11721         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11722           Bang(xx, yy);
11723         else if (Feld[xx][yy] == EL_SP_TERMINAL)
11724           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11725       }
11726     }
11727     else if (IS_BELT_SWITCH(element))
11728     {
11729       ToggleBeltSwitch(x, y);
11730     }
11731     else if (element == EL_SWITCHGATE_SWITCH_UP ||
11732              element == EL_SWITCHGATE_SWITCH_DOWN)
11733     {
11734       ToggleSwitchgateSwitch(x, y);
11735     }
11736     else if (element == EL_LIGHT_SWITCH ||
11737              element == EL_LIGHT_SWITCH_ACTIVE)
11738     {
11739       ToggleLightSwitch(x, y);
11740     }
11741     else if (element == EL_TIMEGATE_SWITCH)
11742     {
11743       ActivateTimegateSwitch(x, y);
11744     }
11745     else if (element == EL_BALLOON_SWITCH_LEFT  ||
11746              element == EL_BALLOON_SWITCH_RIGHT ||
11747              element == EL_BALLOON_SWITCH_UP    ||
11748              element == EL_BALLOON_SWITCH_DOWN  ||
11749              element == EL_BALLOON_SWITCH_NONE  ||
11750              element == EL_BALLOON_SWITCH_ANY)
11751     {
11752       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
11753                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11754                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
11755                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
11756                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
11757                              move_direction);
11758     }
11759     else if (element == EL_LAMP)
11760     {
11761       Feld[x][y] = EL_LAMP_ACTIVE;
11762       local_player->lights_still_needed--;
11763
11764       ResetGfxAnimation(x, y);
11765       DrawLevelField(x, y);
11766     }
11767     else if (element == EL_TIME_ORB_FULL)
11768     {
11769       Feld[x][y] = EL_TIME_ORB_EMPTY;
11770
11771       if (level.time > 0 || level.use_time_orb_bug)
11772       {
11773         TimeLeft += level.time_orb_time;
11774         DrawGameValue_Time(TimeLeft);
11775       }
11776
11777       ResetGfxAnimation(x, y);
11778       DrawLevelField(x, y);
11779     }
11780     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11781              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11782     {
11783       int xx, yy;
11784
11785       game.ball_state = !game.ball_state;
11786
11787       SCAN_PLAYFIELD(xx, yy)
11788       {
11789         int e = Feld[xx][yy];
11790
11791         if (game.ball_state)
11792         {
11793           if (e == EL_EMC_MAGIC_BALL)
11794             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11795           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11796             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11797         }
11798         else
11799         {
11800           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11801             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11802           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11803             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11804         }
11805       }
11806     }
11807
11808     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11809                                         player->index_bit, dig_side);
11810
11811     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11812                                         player->index_bit, dig_side);
11813
11814     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11815                                         player->index_bit, dig_side);
11816
11817     return MP_ACTION;
11818   }
11819   else
11820   {
11821     if (!PLAYER_SWITCHING(player, x, y))
11822     {
11823       player->is_switching = TRUE;
11824       player->switch_x = x;
11825       player->switch_y = y;
11826
11827       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11828                                  player->index_bit, dig_side);
11829       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11830                                           player->index_bit, dig_side);
11831
11832       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11833                                  player->index_bit, dig_side);
11834       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11835                                           player->index_bit, dig_side);
11836     }
11837
11838     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11839                                player->index_bit, dig_side);
11840     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11841                                         player->index_bit, dig_side);
11842
11843     return MP_NO_ACTION;
11844   }
11845
11846   player->push_delay = -1;
11847
11848   if (is_player)                /* function can also be called by EL_PENGUIN */
11849   {
11850     if (Feld[x][y] != element)          /* really digged/collected something */
11851     {
11852       player->is_collecting = !player->is_digging;
11853       player->is_active = TRUE;
11854     }
11855   }
11856
11857   return MP_MOVING;
11858 }
11859
11860 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11861 {
11862   int jx = player->jx, jy = player->jy;
11863   int x = jx + dx, y = jy + dy;
11864   int snap_direction = (dx == -1 ? MV_LEFT  :
11865                         dx == +1 ? MV_RIGHT :
11866                         dy == -1 ? MV_UP    :
11867                         dy == +1 ? MV_DOWN  : MV_NONE);
11868   boolean can_continue_snapping = (level.continuous_snapping &&
11869                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11870
11871   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11872     return FALSE;
11873
11874   if (!player->active || !IN_LEV_FIELD(x, y))
11875     return FALSE;
11876
11877   if (dx && dy)
11878     return FALSE;
11879
11880   if (!dx && !dy)
11881   {
11882     if (player->MovPos == 0)
11883       player->is_pushing = FALSE;
11884
11885     player->is_snapping = FALSE;
11886
11887     if (player->MovPos == 0)
11888     {
11889       player->is_moving = FALSE;
11890       player->is_digging = FALSE;
11891       player->is_collecting = FALSE;
11892     }
11893
11894     return FALSE;
11895   }
11896
11897 #if USE_NEW_CONTINUOUS_SNAPPING
11898   /* prevent snapping with already pressed snap key when not allowed */
11899   if (player->is_snapping && !can_continue_snapping)
11900     return FALSE;
11901 #else
11902   if (player->is_snapping)
11903     return FALSE;
11904 #endif
11905
11906   player->MovDir = snap_direction;
11907
11908   if (player->MovPos == 0)
11909   {
11910     player->is_moving = FALSE;
11911     player->is_digging = FALSE;
11912     player->is_collecting = FALSE;
11913   }
11914
11915   player->is_dropping = FALSE;
11916   player->is_dropping_pressed = FALSE;
11917   player->drop_pressed_delay = 0;
11918
11919   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11920     return FALSE;
11921
11922   player->is_snapping = TRUE;
11923   player->is_active = TRUE;
11924
11925   if (player->MovPos == 0)
11926   {
11927     player->is_moving = FALSE;
11928     player->is_digging = FALSE;
11929     player->is_collecting = FALSE;
11930   }
11931
11932   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
11933     DrawLevelField(player->last_jx, player->last_jy);
11934
11935   DrawLevelField(x, y);
11936
11937   return TRUE;
11938 }
11939
11940 boolean DropElement(struct PlayerInfo *player)
11941 {
11942   int old_element, new_element;
11943   int dropx = player->jx, dropy = player->jy;
11944   int drop_direction = player->MovDir;
11945   int drop_side = drop_direction;
11946   int drop_element = (player->inventory_size > 0 ?
11947                       player->inventory_element[player->inventory_size - 1] :
11948                       player->inventory_infinite_element != EL_UNDEFINED ?
11949                       player->inventory_infinite_element :
11950                       player->dynabombs_left > 0 ?
11951                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11952                       EL_UNDEFINED);
11953
11954   player->is_dropping_pressed = TRUE;
11955
11956   /* do not drop an element on top of another element; when holding drop key
11957      pressed without moving, dropped element must move away before the next
11958      element can be dropped (this is especially important if the next element
11959      is dynamite, which can be placed on background for historical reasons) */
11960   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11961     return MP_ACTION;
11962
11963   if (IS_THROWABLE(drop_element))
11964   {
11965     dropx += GET_DX_FROM_DIR(drop_direction);
11966     dropy += GET_DY_FROM_DIR(drop_direction);
11967
11968     if (!IN_LEV_FIELD(dropx, dropy))
11969       return FALSE;
11970   }
11971
11972   old_element = Feld[dropx][dropy];     /* old element at dropping position */
11973   new_element = drop_element;           /* default: no change when dropping */
11974
11975   /* check if player is active, not moving and ready to drop */
11976   if (!player->active || player->MovPos || player->drop_delay > 0)
11977     return FALSE;
11978
11979   /* check if player has anything that can be dropped */
11980   if (new_element == EL_UNDEFINED)
11981     return FALSE;
11982
11983   /* check if drop key was pressed long enough for EM style dynamite */
11984   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11985     return FALSE;
11986
11987   /* check if anything can be dropped at the current position */
11988   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11989     return FALSE;
11990
11991   /* collected custom elements can only be dropped on empty fields */
11992   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11993     return FALSE;
11994
11995   if (old_element != EL_EMPTY)
11996     Back[dropx][dropy] = old_element;   /* store old element on this field */
11997
11998   ResetGfxAnimation(dropx, dropy);
11999   ResetRandomAnimationValue(dropx, dropy);
12000
12001   if (player->inventory_size > 0 ||
12002       player->inventory_infinite_element != EL_UNDEFINED)
12003   {
12004     if (player->inventory_size > 0)
12005     {
12006       player->inventory_size--;
12007
12008       DrawGameDoorValues();
12009
12010       if (new_element == EL_DYNAMITE)
12011         new_element = EL_DYNAMITE_ACTIVE;
12012       else if (new_element == EL_EM_DYNAMITE)
12013         new_element = EL_EM_DYNAMITE_ACTIVE;
12014       else if (new_element == EL_SP_DISK_RED)
12015         new_element = EL_SP_DISK_RED_ACTIVE;
12016     }
12017
12018     Feld[dropx][dropy] = new_element;
12019
12020     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12021       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12022                           el2img(Feld[dropx][dropy]), 0);
12023
12024     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12025
12026     /* needed if previous element just changed to "empty" in the last frame */
12027     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12028
12029     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12030                                player->index_bit, drop_side);
12031     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12032                                         CE_PLAYER_DROPS_X,
12033                                         player->index_bit, drop_side);
12034
12035     TestIfElementTouchesCustomElement(dropx, dropy);
12036   }
12037   else          /* player is dropping a dyna bomb */
12038   {
12039     player->dynabombs_left--;
12040
12041     Feld[dropx][dropy] = new_element;
12042
12043     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12044       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12045                           el2img(Feld[dropx][dropy]), 0);
12046
12047     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12048   }
12049
12050   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12051     InitField_WithBug1(dropx, dropy, FALSE);
12052
12053   new_element = Feld[dropx][dropy];     /* element might have changed */
12054
12055   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12056       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12057   {
12058     int move_direction, nextx, nexty;
12059
12060     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12061       MovDir[dropx][dropy] = drop_direction;
12062
12063     move_direction = MovDir[dropx][dropy];
12064     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12065     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12066
12067     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12068     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12069   }
12070
12071   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12072   player->is_dropping = TRUE;
12073
12074   player->drop_pressed_delay = 0;
12075   player->is_dropping_pressed = FALSE;
12076
12077   player->drop_x = dropx;
12078   player->drop_y = dropy;
12079
12080   return TRUE;
12081 }
12082
12083 /* ------------------------------------------------------------------------- */
12084 /* game sound playing functions                                              */
12085 /* ------------------------------------------------------------------------- */
12086
12087 static int *loop_sound_frame = NULL;
12088 static int *loop_sound_volume = NULL;
12089
12090 void InitPlayLevelSound()
12091 {
12092   int num_sounds = getSoundListSize();
12093
12094   checked_free(loop_sound_frame);
12095   checked_free(loop_sound_volume);
12096
12097   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12098   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12099 }
12100
12101 static void PlayLevelSound(int x, int y, int nr)
12102 {
12103   int sx = SCREENX(x), sy = SCREENY(y);
12104   int volume, stereo_position;
12105   int max_distance = 8;
12106   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12107
12108   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12109       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12110     return;
12111
12112   if (!IN_LEV_FIELD(x, y) ||
12113       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12114       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12115     return;
12116
12117   volume = SOUND_MAX_VOLUME;
12118
12119   if (!IN_SCR_FIELD(sx, sy))
12120   {
12121     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12122     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12123
12124     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12125   }
12126
12127   stereo_position = (SOUND_MAX_LEFT +
12128                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12129                      (SCR_FIELDX + 2 * max_distance));
12130
12131   if (IS_LOOP_SOUND(nr))
12132   {
12133     /* This assures that quieter loop sounds do not overwrite louder ones,
12134        while restarting sound volume comparison with each new game frame. */
12135
12136     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12137       return;
12138
12139     loop_sound_volume[nr] = volume;
12140     loop_sound_frame[nr] = FrameCounter;
12141   }
12142
12143   PlaySoundExt(nr, volume, stereo_position, type);
12144 }
12145
12146 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12147 {
12148   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12149                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12150                  y < LEVELY(BY1) ? LEVELY(BY1) :
12151                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12152                  sound_action);
12153 }
12154
12155 static void PlayLevelSoundAction(int x, int y, int action)
12156 {
12157   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12158 }
12159
12160 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12161 {
12162   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12163
12164   if (sound_effect != SND_UNDEFINED)
12165     PlayLevelSound(x, y, sound_effect);
12166 }
12167
12168 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12169                                               int action)
12170 {
12171   int sound_effect = element_info[SND_ELEMENT(element)].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 PlayLevelSoundActionIfLoop(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     PlayLevelSound(x, y, sound_effect);
12183 }
12184
12185 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12186 {
12187   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12188
12189   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12190     StopSound(sound_effect);
12191 }
12192
12193 static void PlayLevelMusic()
12194 {
12195   if (levelset.music[level_nr] != MUS_UNDEFINED)
12196     PlayMusic(levelset.music[level_nr]);        /* from config file */
12197   else
12198     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12199 }
12200
12201 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12202 {
12203   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12204   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12205   int x = xx - 1 - offset;
12206   int y = yy - 1 - offset;
12207
12208   switch (sample)
12209   {
12210     case SAMPLE_blank:
12211       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12212       break;
12213
12214     case SAMPLE_roll:
12215       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12216       break;
12217
12218     case SAMPLE_stone:
12219       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12220       break;
12221
12222     case SAMPLE_nut:
12223       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12224       break;
12225
12226     case SAMPLE_crack:
12227       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12228       break;
12229
12230     case SAMPLE_bug:
12231       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12232       break;
12233
12234     case SAMPLE_tank:
12235       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12236       break;
12237
12238     case SAMPLE_android_clone:
12239       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12240       break;
12241
12242     case SAMPLE_android_move:
12243       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12244       break;
12245
12246     case SAMPLE_spring:
12247       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12248       break;
12249
12250     case SAMPLE_slurp:
12251       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12252       break;
12253
12254     case SAMPLE_eater:
12255       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12256       break;
12257
12258     case SAMPLE_eater_eat:
12259       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12260       break;
12261
12262     case SAMPLE_alien:
12263       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12264       break;
12265
12266     case SAMPLE_collect:
12267       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12268       break;
12269
12270     case SAMPLE_diamond:
12271       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12272       break;
12273
12274     case SAMPLE_squash:
12275       /* !!! CHECK THIS !!! */
12276 #if 1
12277       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12278 #else
12279       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12280 #endif
12281       break;
12282
12283     case SAMPLE_wonderfall:
12284       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12285       break;
12286
12287     case SAMPLE_drip:
12288       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12289       break;
12290
12291     case SAMPLE_push:
12292       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12293       break;
12294
12295     case SAMPLE_dirt:
12296       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12297       break;
12298
12299     case SAMPLE_acid:
12300       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12301       break;
12302
12303     case SAMPLE_ball:
12304       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12305       break;
12306
12307     case SAMPLE_grow:
12308       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12309       break;
12310
12311     case SAMPLE_wonder:
12312       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12313       break;
12314
12315     case SAMPLE_door:
12316       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12317       break;
12318
12319     case SAMPLE_exit_open:
12320       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12321       break;
12322
12323     case SAMPLE_exit_leave:
12324       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12325       break;
12326
12327     case SAMPLE_dynamite:
12328       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12329       break;
12330
12331     case SAMPLE_tick:
12332       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12333       break;
12334
12335     case SAMPLE_press:
12336       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12337       break;
12338
12339     case SAMPLE_wheel:
12340       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12341       break;
12342
12343     case SAMPLE_boom:
12344       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12345       break;
12346
12347     case SAMPLE_die:
12348       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12349       break;
12350
12351     case SAMPLE_time:
12352       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12353       break;
12354
12355     default:
12356       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12357       break;
12358   }
12359 }
12360
12361 #if 0
12362 void ChangeTime(int value)
12363 {
12364   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12365
12366   *time += value;
12367
12368   /* EMC game engine uses value from time counter of RND game engine */
12369   level.native_em_level->lev->time = *time;
12370
12371   DrawGameValue_Time(*time);
12372 }
12373
12374 void RaiseScore(int value)
12375 {
12376   /* EMC game engine and RND game engine have separate score counters */
12377   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12378                 &level.native_em_level->lev->score : &local_player->score);
12379
12380   *score += value;
12381
12382   DrawGameValue_Score(*score);
12383 }
12384 #endif
12385
12386 void RaiseScore(int value)
12387 {
12388   local_player->score += value;
12389
12390   DrawGameValue_Score(local_player->score);
12391 }
12392
12393 void RaiseScoreElement(int element)
12394 {
12395   switch (element)
12396   {
12397     case EL_EMERALD:
12398     case EL_BD_DIAMOND:
12399     case EL_EMERALD_YELLOW:
12400     case EL_EMERALD_RED:
12401     case EL_EMERALD_PURPLE:
12402     case EL_SP_INFOTRON:
12403       RaiseScore(level.score[SC_EMERALD]);
12404       break;
12405     case EL_DIAMOND:
12406       RaiseScore(level.score[SC_DIAMOND]);
12407       break;
12408     case EL_CRYSTAL:
12409       RaiseScore(level.score[SC_CRYSTAL]);
12410       break;
12411     case EL_PEARL:
12412       RaiseScore(level.score[SC_PEARL]);
12413       break;
12414     case EL_BUG:
12415     case EL_BD_BUTTERFLY:
12416     case EL_SP_ELECTRON:
12417       RaiseScore(level.score[SC_BUG]);
12418       break;
12419     case EL_SPACESHIP:
12420     case EL_BD_FIREFLY:
12421     case EL_SP_SNIKSNAK:
12422       RaiseScore(level.score[SC_SPACESHIP]);
12423       break;
12424     case EL_YAMYAM:
12425     case EL_DARK_YAMYAM:
12426       RaiseScore(level.score[SC_YAMYAM]);
12427       break;
12428     case EL_ROBOT:
12429       RaiseScore(level.score[SC_ROBOT]);
12430       break;
12431     case EL_PACMAN:
12432       RaiseScore(level.score[SC_PACMAN]);
12433       break;
12434     case EL_NUT:
12435       RaiseScore(level.score[SC_NUT]);
12436       break;
12437     case EL_DYNAMITE:
12438     case EL_EM_DYNAMITE:
12439     case EL_SP_DISK_RED:
12440     case EL_DYNABOMB_INCREASE_NUMBER:
12441     case EL_DYNABOMB_INCREASE_SIZE:
12442     case EL_DYNABOMB_INCREASE_POWER:
12443       RaiseScore(level.score[SC_DYNAMITE]);
12444       break;
12445     case EL_SHIELD_NORMAL:
12446     case EL_SHIELD_DEADLY:
12447       RaiseScore(level.score[SC_SHIELD]);
12448       break;
12449     case EL_EXTRA_TIME:
12450       RaiseScore(level.extra_time_score);
12451       break;
12452     case EL_KEY_1:
12453     case EL_KEY_2:
12454     case EL_KEY_3:
12455     case EL_KEY_4:
12456     case EL_EM_KEY_1:
12457     case EL_EM_KEY_2:
12458     case EL_EM_KEY_3:
12459     case EL_EM_KEY_4:
12460     case EL_EMC_KEY_5:
12461     case EL_EMC_KEY_6:
12462     case EL_EMC_KEY_7:
12463     case EL_EMC_KEY_8:
12464       RaiseScore(level.score[SC_KEY]);
12465       break;
12466     default:
12467       RaiseScore(element_info[element].collect_score);
12468       break;
12469   }
12470 }
12471
12472 void RequestQuitGame(boolean ask_if_really_quit)
12473 {
12474   if (AllPlayersGone ||
12475       !ask_if_really_quit ||
12476       level_editor_test_game ||
12477       Request("Do you really want to quit the game ?",
12478               REQ_ASK | REQ_STAY_CLOSED))
12479   {
12480 #if defined(NETWORK_AVALIABLE)
12481     if (options.network)
12482       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12483     else
12484 #endif
12485     {
12486       if (!ask_if_really_quit || level_editor_test_game)
12487       {
12488         game_status = GAME_MODE_MAIN;
12489
12490         DrawMainMenu();
12491       }
12492       else
12493       {
12494         FadeOut(REDRAW_FIELD);
12495
12496         game_status = GAME_MODE_MAIN;
12497
12498         DrawAndFadeInMainMenu(REDRAW_FIELD);
12499       }
12500     }
12501   }
12502   else
12503   {
12504     if (tape.playing && tape.deactivate_display)
12505       TapeDeactivateDisplayOff(TRUE);
12506
12507     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12508
12509     if (tape.playing && tape.deactivate_display)
12510       TapeDeactivateDisplayOn();
12511   }
12512 }
12513
12514
12515 /* ------------------------------------------------------------------------- */
12516 /* random generator functions                                                */
12517 /* ------------------------------------------------------------------------- */
12518
12519 unsigned int InitEngineRandom_RND(long seed)
12520 {
12521   game.num_random_calls = 0;
12522
12523 #if 0
12524   unsigned int rnd_seed = InitEngineRandom(seed);
12525
12526   printf("::: START RND: %d\n", rnd_seed);
12527
12528   return rnd_seed;
12529 #else
12530
12531   return InitEngineRandom(seed);
12532
12533 #endif
12534
12535 }
12536
12537 unsigned int RND(int max)
12538 {
12539   if (max > 0)
12540   {
12541     game.num_random_calls++;
12542
12543     return GetEngineRandom(max);
12544   }
12545
12546   return 0;
12547 }
12548
12549
12550 /* ------------------------------------------------------------------------- */
12551 /* game engine snapshot handling functions                                   */
12552 /* ------------------------------------------------------------------------- */
12553
12554 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
12555
12556 struct EngineSnapshotInfo
12557 {
12558   /* runtime values for custom element collect score */
12559   int collect_score[NUM_CUSTOM_ELEMENTS];
12560
12561   /* runtime values for group element choice position */
12562   int choice_pos[NUM_GROUP_ELEMENTS];
12563
12564   /* runtime values for belt position animations */
12565   int belt_graphic[4 * NUM_BELT_PARTS];
12566   int belt_anim_mode[4 * NUM_BELT_PARTS];
12567 };
12568
12569 struct EngineSnapshotNodeInfo
12570 {
12571   void *buffer_orig;
12572   void *buffer_copy;
12573   int size;
12574 };
12575
12576 static struct EngineSnapshotInfo engine_snapshot_rnd;
12577 static ListNode *engine_snapshot_list = NULL;
12578 static char *snapshot_level_identifier = NULL;
12579 static int snapshot_level_nr = -1;
12580
12581 void FreeEngineSnapshot()
12582 {
12583   while (engine_snapshot_list != NULL)
12584     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12585                        checked_free);
12586
12587   setString(&snapshot_level_identifier, NULL);
12588   snapshot_level_nr = -1;
12589 }
12590
12591 static void SaveEngineSnapshotValues_RND()
12592 {
12593   static int belt_base_active_element[4] =
12594   {
12595     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12596     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12597     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12598     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12599   };
12600   int i, j;
12601
12602   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12603   {
12604     int element = EL_CUSTOM_START + i;
12605
12606     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12607   }
12608
12609   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12610   {
12611     int element = EL_GROUP_START + i;
12612
12613     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12614   }
12615
12616   for (i = 0; i < 4; i++)
12617   {
12618     for (j = 0; j < NUM_BELT_PARTS; j++)
12619     {
12620       int element = belt_base_active_element[i] + j;
12621       int graphic = el2img(element);
12622       int anim_mode = graphic_info[graphic].anim_mode;
12623
12624       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12625       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12626     }
12627   }
12628 }
12629
12630 static void LoadEngineSnapshotValues_RND()
12631 {
12632   unsigned long num_random_calls = game.num_random_calls;
12633   int i, j;
12634
12635   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12636   {
12637     int element = EL_CUSTOM_START + i;
12638
12639     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12640   }
12641
12642   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12643   {
12644     int element = EL_GROUP_START + i;
12645
12646     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12647   }
12648
12649   for (i = 0; i < 4; i++)
12650   {
12651     for (j = 0; j < NUM_BELT_PARTS; j++)
12652     {
12653       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12654       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12655
12656       graphic_info[graphic].anim_mode = anim_mode;
12657     }
12658   }
12659
12660   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12661   {
12662     InitRND(tape.random_seed);
12663     for (i = 0; i < num_random_calls; i++)
12664       RND(1);
12665   }
12666
12667   if (game.num_random_calls != num_random_calls)
12668   {
12669     Error(ERR_RETURN, "number of random calls out of sync");
12670     Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12671     Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12672     Error(ERR_EXIT, "this should not happen -- please debug");
12673   }
12674 }
12675
12676 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12677 {
12678   struct EngineSnapshotNodeInfo *bi =
12679     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12680
12681   bi->buffer_orig = buffer;
12682   bi->buffer_copy = checked_malloc(size);
12683   bi->size = size;
12684
12685   memcpy(bi->buffer_copy, buffer, size);
12686
12687   addNodeToList(&engine_snapshot_list, NULL, bi);
12688 }
12689
12690 void SaveEngineSnapshot()
12691 {
12692   FreeEngineSnapshot();         /* free previous snapshot, if needed */
12693
12694   if (level_editor_test_game)   /* do not save snapshots from editor */
12695     return;
12696
12697   /* copy some special values to a structure better suited for the snapshot */
12698
12699   SaveEngineSnapshotValues_RND();
12700   SaveEngineSnapshotValues_EM();
12701
12702   /* save values stored in special snapshot structure */
12703
12704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12706
12707   /* save further RND engine values */
12708
12709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12712
12713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12715   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12717
12718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12719   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12720   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12721   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12722   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12723
12724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12725   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12726   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12727
12728   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12729
12730   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12731
12732   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12733   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12734
12735   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12736   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12737   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12738   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12739   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12740   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12741   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12742   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12743   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12744   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12745   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12746   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12747   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12748   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12749   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12750   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12751   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12752
12753   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12754   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12755
12756   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12757   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12758   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12759
12760   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12761   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12762
12763   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12764   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12765   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12766   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12767   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12768
12769   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12770   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12771
12772   /* save level identification information */
12773
12774   setString(&snapshot_level_identifier, leveldir_current->identifier);
12775   snapshot_level_nr = level_nr;
12776
12777 #if 0
12778   ListNode *node = engine_snapshot_list;
12779   int num_bytes = 0;
12780
12781   while (node != NULL)
12782   {
12783     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12784
12785     node = node->next;
12786   }
12787
12788   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12789 #endif
12790 }
12791
12792 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12793 {
12794   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12795 }
12796
12797 void LoadEngineSnapshot()
12798 {
12799   ListNode *node = engine_snapshot_list;
12800
12801   if (engine_snapshot_list == NULL)
12802     return;
12803
12804   while (node != NULL)
12805   {
12806     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12807
12808     node = node->next;
12809   }
12810
12811   /* restore special values from snapshot structure */
12812
12813   LoadEngineSnapshotValues_RND();
12814   LoadEngineSnapshotValues_EM();
12815 }
12816
12817 boolean CheckEngineSnapshot()
12818 {
12819   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12820           snapshot_level_nr == level_nr);
12821 }
12822
12823
12824 /* ---------- new game button stuff ---------------------------------------- */
12825
12826 /* graphic position values for game buttons */
12827 #define GAME_BUTTON_XSIZE       30
12828 #define GAME_BUTTON_YSIZE       30
12829 #define GAME_BUTTON_XPOS        5
12830 #define GAME_BUTTON_YPOS        215
12831 #define SOUND_BUTTON_XPOS       5
12832 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12833
12834 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12835 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12836 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12837 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12838 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12839 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12840
12841 static struct
12842 {
12843   int x, y;
12844   int gadget_id;
12845   char *infotext;
12846 } gamebutton_info[NUM_GAME_BUTTONS] =
12847 {
12848   {
12849     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
12850     GAME_CTRL_ID_STOP,
12851     "stop game"
12852   },
12853   {
12854     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
12855     GAME_CTRL_ID_PAUSE,
12856     "pause game"
12857   },
12858   {
12859     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
12860     GAME_CTRL_ID_PLAY,
12861     "play game"
12862   },
12863   {
12864     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
12865     SOUND_CTRL_ID_MUSIC,
12866     "background music on/off"
12867   },
12868   {
12869     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
12870     SOUND_CTRL_ID_LOOPS,
12871     "sound loops on/off"
12872   },
12873   {
12874     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
12875     SOUND_CTRL_ID_SIMPLE,
12876     "normal sounds on/off"
12877   }
12878 };
12879
12880 void CreateGameButtons()
12881 {
12882   int i;
12883
12884   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12885   {
12886     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12887     struct GadgetInfo *gi;
12888     int button_type;
12889     boolean checked;
12890     unsigned long event_mask;
12891     int gd_xoffset, gd_yoffset;
12892     int gd_x1, gd_x2, gd_y1, gd_y2;
12893     int id = i;
12894
12895     gd_xoffset = gamebutton_info[i].x;
12896     gd_yoffset = gamebutton_info[i].y;
12897     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12898     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12899
12900     if (id == GAME_CTRL_ID_STOP ||
12901         id == GAME_CTRL_ID_PAUSE ||
12902         id == GAME_CTRL_ID_PLAY)
12903     {
12904       button_type = GD_TYPE_NORMAL_BUTTON;
12905       checked = FALSE;
12906       event_mask = GD_EVENT_RELEASED;
12907       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12908       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12909     }
12910     else
12911     {
12912       button_type = GD_TYPE_CHECK_BUTTON;
12913       checked =
12914         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12915          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12916          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12917       event_mask = GD_EVENT_PRESSED;
12918       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
12919       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12920     }
12921
12922     gi = CreateGadget(GDI_CUSTOM_ID, id,
12923                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
12924                       GDI_X, DX + gd_xoffset,
12925                       GDI_Y, DY + gd_yoffset,
12926                       GDI_WIDTH, GAME_BUTTON_XSIZE,
12927                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
12928                       GDI_TYPE, button_type,
12929                       GDI_STATE, GD_BUTTON_UNPRESSED,
12930                       GDI_CHECKED, checked,
12931                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12932                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12933                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12934                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12935                       GDI_EVENT_MASK, event_mask,
12936                       GDI_CALLBACK_ACTION, HandleGameButtons,
12937                       GDI_END);
12938
12939     if (gi == NULL)
12940       Error(ERR_EXIT, "cannot create gadget");
12941
12942     game_gadget[id] = gi;
12943   }
12944 }
12945
12946 void FreeGameButtons()
12947 {
12948   int i;
12949
12950   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12951     FreeGadget(game_gadget[i]);
12952 }
12953
12954 static void MapGameButtons()
12955 {
12956   int i;
12957
12958   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12959     MapGadget(game_gadget[i]);
12960 }
12961
12962 void UnmapGameButtons()
12963 {
12964   int i;
12965
12966   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12967     UnmapGadget(game_gadget[i]);
12968 }
12969
12970 static void HandleGameButtons(struct GadgetInfo *gi)
12971 {
12972   int id = gi->custom_id;
12973
12974   if (game_status != GAME_MODE_PLAYING)
12975     return;
12976
12977   switch (id)
12978   {
12979     case GAME_CTRL_ID_STOP:
12980       if (tape.playing)
12981         TapeStop();
12982       else
12983         RequestQuitGame(TRUE);
12984       break;
12985
12986     case GAME_CTRL_ID_PAUSE:
12987       if (options.network)
12988       {
12989 #if defined(NETWORK_AVALIABLE)
12990         if (tape.pausing)
12991           SendToServer_ContinuePlaying();
12992         else
12993           SendToServer_PausePlaying();
12994 #endif
12995       }
12996       else
12997         TapeTogglePause(TAPE_TOGGLE_MANUAL);
12998       break;
12999
13000     case GAME_CTRL_ID_PLAY:
13001       if (tape.pausing)
13002       {
13003 #if defined(NETWORK_AVALIABLE)
13004         if (options.network)
13005           SendToServer_ContinuePlaying();
13006         else
13007 #endif
13008         {
13009           tape.pausing = FALSE;
13010           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13011         }
13012       }
13013       break;
13014
13015     case SOUND_CTRL_ID_MUSIC:
13016       if (setup.sound_music)
13017       { 
13018         setup.sound_music = FALSE;
13019         FadeMusic();
13020       }
13021       else if (audio.music_available)
13022       { 
13023         setup.sound = setup.sound_music = TRUE;
13024
13025         SetAudioMode(setup.sound);
13026
13027         PlayLevelMusic();
13028       }
13029       break;
13030
13031     case SOUND_CTRL_ID_LOOPS:
13032       if (setup.sound_loops)
13033         setup.sound_loops = FALSE;
13034       else if (audio.loops_available)
13035       {
13036         setup.sound = setup.sound_loops = TRUE;
13037         SetAudioMode(setup.sound);
13038       }
13039       break;
13040
13041     case SOUND_CTRL_ID_SIMPLE:
13042       if (setup.sound_simple)
13043         setup.sound_simple = FALSE;
13044       else if (audio.sound_available)
13045       {
13046         setup.sound = setup.sound_simple = TRUE;
13047         SetAudioMode(setup.sound);
13048       }
13049       break;
13050
13051     default:
13052       break;
13053   }
13054 }